Bài viết mới nhất



Chào các bạn!

Hôm nay mình xin trở lại với chuỗi bài hướng dẫn về Lập trình hướng đối tượng OOP.  Trong C#, một class con có thể chứa các hàm cùng tên với các hàm trong class mà nó kế thừa. Điều đó là tốt đối với lập trình viên bởi họ có thể tận dụng điều đó để cài đặt mã nguồn của mình theo OOP, tuy nhiên nó đôi khi sẽ gây nhầm lẫn.
Bạn có thể định rõ ra cách mà hàm đó tương tác, hoạt động như thế nào bằng cách sử dụng từ khóa new hoặc override



Sự khác nhau được mô tả trong bài dưới đây:

Trong một chương trình Console, khai báo hai class: BaseClass DerivedClass kế thừa từ BaseClass.


Trong hàm Main, khai báo các biến bc, dc, bcdc tương tướng với:
  • bc là đối tượng kiểu BaseClass, và mang giá trị kiểu BaseClass
  • dc là đối tượng kiểu DerivedClass, và mang giá trị kiểu DerivedClass
  • bcdc là đối tượng kiểu BaseClass và mang giá trị kiểu DerivedClass: Đây là biến cần chú ý tới nhiều nhất!
Bởi vì bcbcdc điều có kiểu dữ liệu là BaseClass, nó chỉ nó thể truy cập trực tiếp đến Method1() (ngoại trừ khi bạn dùng casting ép kiểu nó về DerivedClass - việc này là có thể được).  Biến dc có thể truy cập đến cả Method1()Method2(). Những mối quan hệ trên được thể hiện như hình bên dưới:


Ta thấy kết quả là những gì có thể đoán trước được. Tiếp tục ta thêm một hàm Method2() vào BaseClass. Bây giờ ta đã có hai hàm trùng tên, một nằm ở class cha và một nằm ở class con.


Bởi vì BaseClass bây giờ đã có Method2(), ta sẽ gọi được hàm Method2() ở các biến mạng kiểu là BaseClass là bc và bcdc:


Khi bạn build project, bạn sẽ thấy một thông báo cảnh cáo. Dòng warning nói rằng Method2() ở DerivedClass "che" đi Method2() ở BaseClass. Bạn cũng được khuyên nên sử dụng từ khóa new ở phần cài đặt/định nghĩa của Method2() nếu bạn muốn tạo ra kết quả tượng tự như vậy. Hoặc bạn có thể đổi tên Method2() để resolve warning đó, nhưng nó không phải lúc nào cũng tốt.

Trước khi thêm từ khóa new, chạy chương trình và xem output tạo ra bởi những hàm đã gọi, bạn sẽ nhận được như sau:


Hãy chú ý đến những dòng được mình highlight, dòng thứ 2 được bc mang kiểu BaseClass nên việc nó chỉ gọi hàm Method2() của lớp cha cũng không có gì là lạ. Ở hai dòng cuối, hành vi mặc định của nó được giữ nguyên, nên nó ưu tiên gọi hàm từ lớp cha dù ở lớp con cũng có một hàm Method2() cùng tên.
Từ khóa new "bảo tồn" mối quan hệ mà tạo ra kết quả ban đầu, nhưng nó vô hiệu hóa đi các cảnh bảo từ IDE. Các biến có kiểu là BaseClass vẫn giữ nguyên hành vi của nó và tiếp tục truy cập các hàm/thuộc tính của BaseClass, và các biến có kiểu là DerivedClass tiếp tục tuy cập vào các hàm/thuộc tính của DerivedClass đầu tiên, sau đó mới suy xét đến những thành phần mà nó kế thừa từ BaseClass.

Ví dụ: 
Ta có thể thêm từ khóa new trước hoặc sau public

Chạy lại chương trình để chắc rằng output không thay đổi. Cũng để thấy rằng những warnings từ IDE không còn xuất hiện nữa.
Bằng cách sử dụng từ khóa new, bạn thầm khẳng định rằng bạn biết hàm/thuộc tính đó "che" đi hàm/thuộc tính mà nó kế thừa từ chính lớp cha của nó. Để biết thêm thông tin về ẩn tên (name hiding) trong kế thừa, xem new Modifier

Mặc khác, để tạo ra 1 hành vi tương phản lại ở trên, ta sử dụng từ khóa override. Thêm hàm sau kèm từ khóa override vào DerivedClass và từ khóa virtual vào tên hàm ở BaseClass
Từ khóa override có thể đặt trước hoặc sau public


Chạy lại chương trình, ta chú ý sự khác nhau giữa 2 dòng cuối của output với nhau, và so với output của lần test trước. Qua đó thấy rõ sự khác nhau của override new.


Ta thấy khi gọi hàm Method1() trùng tên giữa 2 lớp, mang từ khóa override từ biến bcdc lần này nó không gọi hàm của lớp cha nữa, mà chuyển sang gọi hàm của lớp con. Tuy nhiên cũng là gọi hàm trùng tên Method2(), không có từ khóa override thì nó sẽ trở lại hành vi mặc định là gọi hàm từ lớp cha.

Việc sử dụng từ khóa override làm cho biến bcdc có thể truy cập đến Method1() của lớp con DerivedClass. Thông thường, đây mới là hành vi được các lập trình viên chúng ta mong muốn khi sử dụng OOP và kế thừa. Bạn luôn muốn những object được khởi tạo như một thể hiện của lớp con sử dụng các hàm từ chính lớp con đó chứ không phải những hàm cùng tên, được cài đặt từ trước ở lớp cha. 

Tuy nhiên hành động mặc định của C# lại ưu tiên lớp cha hơn :D. Vậy nên bạn có thể đạt được mong muốn của mình bằng cách phải dùng từ khóa override để extend và ghi đè những hàm của lớp cha bằng những hàm mới cùng tện trong lớp con.

Full source code:


Đến đây các bạn đã hiểu về sự khác nhau giữa new và override trong C# chưa nào? 
Bạn có thể tham khảo thêm ví dụ sau đây để biết rõ hơn về chúng. Mọi thắc mắc xin để lại bình luận bên dưới nhé.

Chào các bạn, chúc các bạn học tốt!


Chào các bạn!

Hôm nay nhân dịp đang làm task về "immersive fullscreen mode" trong công ty, mình xin viết một bài chia sẻ về một thuộc tính khá liên quan. Đó là fitsSystemWindow.




Tại sao chúng ta lại sử dụng fitsSystemWindow?

System windows (system bars) là một phần của màn hình, nơi hệ thống Android vẽ ra những View không-thể-tương-tác (trong trường hợp của StatusBar) hay là những View có-thể-tương-tác-được (như là Toolbar, NavigationBar).

Trong Android Developers Docs ghi rằng:
 - android:fitsSystemWindow là một thuộc tính nội bộ kiểu boolean để điều chỉnh view layout dựa theo System windows (như là StatusBar,..). Nếu nó được set là true, Android sẽ tự động điều chỉnh lại paddding của View đó sao cho nó chừa đủ khoảng trống dành cho các System windows. Tuy nhiên nó chỉ có tác dụng đối với các view không phải là embeded-Activity. (Là các Activity đươc host ngay trong một Activity khác, ví dụ như TabHost/TabActivity. Trong thực tế các embeded-Activity tồn tại ở LocalActivityManager của Activity mẹ, giống như FragmentManager cho phép bạn hiển thị một Activity này trong một Activity khác.). Bởi vì chúng bị giới hạn bởi diện tích được cấp bởi Activity mẹ).
Toolbar bị vẽ dưới StatusBar

Đa phần thời gian, ứng dụng của bạn sẽ không cần phải được vẽ ở dưới StatusBar hay NavigationBar, tuy nhiên nếu bạn muốn: bạn cần phải chắc chắn rằng những thành phần giao diện mà có-thể-tương-tác-được (như Button, EditText,..) không bị "ẩn" bên dưới các System windows. Đó cũng là hành vi (behaviour) mặc định mà thuộc tính android:fitsSystemWindows=“true” cung cấp cho bạn: và như mình đã trích dẫn ở trên, nó set lại padding của View để đảm bảo nội dung không bị che bởi các System windows.

Một số thứ cần lưu ý:
  • fitsSystemWindows được áp dụng theo chiều sâu dạng "deep first" - có quan trọng thứ tự: View đầu tiên nhận được insets sẽ thay đổi.
  • Insets (viền) luôn quan hệ và tỉ lệ với toàn cửa sổ trên màn hình: insets có thể được áp dụng ngay cả trước quá trình parse và tạo layout được diễn ra. Vậy nên đừng nghĩ rằng hệ thống Android có chút ít thông tin gì về vị trí của View khi dựa vào thuộc tính fitsSystemWindows để thiết lập padding cho View đó.
  • Và dĩ nhiên, vì nó không có chút thông tin gì nên mọi padding khác mà bạn set cho View đó đều bị ghi đè (overwritten): bạn hãy chú ý rằng mọi thuộc tính như paddingLeft/padding/paddingBottom đều bị ghi đè và không còn hiệu lực khi bạn áp dụng android:fitsSystemWindow = "true".
Và, trong nhiều trường hợp, như là một trình phát video full screen, bạn cần một View full màn hình, không có thuộc tính, và một ViewGroup mang thuộc tính android:fitsSystemWindow = "true" để chứa View full màn hình mà bạn cần thiết lập insets.

Hoặc có thể bạn muốn RecyclerView của mình scroll xuống và vẽ ẩn bên dưới thanh NavigationBar, đồng thời biến màu nền của NavigationBar thành trong suốt - bằng cách sử dụng thuộc tính android:fitsSystemWindow = "true" kết hợp với android:clipToPadding = "false". Các nội dung bên trong RecyclerView của bạn sẽ nằm bên dưới NavigationBar, tuy nhiên nếu bạn scroll xuống đến cuối cùng thì thấy item cuối của RecyclerView vẫn được thiết lập padding bottom và nằm phía trên NavigationBar (còn tốt hơn là nó bị che khuất và ẩn bên dưới nhiều nhỉ :D)


Tùy biến fitsSystemWindows

Ở phiên bản Android KitKat hoặc thấp hơn, một custom View của bạn có thể override lại hàm fitsSystemWindows(Rect insets) và thay vào đó bất kì phương thức nào bạn muốn, chỉ việc return true; nếu bạn muốn "nuốt" (consume) những viền insets hoặc false nếu bạn muốn cho những View khác một cơ hội "chạm tay vào điều ước" :D

Tuy nhiên, kể từ phiên bản Android Lollipop, Google cung cấp thêm một số APIs mới để cho việc tùy biến tính năng này dễ dàng hơn và phù hợp hơn với các hành vi, tính năng hiện có khác của View. Thay vì như ở trên, bạn sẽ override onApplyWindowInsets() thứ sẽ cho phép View có thể tùy biến giữ lấy bao nhiêu phần viền insets tùy ý, ngoài ra bạn còn có thể gọi hàm dispatchWindowInsets() lên các View con khi cần thiết.

Và tốt hơn cả là bạn sẽ không phải đau đầu hay hack não khi phải subclass những Views của mình để custom các hành vi cho Views như trên ở các phiên bản Android Lollipop hoặc cao hơn. Bạn có thể dùng ViewCompat.setOnApplyWindowInsetsListener(), thứ có thể cho tham chiếu đến onApplyWindowInsets() của View. ViewCompat còn cung cấp các hàm helper để gọi onApplyWindowInsets()dispatchWindowInsets() mà không cần kiểm tra version.


Các ví dụ về tùy biến fitsSystemWindows

Trong khi các layouts cơ bản (FrameLayout, RelativeLayout, LinearLayout,..) đang sử dụng hành vi mặc định của mình, thì có rất nhiều layouts khác đã tùy biến sẵn những hành vi mà nó thực hiện khi gặp fitsSystemWindows(), nhờ đó có thể đáp ứng được một số trường hợp usecase cụ thể.

Một ví dụ điển hình ở đây là navigation drawer, thứ cần phải phủ ra cả màn hình, và được vẽ dưới một StatusBar có màu nền trong suốt. 


Ở đây DrawerLayout sử dụng thuộc tính fitsSystemWindows() như là một dấu hiệu rằng nó muốn dịch chuyển viền của những View con (như là main content view), nhưng vẫn vẽ StatusBar bình thường với màu nền được định sẵn trong colorPrimaryDark của theme, nhằm tuân theo qui tắc của Material design.

Nếu bạn chú ý một chút có thể thấy rằng trên các thiết bị chạy Android Lollipop trở lên,  DrawerLayout luôn gọi hàm dispatchApplyWindowsInsets() cho từng View con mà nó chứa, qua đó cho phép những View con đó cũng có thể nhận fitsSystemWindows, khác với hành vi mặc định của nó trên các phiển bản trước (là đơn giản "nuốt" những viền insets đó, và những View con của nó không bao giờ có thể chạm tới fitsSystemWindows).

CoordinatorLayout cũng được cải tiến và có thể override cái cách mà nó "xử lý" những viền window insets - cho phép những hành vi được thiết lập ở những View con có thể đánh chặn (intercept) và thay đổi cách thức mà chúng hành động khi gặp window insets - trước khi gọi hàm dispatchApplyWindowInsets() lên mỗi View con. Nó cũng sử dụng thuộc tính fitsSystemWindows như là một biến cờ (flag) để xác định cách thức mà nó sẽ vẽ màu nền của StatusBar.

Tương tự, CollapsingToolbarLayout tìm fitsSystemWindows trong các thuộc tính của nó để quyết định vẽ content scrim - là những gì được vẽ chồng lên khoảng trống của StatusBar và Toolbar khi CollapsingToolbarLayout được scroll dài ra phủ xuống màn hình.

Nếu bạn vẫn hứng xem những trường hợp phổ biến liên quan đến Design Library, hãy thử nhìn qua ứng dụng mẫu cheesesquare sau.


Sử dụng hệ thống Android, đừng chống lại nó!

Một thứ cần phải luôn nhớ trong đầu rằng thuộc tính này không phải được gọi là fitsStatusBar hay fitsNavigationBar. Bất cứ thứ gì cấu thành System Windows, thì kích thước (dimensions) và vị trí của nó có thể thay đổi khi chạy trên các nền tảng, phiên bản khác nhau. Ví dụ rõ ràng nhất, bạn hãy nhìn vào những điểm khác nhau khi chạy ứng dụng trên Android Honeycomb và Ice Cream Sandwich.

Cuối cùng các bạn hãy yên tâm rằng, phần viền insets mà bạn lấy đi từ fitsSystemWindow sẽ đúng với mọi phiên bản platform để chắc chắn rằng những nội dung trong layout của bạn không bị xếp chồng với những thành phần giao diện UI cung cấp bởi hệ thống - hay chắc chắn rằng bạn sẽ tránh đưa ra bất kì giả định nào về tính khả dụng hoặc kích cỡ nếu bạn tùy biến hành vi mặc định của nó.

Chào các bạn! Chúc các bạn học tốt!

#BuildBetterApps



Chào các bạn!

Sau một thời gian vắng bóng do bận thi và có khá nhiều việc trên công ty thì hôm nay mình sẽ trở lại với một bài viết nho nhỏ :D

Khi lập trình Android, đặc biệt là khi xử lý với các XML layout bạn chắc hẳn phải gặp qua thuộc tính "id" trong các thẻ View hoặc Layout. Khi mới học chúng ta có thể hiểu đơn giản là nó sẽ gán hoặc gọi id của các View hoặc Layout đó. Tuy nhiên đôi khi bạn thấy nó có sự xuất hiện của các cách gọi, gán id khác nhau như:
- android:id="@id/myView"
 - android:id="@android:id/myView"
- android:id="@+id/myView"

Vậy chúng khác nhau ra sao, và được dùng trong các trường hợp như thế nào?



Thứ nhất, các bạn nên biết "android:" là tiền tố để chỉ đến các thuộc tính, layout được định sẵn trong android. Không chỉ riêng "@android:id/mà còn nhiều thứ khác như "@android:color/", "@android:drawable/", "@android:layout/"... 
Đây là những tài nguyên PHỔ BIẾNAndroid System đã định sẵn cho nhà lập trình để sử dụng vào các project của họ.

Ví dụ:
Bạn còn nhớ khi tạo một ArrayAdapter cho ListView ta có sử dụng tham số  android.R.layout.simple_list_item_1, hay đơn giản hơn khi dùng "@android:color/transparent" để chỉ màu trong suốt, được rất nhiều người sử dụng, nên android đã tạo ra nó, và tất cả những gì lập trình viên cần là gọi ra và sử dụng. 


Đó là Android Resources, còn khi làm việc với những tài nguyên do chính bạn tạo/defined trong project. Bạn sẽ sử dụng "@id/" và "@+id/"

Căn bản thì bạn có thể không cần suy nghĩ gì cả, hãy sử dụng "@+id/" ở tất cả các chỗ bạn muốn.
Tuy nhiên hiểu sao cho đúng thì là một vấn đề khác. Nếu đơn giản là những gì bạn thích, thì bạn có thể bỏ qua những phần bên dưới!

-----------------------------------------------------------

Khi bạn sử dụng "@+id/myView" thì dấu "+" nói với Android System rằng đây là một tài nguyên mới với tên là "myView" cần được tạo và thêm vào kho tài nguyên (resources) chung. (trong file R.java) NẾU NÓ CHƯA TỒN TẠI. Và sau đó trong Java code bạn sẽ gọi lại các entry đó trong R.java ra bằng cách View view = findViewById(R.id.myView)Quá trình tạo file R.java đó sẽ diễn ra lúc app bạn được compile. 

Mặt khác, khi bạn sử dụng "@id/myView", bạn đang liên kết, refer đến một tài nguyên đã được định nghĩa từ trước bằng "@+id/" và đã tồn tại trong R.java.

File R.java mẫu

Ví dụ:
Trong XML Layout:
- Khi bạn sử dụng thuộc tính "android:id=..." nghĩa là bạn đang muốn xác định id cho một View/Layout mới và cần hướng dẫn cho parser/builder tạo một entry mới trong R.java. Khi đó bạn sẽ phải thêm dấu "+" vào như "android:id=@+id/myView",...
- Khi bạn muốn tham chiếu đến id của một View/Layout khác đã được định nghĩa trước như android:layout_below="@id/myTextView", android:layout_toRightOf="@id/myTextView", ... thì chỉ cần sử dụng "@id/", parser sẽ tự động hiểu và link đến entry đó trong R.java.

Vậy có trường hợp ngoại lệ nào không? 

Xin trả lời là có!

XML Layout của Android có một điểm yếu, nó không như Java code, bạn viết 1 hàm bất kì đâu trong file .java, bạn đều gọi được, không phân biệt thứ tự. Ở đây nó sẽ parse từ trên xuống, nên nếu bạn viết như thế này, thì xuất hiện lỗi lúc compile-time, vì nó không tìm thấy R.id.check.


Rõ ràng đoạn xml trên mình đã dùng "
@+id/check" nghĩa là phải có check trong R.java rồi chứ. Tại sao Android lại bảo không có? :D
...
Bạn đã có câu trả lời cho mình chưa nào? :D

Vậy trong trường hợp này ta sẽ có hai cách giải quyết.

Thứ nhất, đem thẻ Button với id.start xuống dưới Button với id.check.


Như vậy dù thay đổi thứ tự trong xml layout, nhưng thứ tự thực tế khi parse ra vẫn không thay đổi khi sử dụng RelativeLayout. Còn LinearLayout thì sai bét nhé :D
Mặt khác code như thế này làm cho người lập trình viên đọc cảm thấy confused, khó hiểu, dẫn đến dễ gây ra nhầm lẫn, bug không đáng có.

Vậy lỡ gặp LinearLayout hay để dễ đọc hơn thì ta sẽ làm thế nào?
Đơn giản, ta thấy trong đây "check" xuất hiện nhiều hơn 1 lần. Vậy nhớ lại qui tắc. Lần đầu tiên dùng "@+id/" để thêm vào, rồi từ đó có thể refer đến nó bằng "@id/".


Và mọi thứ sẽ chạy tốt như bình thường, người lập trình cũng dễ đọc hơn.

Dường như mọi thứ tưởng chừng như đang dễ hiểu khi làm việc với XML Layout trong Android, mà mình lại làm cho nó trở nên khó hiểu nhỉ :D
Biết nhiều hơn một tí cũng tốt mà. Còn dùng thì chọn cách nào tùy bạn!

Cảm ơn các bạn đã xem! Chúc các bạn học tốt!



Chào các bạn,

Đã lâu rồi mình không viết bài về C# nhỉ :D, hôm nay nhân lúc đang làm đồ án môn Thiết kế phần mềm, cần sử dụng OOPinheritance,.. khá nhiều, mình xin viết một article nho nhỏ về hàm dựng (constructor) trong tính kế thừa của C#.



Theo nguyên tắc, khi khởi tạo đối tượng của lớp con, thì hàm dựng (constructor) của lớp cha sẽ được gọi trước, sau đó mới tới đổi tượng của lớp con.

Ví dụ mình có class Base Child kế thừa từ lớp Base như sau:


Thì khi Run, chương trình sẽ cho kết quả như sau:


Rõ ràng constructor của lớp Base đã được gọi trước! 

Chúng ta có thể kiểm tra các trường hợp khác:
 - Cho lớp Base thành abstract.
 - Sửa lại hàm Main, khởi tạo đối tượng bằng Child c = new Child();

Tất cả đều cho kết quả tương tự.

Chúng ta tiếp tục thử một trường hợp đặc biệt khác, thêm một constructor mới chứa tham số vào class Base Child như sau:


Run chương trình, chúng ta được kết quả sau:


Ở đây bạn thấy, dù chúng ta tạo đối tượng với constructor có tham số, thì constructor cơ bản của lớp Base luôn được gọi.
Bây giờ nếu bạn xóa đi constructor cơ bản (không tham số) của lớp Base và Run chương trình thì sẽ bị lỗi ngay lập tức.


Từ đó ta rút ra các kết luận sau:

Về vấn đề lỗi khi xóa constructor cơ bản của lớp Base: Khi một class được tạo ra, nếu class đó chưa có constructor, Visual C# sẽ tự động generate một constructor mặc định (không tham số) cho chúng ta, vì nếu không làm vậy chúng ta sẽ không thể tạo một object mới thuộc class đó rồi :D. Tuy nhiên nếu bạn tạo thêm một constructor khác có tham số, và xóa constructor mặc định cơ bản đi, thì Visual C# sẽ cho rằng class của bạn đã có thể tự tạo một object mới và sẽ không generate constructor mặc định. Điều đó đồng nghĩa với việc các object được tạo bằng cách gọi "new Base()" sẽ không thể khởi tạo.

Về vấn đề chính về constructor trong kế thừa mình muốn nói là khi bạn khởi tạo một đối tượng lớp con Child, thì C# sẽ automatically gọi constructor mặc định-không tham số của lớp cha Base trước, sau đó mới gọi constructor của lớp con (constructor nào của lớp con thì còn tùy mình new đối tượng với tham số gì :D).


Tại sao lại như vậy?

Theo ý kiến chủ quan của cá nhân mình, thì bạn cứ tưởng tượng lớp con Child là một "cụ thể hóa" của một lớp cha, giống như một chiếc xe, trước khi thành một chiếc xe nó phải qua công đoạn lắp ráp khung xe, thì KhungXe đó chính là lớp cha. Mà để tạo khung xe thì phải dùng constructor của KhungXe, trong đó sẽ gọi các hàm ráp, hàn, bắt ốc,... Sau khi đã xong phần khung xe, ta gọi constructor của XeMay gọi các hàm gắn bánh, gắn yên xe,.. giúp tạo thành một chiếc xe hoàn chỉnh.
Vậy thứ tự gọi thực sự logic và thực tế đúng không nào?

Nếu bạn muốn gọi constructor có tham số cụ thể của lớp cha theo ý của mình thì sử dụng tự khóa "base" như sau:



Trên là một số kiến thức tổng quát về Lập trình hướng đối tượng (OOP) cho các bạn nào lỡ quên :D.
Minh xin nói luôn một cách tổng quát, trong Java thì hàm dựng của lớp cha cũng sẽ tự động được gọi khi khởi tạo đối tượng của lớp con. Các bạn có thể tham khảo tại đây.



Cuối cùng, mình có một câu hỏi cho các bạn, có cách nào để tạo một đối tượng thuộc lớp con mà không cần gọi hàm dựng (constructor) của lớp cha không? 

Chào các bạn, chúc các bạn học tốt!



Chào các bạn,



Đã lâu rồi mình không viết blog, phần vì cũng lười, phần vì vừa phải làm final project tại CoderSchool. Hôm nay vừa internal demo xong, mình ngồi refactor lại code thì thấy như sau


Nhìn vào đống item ở thư mục 'res/layout' kìa!
Horrible, right?

Lang thang search trên mạng cũng thấy nhiều người có vấn đề giống như mình, như article ở StackOverflow.

Thư mục 'res/layout' của bạn quá lớn? khó kiểm soát? khó tìm kiếm? Hãy tưởng tượng trong lúc đang chạy deadline đầu óc rối bời mà tìm hoài không thấy cái 'item_recently_search.xml' nằm ở đâu? (mình cũng vừa bị T.T)

Ok, dài dòng đủ rồi, mình sẽ hướng dẫn các bạn ngay đây.

Bước 1: Đầu tiên các bạn tạo folder tổng "layouts"
Right-click vào thư mục 'res' -> New -> Directory -> gõ "layouts" (nhớ là có 's' nhé, không là bị trùng đấy)



Bước 2: Tạo các Res Folder tương ứng với các category mà bạn muốn làm.
Chọn 'layouts' folder vừa tạo.
Right-click -> New -> Folder -> Res Folder



Đặt tên cho Res Folder theo category mà bạn muốn làm.
Ví dụ ở một chương trình chat bạn sẽ có các category là "home", "profile" và "chat". Ví dụ mình sẽ đặt tên nó là 'chat' nha.

Khi vừa add xong 1 folder, đợi 1 lát để Gradle update cấu trúc chương trình. Sau đó bạn sẽ thấy một folder 'char' màu vàng tương ứng với folder 'res' nhưng lại không năm trong folder 'layouts'.
Thực chất nó vẫn nằm trong 'layouts' tuy nhiên vì minh chỉ mới add một folder duy nhất là 'chat' nên nó chỉ show 'chat' ra thôi.

Tiếp tục bạn tạo thêm một Res Folder mới tên là 'home' thì cấu trúc cây sẽ bắt đầu hiện ra đúng theo những gì bạn mong đợi: 'home' và 'chat' nằm trong 'layouts'.

Tới đây bạn đã chia thành công thư mục ra thành các category riêng biệt rồi.

Bước 3: Thêm folder 'layout' (không có 's') vào từng sub-folder tương ứng với các category cần thiết và sử dụng chúng bình thường như folder 'res/layout' truyền thống. 
Bạn cũng có thể thêm các folder 'menu', 'drawable', 'anim',... vào từng Res Folder nha.

Bước 4: Build thử chương trình. Bạn sẽ thấy trong tag 'android' của file 'app/build.gradle' xuất hiện đoạn lệnh sau


Đoạn lệnh này tương ứng cho khi bạn add 2 category là 2 Res Folder 'home' và 'chat'.
Nếu không tìm thấy các bạn có thể thêm bằng tay vào tag 'android' nha.


Chúc các bạn thành công và quản lý tốt source code của mình. Bởi vì lập trình còn là một quá trình tạo ra sản phẩm cùng với team nên code style là rất quan trọng. Và những thứ như vậy thường không được dạy ở trường.



Chào các bạn,  ở phần trước mình đã nêu ra tổng quan và 2 ý kiến cho rằng Thiết kế = Hình mẫu và Chức năng. Hôm nay mình sẽ tiếp tục bài viết chia sẻ về thiết kế trong cuộc sống của chúng ta.



Thiết kế = Thương hiệu

Khi một công ty luôn cung cấp ra thị trường những sản phẩm thẩm mỹ và chất lượng, thì thiết kế của công ty đó bây giờ sẽ biến thành thương hiệu. Nó trở thành một phương tiện để kết nối giữa nhà sản xuất và khách hàng của họ. Người tiêu thụ thường chọn gắn bó với một thương hiệu nào đó lâu dài bởi vì những thương hiệu là hiện thân và đại diện cho những giá trị và ý tưởng của họ, thứ mà đã làm họ cảm thấy hấp dẫn và thoải mái khi sử dụng.



Thiết kế = Trải nghiệm

Về cuối thập nên 90s, một thiết kế được gọi là thành công khi nó gây được sự chú ý, và để lại ấn tượng trong ký ức của khách hàng, và những thứ đấy đã trở thành sản phẩm - "sự trải nghiệm"
Hãy lấy những con búp bê làm ví dụ. Nó không chỉ là "mua" một con búp bê nữa, mà bây giờ trẻ em có thể thiết kế búp bê cho riêng mình, trang điểm nó giống như bản thân, cho nó đi shopping, đi spa, nuôi tóc dài,... Nó không còn là "mua" một con búp bê nữa, mà là tạo ra cả một không gian trải nghiệm riêng xung quanh việc mua và sở hữu con búp bê này.
Thiết kế đã vươn lên khỏi tầm của một sản phẩm độc lập hay một thương hiệu, mà nó còn bao gồm cả trải nghiệm của khách hàng!



Trải nghiệm: hãy nghĩ về lần gần đây nhất mà bạn đến rạp xem phim. Trong suốt 2-3h của bộ phim, bạn bị cuốn hút hoàn toàn vào màn ảnh, câu chuyện và những cảm xúc vui buồn của nhân vật. Khi bộ phim kết thúc bạn cảm thấy mình vừa lấy được một giá trị gì đấy cho cuộc sống của mình: bạn nên trân trọng cuộc sống này hơn? Bạn nên đỗi xử tốt hơn với gia đình và những người mình yêu quý? Hay đơn giản bạn cảm thấy sảng khoái tinh thần.Dù cảm xúc đó là gì đi nữa thì bạn bữa "trải nghiệm" qua một bộ phim, và trải nghiệm đó đã làm bạn hoàn toàn hài lòng vào số tiền đã bỏ ra.

Hiện nay trong lĩnh vực thiết kế nói chung và thiết kế trang web, phần mềm nói riêng, việc lưu tâm tới trải nghiệm của người dùng (User Experience) đã trở thành một vấn đề thực sự lớn và quan trọng. Nó dẫn trở thành một nhóm ngành mới, các nhà thiết kế UX sẽ nghiên cứu và đánh giá cách người dùng cảm nhận về sản phẩm như tính dễ sử dụng, tiện ích, sự hiệu quả, linh hoạt,..
Ví dụ: cái ghế trong rạp có thoải mái hay không, âm thanh quá to hay nhỏ, phòng chiếu có đủ tối hay không,...có ảnh hưởng đến việc xem phim của bạn không?
Lại nhìn rộng thêm nữa, trải nghiệm người dùng không chỉ bắt đầu khi bạn bước vào phòng chiếu mà là khi bạn vừa vào đến cửa rạp bạn đã được chào đón nồng nhiệt bởi đội ngũ nhân viên. Đập vào mắt bạn sẽ là poster và các nhân vật của bộ phim đang chiếu lôi cuốn và gây ấn tượng cho bạn. Khi đó bạn vừa có ý định mua vé xem một bộ phim thì ngay phía trên quầy bán vé là những tấm bảng đen lớn với tên phim, giờ chiếu và tình trạng phòng rõ ràng với nền chữ sáng màu vàng làm bạn cảm thấy ấm áp và được chào đón

Tất cả, tất cả mọi thứ đều tạo nên một hệ sinh thái, một không gian trải nghiệm người dùng



Tư duy thiết kế = Giải quyết vấn đề

Thuật ngữ "design thinking" được phổ biến bởi IDEO và đại học Stanford trong vòng 10 năm trở lại đây. Design thinking liên quan đến một tập hợp các quá trình nhận thức hướng vào giải quyết vấn đề. Các công đoạn của quá trình design thinking bao gồm: Định nghĩa vấn đề, xây dựng sự đồng cảm với người dùng, sinh ra nhiều ý tưởng, tạo mẫu, đưa ra prototype các giải pháp khả thi, thu thập phản hồi, cứ vậy và lặp đi lặp lại. Với cách hiểu này, Design là những thứ gì đó xoay quanh việc giải quyết vấn đề


Tóm lại, Thiết kế là một thuật ngữ dùng để chỉ một sản phẩm hoặc một dịch vụ nào đó trông như thế nào, cách nó hoạt động, cảm xúc của người dùng khi tương tác với nó, những trải nghiệm nào mà khách hàng nhận được, và cách suy nghĩ, tư duy và hành động để giải quyết vấn đề. Đó cũng là sự biểu hiện của Con người!


Bài viết dựa trên "Design and the Self" của blogger Irene Au

Contact Form

Name

Email *

Message *

Powered by Blogger.
Javascript DisablePlease Enable Javascript To See All Widget