Android Bài 35: Tìm Hiểu Về Danh Sách Tập 1 – Adapter & AdapterView

Posted by
Rating: 5.0/5. From 1 vote.
Please wait...

Chào mừng các bạn đã đến với bài học Android thứ 35, bài học về Danh sách – Tập 1 – Adapter & AdapterView. Đây là bài học trong chuỗi bài viết về lập trình ứng dụng Android bằng Java của Yellow Code Books.

Chào các bạn. Trước khi bài học hôm nay ra đời, mình đã nhận được kha khá các câu hỏi của các bạn về việc xây dựng hoàn chỉnh các ứng dụng Android của riêng bạn. Điều này chứng tỏ rằng 34 bài viết vừa qua của mình cũng đã cung cấp đủ cho bạn một nền tảng để hiểu về việc xây dựng một ứng dụng Android như thế nào. Chí ít thì nó cũng là một động lực để các bạn cảm thấy thích thú, tìm tòi những kiến thức vượt xa hơn cả những gì mình mang lại. Điều này là một tin mừng.

Tuy nhiên nếu dừng lại ở đây mà không viết tiếp thì hẳn là một tội lỗi rất lớn. Hôm nay. Sau nhiều đắn đo đong đếm, mình quyết định sẽ nói tiếp một khía cạnh mạnh mẽ khác của Android. Một thể loại giao diện rất phổ biến, không chỉ trên các thiết bị di động, giao diện có tên: Danh sách.

Nên nhớ Danh sách mà chúng ta nói đến, là một giao diện. Về mặt kiến thức, nó khác với cấu trúc dữ liệu dạng Danh sách (List) mà bài học Java mình sẽ nói đến một cách cụ thể. Và giao diện Danh sách hôm nay có liên quan đến hai khái niệm mở màn: AdapterAdapterView. Mời bạn cùng đến với 2 thành phần cơ bản này trước tiên.

Adapter & AdapterView Là Gì?

Cảm nhận ban đầu về khái niệm Adapter
Cảm nhận ban đầu về khái niệm Adapter

Nói về Adapter trước. Thú thiệt ngày trước, khi mình mới bắt đầu làm quen với Android, cảm nhận của mình khi đọc đến Adapter, chính là cái cục Adapter như hình minh họa của mình trên đây.

Khi mình tìm hiểu kỹ, thì mới ngộ ra rằng, nếu hiểu theo hướng công năng của nó, thì rõ ràng Adapter của Android nó mang công năng của bộ Adapter ngoài đời thực. Adapter ngoài đời thực mang trách nhiệm chuyển đổi đặc tính của một thiết bị ở đầu vào, thành các đặc tính phù hợp hơn với thiết bị ở đầu ra. Còn Adapter trong Android mang trách nhiệm chuyển đổi dữ liệu, từ dữ liệu thô ở đầu vào (mà mình gọi là DataSource như hình bên dưới), thành dữ liệu hiển thị lên View mà người dùng có thể dễ dàng đọc, hiểu và tương tác. Cơ bản thì công năng của Adapter sẽ được minh họa bằng sơ đồ sau.

Sơ đồ cho thấy vai trò của Adapter
Sơ đồ cho thấy vai trò của Adapter

Đến đây hẳn sẽ làm bạn hoang mang. Bạn sẽ tự hỏi rằng, tại sao phải cần sự chuyển đổi dữ liệu này? Các bài học trước giờ có nói đến cái gì liên quan đến chuyển đổi dữ liệu này đâu? Thực ra thì sự chuyển đổi này rất quan trọng, và trước đây bạn chưa làm quen là vì chưa đụng đến nó, nay đụng đến rồi bạn cũng nên biết.

Chúng ta sẽ tìm hiểu từ mong muốn thực tế trước. Các bạn đều biết. Theo như lời mở đầu bài viết trên kia. Hầu hết các ứng dụng, không riêng gì các ứng dụng trên thiết bị di động đâu, đều cần hiển thị một dạng giao diện đặc biệt: giao diện theo kiểu Danh sách. Đó có thể là danh sách các hình ảnh, danh sách các email đến. Hay như TourNote sau này còn có danh sách các ghi chú mà bạn sắp sửa bắt tay vào xây dựng đây. Các danh sách này được sắp xếp theo chiều ngang, hoặc theo chiều dọc, hoặc theo dạng bảng (hay còn gọi dạng lưới) trên một vùng không gian màn hình, hoặc mỗi phần tử danh sách là một màn hình (dạng view pager). Nói chung là có rất nhiều các loại danh sách mà bạn đã biết. Như các hình ảnh chụp màn hình của mình dưới đây.

Bạn có nhìn thấy các danh sách mà mình muốn nói đến không?
Bạn có nhìn thấy các danh sách mà mình muốn nói đến không?

Các danh sách này như bạn thấy, chứa rất nhiều phần tử. Mỗi phần tử có thể chỉ là text, có thể là ảnh, có thể vừa ảnh vừa text, hay vừa icon vừa text,… Số lượng phần tử trong các danh sách có thể là cố định, hoặc cũng có thể được thêm động vào danh sách khi ứng dụng đã thực thi (như các ghi chú của TourNote, hay gần gũi hơn đó là các phần tử feed trên danh sách feed của Facebook).

Từ những yêu cầu đặc biệt trên đây của giao diện Danh sách, thì bạn cũng có thể thấy rằng không thể nào mà bạn có thể tự viết các dòng code rồi thêm từng phần tử vào trong một View (như ScrollView chẳng hạn) để giả lập thành một danh sách được. Mà bạn phải dùng đến Adapter. Adapter sẽ giúp bạn chuyển đổi dữ liệu từ DataSource ban đầu thành một danh sách chuyên nghiệp. Từ đây chúng ta gọi các danh sách này là các AdapterView luôn nhé. Vậy để nhớ lâu, sơ đồ của chúng ta nên viết lại đúng đắn như sau.

Sơ đồ cho thấy vai trò của Adapter (cập nhật)
Sơ đồ cho thấy vai trò của Adapter (cập nhật)

Với sơ đồ trên đây thì chuỗi kiến thức về Adapter này chúng ta sẽ tìm hiểu gì?

  • Về khối DataSource: đọc lên thấy cao siêu nhưng thực ra chúng chỉ là kiểu mảng hoặc kiểu List là được. Adapter sẽ có cách (hoặc bạn cũng giúp Adapter ít nhiều) hiểu được các thể loại hoặc các cấu trúc này để chuyển chúng lên AdapterView hiển thị ra cho người dùng.
  • Về khối Adapter: chúng ta sẽ làm quen với ArrayAdapter được cung cấp sẵn bởi hệ thống trước. Rồi các bài học sau chúng ta sẽ tìm cách tùy chỉnh (hay còn gọi custom) lại ArrayAdapter này sao cho có thể hiển thị các danh sách phức tạp theo ý của chúng ta. Ngoài ArrayAdapter ra thì sau này các bạn sẽ gặp một vài thể loại Adapter khác cho một số danh sách đặc thù. Tuy cái tên của chúng khác nhau, nhưng có điểm chung là đều có chữ Adapter ở cuối mỗi cái tên, như ExpandapleListAdapter, PagerAdapter. Cộng với việc xây dựng các Adapter là gần như giống nhau nên bạn cứ yên tâm là không đau đầu lắm đâu nhé.
  • Về khối AdapterView: uhm việc tìm hiểu cũng sẽ dài hơi không kém Adapter. Các bạn sẽ được làm quen với ListView, GridView, ExpandableListView, Gallery, StackView, Spinner. Và cả RecyclerView. Tuy RecyclerView bản chất không thuộc khối AdapterView đâu, nhưng do công năng và cách sử dụng tương tự nên mình nêu ra ở đây luôn, chúng ta sẽ nói về RecyclerView riêng nó ở một bài học khác.

Ôi thôi nhức đầu quá. Yên tâm, chúng ta sẽ tìm hiểu từ từ. Và chỉ cần mình nêu một vài ví dụ thôi, thì bạn sẽ hiểu hết cách vận hành bộ ba DataSource – Adapter – AdapterView này nhanh thôi.

Xây Dựng ListView Từ ArrayAdapter

Nào dựa trên những gì mình nói trên đây, bạn đã biết ListView chính là một AdapterView. Còn ArrayAdapter là một Adapter. DataSource một lát nữa đây chúng ta sẽ dùng một mảng được xây dựng sẵn. Bạn sẽ thấy sự kết hợp của bộ ba DataSource – Adapter – AdapterView ngay trong bài ví dụ hôm nay để hiểu rõ hơn về giao diện Danh sách.

Tuy nhiên mình cũng xin nói thêm, rằng mục này mình không nói cụ thể vào Adapter hay AdapterView gì đâu nhé. Mình chỉ vận dụng những gì có sẵn của hệ thống để bạn nắm được ý của bài học. Bạn sẽ hiểu tường tận Adapter hơn khi vào các bài kế tiếp. Và bạn cũng sẽ hiểu tường tận về ListView hay các AdapterView khác ở các phần tiếp theo của chuỗi bài Danh sách nữa. Các bạn chờ đọc nhé.

Và một ý nữa trước khi chính thức vào xây dựng ListView. Đó là code của bài học hôm nay tuy được vận dụng trên TourNote nhưng sẽ không được đưa lên GitHub. Vì ListViewAdapter ở bài này còn khá sơ sài, chúng ta sẽ cần một giao diện Danh sách cao cấp hơn, phức tạp hơn và sẽ được giới thiệu ở các bài học sau.

Nào bạn hãy mở project TourNote lên nhé. Mình sẽ xây dựng ListView trên Activity ContactActivity, vì code và giao diện ở Activity này còn đơn giản, rất dễ để chúng ta code và chạy thử. Bạn cũng có thể tự tạo mới một project rồi làm theo các bước như mình dưới đây cũng được nhé.

Xây Dựng DataSource

Nói xây dựng nghe ghê quá. Cái chính là chúng ta khai báo một mảng các String như sau ở ContactActivity. Chúng chính là DataSource của chúng ta.

public class ContactActivity extends AppCompatActivity {

    // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn

    private static final String[] items = {"lorem", "ipsum", "dolor",
            "sit", "amet", "consectetuer",
            "adipiscing", "elit", "morbi",
            "vel", "ligua", "vitae",
            "arcu", "aliquet", "mollis",
            "eiam", "vel", "erat",
            "placerat", "ante", "porttitor",
            "sodales", "pellentesque", "augue",
            "purus"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);

        // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn
    }

    // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn
}

Mảng items trên đây chính là DataSource. Bạn đừng cố hiểu nghĩa các String bên trong mảng nhé, mình chỉ tạo đại chúng thôi, tạo nhiều nhiều phần tử mảng xíu để lát cái Danh sách hiển thị lên màn hình đủ dài để bạn thử cuộn lên/xuống. Bạn có thể sử dụng List thay cho mảng. Nhưng đơn giản nhất ở bài học hôm nay vẫn là mảng, chúng ta sẽ có dịp dùng đến List sau này.

Xây Dựng AdapterView

AdapterView của chúng ta đang muốn xây dựng chính là ListView. Và bở vì AdapterView là một View, nên nó có thể được “nhét” vào giao diện của Activity. Mặt khác bạn còn có thể nhìn thấy chúng trong Pallet của editor giao diện nữa. Bạn hãy mở file giao diện activity_contact.xml lên, nhớ chọn tab Design ở editor này, và tìm xem nhé.

ListView hay các AdapterView khác đều xuất hiện trong Pallete
ListView hay các AdapterView khác đều xuất hiện trong Pallete

Như vậy để nhét ListView vào màn hình activity_contact, bạn có thể kéo thả ListView từ Pallete vào màn hình trực quan thiết kế. Rồi sau đó canh chỉnh các thông số của ListView này sao cho nó chiếm không gian toàn màn hình nhé, như hình minh họa dưới đây của mình, và hãy chú ý một số canh chỉnh của mình trong khung màu đỏ (bạn có thể xem thêm bài viết về ContraintLayout này của mình nếu như vẫn chưa tự tin lắm với việc canh chỉnh bằng giao diện đối với layout này).

Kéo thả ListView và thiết lập vài thông số
Kéo thả ListView và thiết lập vài thông số

Nhớ đừng quên đặt ID cho ListView này là my_lisview nhé. Chúng ta sẽ cần đến ID khi dùng Adapter đổ dữ liệu từ DataSource lên ListView này ở Java code sau.

Nếu chuyển sang tab Text của cửa sổ thiết kế, thì ListView mới thêm vào của chúng ta sẽ như sau.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ContactActivity">

    <ListView
        android:id="@+id/my_listview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Dùng Adapter Để Kết Nối DataSource Với AdapterView

Chúng ta đã có DataSource chính là mảng các String rồi, và cũng đã khai báo AdapterView chính là ListView rồi. Vậy Adapter dùng như thế nào? Như đã nói, chúng ta sẽ dùng đến ArrayAdapter. Bạn hãy thêm vào ContactActivity các dòng code được tô sáng sau, rồi mình sẽ giải thích sau nhé.

public class ContactActivity extends AppCompatActivity {

    // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn

    private static final String[] items = {"lorem", "ipsum", "dolor",
            "sit", "amet", "consectetuer",
            "adipiscing", "elit", "morbi",
            "vel", "ligua", "vitae",
            "arcu", "aliquet", "mollis",
            "eiam", "vel", "erat",
            "placerat", "ante", "porttitor",
            "sodales", "pellentesque", "augue",
            "purus"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);

        // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn

        // Khai báo myListView và kết nối với my_listview bên XML
        ListView myListView = findViewById(R.id.my_listview);
        // Khai báo myArrayAdapter
        ArrayAdapter<String> myArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items);
        // Gắn myArrayAdapter vào cho myListView
        myListView.setAdapter(myArrayAdapter);
    }

    // Các khai báo khác của lớp này cứ để nguyên vậy trong code của bạn, mình tạm ẩn đi cho bạn dễ nhìn
}

Dòng code đầu tiên.

ListView myListView = findViewById(R.id.my_listview);

Theo mình nghĩ đây dòng code quen thuộc với các bạn rồi, do đó không nên nói nhiều. Cơ bản nó giúp chúng ta khai báo ra một ListView ở Java code, có tên là myListView. ListView này cũng chính là cái ListView đã vẽ bên giao diện XML, để một lát nữa chúng ta sẽ cần đến cái ListView Java code vừa được khai báo này.

Đến các dòng code quan trọng tiếp theo, chính là dòng code khởi tạo một ArrayAdapter có tên myArrayAdapter. ArrayAdapter này được khai báo là ArrayAdapter<String>, cho biết rằng Adapter này giúp chuyển đổi danh sách các kiểu String từ DataSource sang AdapterView.

ArrayAdapter<String> myArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items);

Chúng ta cùng làm quen với 3 thông số truyền vào cho phương thức khởi tạo ArrayAdapter này.

  • Tham số đầu tiên đòi hỏi một Context. Context là gì thì chúngta tìm hiểu sau. Trong trường hợp này Activity chính là một Context, do đó chúng ta dùng từ khóa this, ý nói chính là Activity này (ContactActivity).
  • Tham số thứ hai chúng ta truyền vào một android.R.layout.simple_list_item_1. Đây là một view được xây dựng sẵn của hệ thống, chúng ta lấy tạm ra dùng. View này giúp hiển thị vỏn vẹn một String của chúng ta lên TextView của một phần tử trong Danh sách. Hay nói cách khác, Danh sách, hay ListView của chúng ta sẽ là danh sách các TextView, mỗi TextView sẽ giúp hiển thị một String của chúng ta. Và từng phần tử danh sách này chúng ta dùng tạm cái hệ thống đang có. Tuy việc dùng tạm này cho ra một ListView “tầm thường” thôi nhưng đủ để chúng ta hiểu về Adapter. Chúng ta sẽ cùng nhau xây dựng một ListView “hoành tráng” ở bài học sau nhé.
  • Tham số thứ ba chính là DataSource đã khai báo. Bạn đã biết, DataSource này được truyền vào để ArrayAdapter biết mỗi String có nội dung là gì, mà hiển thị lên từng TextView của từng phần tử Danh sách.

Dòng code cuối cùng cần tìm hiểu. Tuy ngắn nhưng quan trọng. Thiếu nó thì ListView cũng chưa có dữ liệu. Đó chính là dòng gán ArrayAdapter vừa khai báo vào cho ListView.

myListView.setAdapter(myArrayAdapter);

Đây là kết quả khi bạn thực thi chương trình (và nhớ là sau khi thực thi, bạn phải vào màn hình ContactActivity thông qua lựa chọn trên ActionBar của màn hình chính MainActivity nhé).

ListView của bài học hôm nay
ListView của bài học hôm nay

Wow một khi ListView đã hiện lên như trên đây thì có nghĩa là bạn đã thành công, và đã hiểu sơ qua khái niệm về Adapter & AdapterView của bài học hôm nay rồi đó. Bạn có thể thử cuộn lên/xuống danh sách vừa tạo này. Thậm chí hãy thử nhấn vào từng phần tử của danh sách để xem giao diện của chúng ra sao. Dĩ nhiên khi bạn nhấn vào từng phần tử của danh sách, mọi thứ vẫn cứ “trơ trơ”, vì chúng ta chưa code gì cho sự kiện này cả. Bài học đã dài và mình sẽ trình bày tiếp việc bắt các sự kiện trên AdapterView, cụ thể là ListView ở bài học sau nhé.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:

Đánh giá 5 sao ở mỗi bài nếu thấy thích.
Comment bên dưới mỗi bài nếu có thắc mắc.
Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.

Bài Kế Tiếp

Bài sau chúng ta sẽ tìm hiểu thêm về ListView. Để cùng làm rõ xem Adapter và AdapterView có những hỗ trợ gì hay ho nữa không, đồng thời cùng cũng cố “tay nghề” xây dựng Adapter và AdapterView của chúng ta. Ngoài ra việc xây dựng các sự kiện trên ListView cũng sẽ được mình nói tới ở bài này.

Gửi phản hồi