Android Bài 36: Tìm Hiểu Về Danh Sách Tập 2 – ListView & ListActivity

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ứ 36, bài học về Danh sách – Tập 2 – ListView & ListActivity. Đâ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.

Sau khi hoàn thành xong tập đầu tiên về giao diện Danh sách, bạn đã hiểu được phần nào khái niệm và cách vận dụng của bộ ba DataSource – Adapter – AdapterView để tạo nên một Danh sách từ mảng các String rồi đúng không nào. Và thông qua bộ ba phần tử của Danh sách này, bạn cũng đã được làm quen với một AdapterView khá hay có tên ListView.

Ở phần này, chúng ta tạm gác việc tìm hiểu thêm về Adapter. Mà hãy tiếp tục nói về ListView, AdapterView này còn có nhiều điều để nói lắm đấy. Rồi mở rộng hơn, dựa trên kiến thức khá ổn của ListView, ở bài sau chúng ta sẽ nói đến nhiều các thể loại AdapterView khác, dù giao diện chúng có khác nhau, nhưng cách thức xây dựng cũng sẽ gần như ListView mà thôi. Sau cùng, chúng ta cùng nhau quay lại Adapter để mổ xẻ phần tử này, khi mà kiến thức sử dụng AdapterView đã khá vững vàng.

Thôi lan man quá. Mời các bạn bắt đầu bài học.

ListView Và ListActivity

Vâng, bài hôm trước chúng ta làm quen với ListView. Bài này chúng ta nói rõ hơn về ListView. Nhưng… có cả ListActivity nữa ư, nó là cái gì? Có giống với ListView không?

Thực ra thì, ListViewListActivity cũng cùng một công năng là xây dựng một giao diện dạng Danh sách theo chiều dọc màn hình. Với việc làm quen ở bài trước, chúng ta xây dựng Danh sách này bên trong một Activity có sẵn. Tức là trong Activity đó chúng ta kéo thả một ListView vào như một View bình thường, canh chỉnh vị trí của ListView đó ở trên giao diện của Activity. Nhưng Google không muốn chúng ta sống một cách giản đơn như vậy. Họ nhận thấy rằng, nếu một Activity mà chỉ có một ListView bên trong nó, thì họ có một cách nữa để chúng ta xây dựng ListView, họ đẻ ra một Activity đặc biệt có tên ListActivity.

Về cơ bản ListActivity giúp thiết kế ra một giao diện Danh sách cực nhanh (có thể mục đích chính của Google là đây). Chỉ cần vài thao tác bạn đã có một Danh sách, thậm chí ListActivity không cần đến file giao diện (tức là file XML luôn), ghê không. Bạn chỉ cần khai báo file XML cho ListActivity khi bạn muốn thiết kế giao diện vừa ListView vừa các thành phần giao diện khác nữa. Nào chúng ta cùng nhau xây dựng nhanh một ListActivity như thế này.

Xây Dựng ListActivity Không Cần Giao Diện XML

Đầu tiên, bạn hãy mở lại project TourNote, mở lại lớp ContactActivity đã thực hành xây dựng ListView từ bài học trước. Hoặc bạn có thể mở lại project mới của bạn đã tạo ra hôm trước. Hoặc siêng hơn có thể tạo mới một project cho bài hôm nay cũng được. Nhưng hãy đảm bảo ContactActivity, hay Activity nào đó của riêng bạn, kế thừa từ ListActivity nhé. Đại loại như thế này.

public class ContactActivity extends ListActivity {

    // Code trong này lát tính tiếp
}

Tiếp theo. Về DataSource, bạn hãy giữ nguyên việc khai báo danh sách các mảng String mang tên items như bài học trước.

public class ContactActivity extends ListActivity {

    // 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"};

    // 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ũng chưa có gì khác nhiều đúng không nào. Tiếp tục, ở code của phương thức onCreate(). Bạn chú ý các dòng được tô sáng của mình nhé.

public class ContactActivity extends ListActivity {

    // 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);
        // Không cần dòng này, vì ListActivity có sẵn ListView cho chúng ta rồi 
//        setContentView(R.layout.activity_contact);

        // ListActivity không hỗ trợ các hàm này
//        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//        getSupportActionBar().setHomeButtonEnabled(true);

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

//        // Khai báo myArrayAdapter
        ArrayAdapter<String> myArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items);
//        // Gắn myArrayAdapter vào cho ListView có sẵn
        setListAdapter(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
}

Do onCreate() có nhiều khác biệt, nên mình xin giải thích rõ ràng hơn như sau.

  • Đầu tiên, bạn có thể thấy chúng ta không cần đến phương thức setContentView() nữa. Bạn đã biết phương thức này giúp định nghĩa giao diện cho Activity. Và vì ListActivty đã có sẵn ListView rồi nên chúng ta có thể bỏ luôn việc khai báo giao diện này. Lát nữa chúng ta sẽ thử tạo giao diện cho ListActivity để mà hiển thị vừa ListView vừa TextView sau xem thế nào nhé.
  • Tiếp theo, các dòng code liên quan đến getSupportActionBar() chúng ta cũng không thể thêm vào ListActivity. Vì sao ư? Mình nghĩ có lẽ ListActivity không hỗ trợ ActionBar. Đây là một điểm trừ cực kỳ lớn khiến trước giờ mình hầu như không dùng đến ListActivity.
  • Tuy dòng code khai báo myArrayAdapter giống hệt bài học trước. Nhưng dòng gán Adapter vào ListView thì rất khác. Hôm trước bạn phải gọi myListView.setAdapter(myArrayAdapter), thì hôm nay chỉ cần gọi setListAdapter(myArrayAdapter), vì như mình đã nói, ListActivity có sẵn ListView rồi nên phương thức setListAdapter() sẽ biết và set thẳng Adapter vào cho ListView sẵn có luôn.

Giờ thị bạn có thể thực thi chương trình để xem ListActivity của chúng ta ở bài này có khác gì với cách dùng ListView ở bài hôm trước không nhé.

ListActivity của bài hôm nay
ListActivity của bài hôm nay

Bạn có thể thấy rằng ListActivity này không có ActionBar đúng không. Tuy nhiên mình không nói nhiều về khía cạnh này của ListActivity. Mình chỉ biết có ý kiến rằng nếu muốn ListActivityActionBar thì phải set lại Theme về Theme.Holo, nhưng vậy thì chán quá. Thôi tùy bạn quyết định có dùng ListActivity hay không nhé.

Xây Dựng ListActivity Với Giao Diện XML

Dù cho bạn có thích ListActivity hay không. Nhưng mình đã lỡ nói rồi nên sẽ nói cho trót. Mục trên đây giúp bạn xây dựng nhanh một giao diện dạng Danh sách mà không cần phải khai báo file XML, tức file giao diện cho Activity. Cách làm này rất nhanh, nhưng không phải lúc nào trong thực tế giao diện cũng chỉ có mỗi một ListView như thế này. Chính vì vậy chúng ta giả sử cần một giao diện phức tạp hơn nhưng cũng vừa đủ đơn giản thôi, giao diện chúng ta sắp xây dựng sẽ vừa có TextView vừa có ListView xem sao nhé.

Bạn hãy mở activity_contact.xml lên. Hoặc bất kỳ file XML nào của project của bạn. Chúng ta tiến hành kéo thả vào màn hình trực quan thiết kế TextViewListView sao cho nó trông như thế này.

Thiết kế giao diện bao gồm TextView và ListView
Thiết kế giao diện bao gồm TextView và ListView

Bạn chú ý, đoạn này của mình rất quan trọng này. Bạn nhớ set ID cho TextViewmyTextView. Còn ID cho ListView@android:id/list. Bạn có thể set ID cho TextView là bất kỳ, như những gì bạn từng làm với ID trước kia. Nhưng ID cho ListView lúc này luôn luôn phải là @android:id/list bạn nhé. Đó là quy định của ListActivity, hệ thống sẽ tìm đến @android:id/list để thiết lập Adapter cho nó thông qua phương thức setListAdapter() trên kia.

Nếu bạn muốn xem file XML của giao diện trên, thì đây.

<?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="@android:id/list"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/myTextView" />

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:gravity="center_horizontal"
        android:text="Bellow is a ListView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Với sự chỉnh sửa này thì ContactActivity chỉ cần bạn để lại phương thức setContentView()onCreate() là được. Code hoàn chỉnh giống như sau, khác biệt duy nhất so với code ở mục trên kia chỉ là dòng mà mình đã tô sáng.

public class ContactActivity extends ListActivity {

    // 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);

        // ListActivity không hỗ trợ các hàm này
//        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//        getSupportActionBar().setHomeButtonEnabled(true);

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

        // Khai báo myArrayAdapter
        ArrayAdapter<String> myArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items);
        // Gắn myArrayAdapter vào cho ListView có sẵn
        setListAdapter(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
}

Kết quả khi thực thi chương trình. Bạn chú ý đã thấy có TextView hiển thị ở trên ListView đấy nhé.

ListActivity với giao diện gồm TextView và ListView
ListActivity với giao diện gồm TextView và ListView

Sự Kiện Click Trên ListView

Qua các kiến thức về ListView mà bạn đã làm quen hôm trước đến giờ, thì bạn đã phần nào hiểu về ListView rồi. Tuy nhiên chúng ta cũng chỉ mới hiển thị ListView lên màn hình thôi. Một công năng quan trọng khác của ListView mà mình muốn nói tới, đó là giúp chúng ta bắt được các sự kiện trả về khi người dùng thao tác trên Danh sách. Đó có thể là sự kiện click chọn trên một phần tử Danh sách, hay sự kiện nhấn giữ (long click) trên một phần tử Danh sách, hay sự kiện chọn (select) trên một phần tử Danh sách (dành cho các thiết bị có các phím điều hướng chuyên dụng như TV chẳng hạn),… Có nhiều sự kiện trả về là thế, nhưng mục này mình sẽ nói đến một sự kiện tiêu biểu, được sử dụng rất nhiều, hầu như có Danh sách là có sự kiện này, đó là sự kiện click chọn (hay nói ngắn gọn là click, hay touch) trên Danh sách.

Nói về mặt sử dụng, thì kiến thức về bắt sự kiện click ở mục này nói một mục thôi là đủ. Nhưng do chúng ta đã tách ra làm hai cách khai báo, là ListView của bài trước, và ListActivity của bài hôm nay, nên sự kiện click cũng sẽ được mình tách ra làm 2 mục nhỏ cho các bạn dễ phân biệt và nhớ lâu.

Sự Kiện Click Trên ListView Bài Hôm Trước

Với bài hôm trước, chúng ta có một myListView được khai báo tường minh từ ListView. Do đó để lấy sự kiện click trên myListView, chúng ta gọi đến sự kiện setOnItemClickListView() trên myListView này. Code hoàn chỉnh của ContactActivity khi này như sau (bạn hãy chú ý đoạn được tô sáng).

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);

        // Khai báo sự kiện Click cho myListView
        myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(ContactActivity.this, items[position] + " clicked", Toast.LENGTH_SHORT).show();
            }
        });
    }

    // 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
}

Nếu bạn đã từng hiểu cách thiết lập sự kiện click trên Button ra sao, thì sự kiện click trên phần tử của Danh sách trên đây cũng thiết kế tương tự. Có điều trong interface OnItemClickListener trên có khai báo một phương thức gọi về onItemClick() với vài tham số hữu dụng, như position, tham số này trả về một con số, con số này chính là vị trí của phần tử trong Danh sách được click. Dựa vào thông số position này mà trong phương thức onItemClick() mình có thể hiển thị một Toast với nội dung chính là phần tử thứ position trong danh sách String items.

Kết quả khi thực thi chương trình, và khi nhấn vào một item trên list.

Khi nhấn vào phần tử trên ListView
Khi nhấn vào phần tử trên ListView

Sự Kiện Click Trên ListActivity

Còn đây là cách set sự kiện click cho phần tử của ListActivity hôm nay. Ngoại trừ các dòng code khác nhau liên quan đến việc khai báo ListView mà bạn đã biết trên kia, thì việc set sự kiện click dưới đây chỉ có việc thay thế myListView.setOnItemClickListener() thành getListView().setOnItemClickListener() mà thôi, tại sao có sự khác biệt này thì chắc bạn cũng hình dung ra được rồi.

public class ContactActivity extends ListActivity {

    // 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);

        // ListActivity không hỗ trợ các hàm này
//        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//        getSupportActionBar().setHomeButtonEnabled(true);

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

        // 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
        setListAdapter(myArrayAdapter);

        // Khai báo sự kiện Click cho item của list
        getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(ContactActivity.this, items[position] + " clicked", Toast.LENGTH_SHORT).show();
            }
        });
    }

    // 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
}

Kết quả thực thi cũng tương tự như trên mà thôi.

Khi nhấn vào phần tử trên ListView
Khi nhấn vào phần tử trên ListView

Chúng ta kết thúc bài học hôm nay tại đây. Qua đây hẳn bạn đã thủ sẵn cho bản thân 2 cách xây dựng ListView rồi. Và bạn cũng đã có thể bắt sự kiện click trên phần tử của ListView nữa. Như phần mở đầu bài viết có nói, xong phần này, tiếp theo chúng ta sẽ nói về các cách xây dựng một số AdapterView khác. Sẽ có nhiều thú vị lắm đấy.

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

Chúng ta sẽ tìm hiểu thêm các AdapterView khác. Như Spinner, GridView, AutoCompleteTextView.

Gửi phản hồi