Adapter View ở khắp mọi nơi và bạn sẽ khó mà tìm thấy một ứng dụng Android phổ biến mà không sử dụng chúng. Tên nghe có vẻ lạ, nhưng nếu bạn nghĩ rằng bạn chưa bao giờ nhìn thấy một Adapter View, thì bạn có thể đã sai lầm. Mỗi khi bạn nhìn thấy một ứng dụng Android hiển thị các phần tử của giao diện người dùng dưới dạng một danh sách, một lưới hoặc ngăn xếp, tức là bạn đã nhìn thấy một Adapter View trong thực tế.
Một Adapter View, như tên gọi của nó, là một đối tượng
View
. Điều này có nghĩa là, bạn có thể thêm nó vào các Activity của bạn theo cùng một cách bạn thêm bất cứ thành phần giao diện người dùng khác. Tuy nhiên, nó không có khả năng hiển thị bất kỳ dữ liệu nào của riêng mình. Nội dung của nó luôn luôn được xác định bởi một đối tượng khác, một Adapter. Trong bài này, tôi sẽ hướng dẫn cho bạn cách làm thế nào để tạo ra các Adapter và sử dụng chúng để tạo ra các loại Adapter View khác nhau chẳng hạn như ListView
và GridView
.1. Adapter là gì?
Một Adapter là một đối tượng của một lớp cài đặt giao diện
Adapter
. Nó đóng vai trò như là một liên kết giữa một tập hợp dữ liệu và một Adapter View, một đối tượng của một lớp thừa kế lớp trừu tượng AdapterView
. Tập hợp dữ liệu có thể là bất cứ điều gì mà trình bày dữ liệu một cách có cấu trúc. Mảng, các đối tượng List
và các đối tượng Cursor
thường sử dụng bộ dữ liệu.
Một Adapter có trách nhiệm lấy dữ liệu từ bộ dữ liệu và tạo ra các đối tượng
View
dựa trên dữ liệu đó. Các đối tượng View
được tạo ra sau đó được sử dụng để gắn lên bất kỳ Adapter View mà ràng buộc với Adapter
Bạn có thể tạo các lớp Adapter riêng của bạn từ đầu, nhưng hầu hết các nhà phát triển muốn sử dụng hoặc thừa kế các lớp Adapter được cung cấp bởi Android SDK, chẳng hạn như
ArrayAdapter
và SimpleCursorAdapter
. Trong hướng dẫn này, chúng ta sẽ tập trung vào lớp ArrayAdapter
.2. Adapter View làm việc như thế nào?
Adapter View có thể hiển thị các bộ dữ liệu lớn rất hiệu quả. Ví dụ,
ListView
và GridView
có thể hiển thị hàng triệu phần tử mà không có bất cứ độ trễ đáng kể nào trong khi vẫn sử dụng bộ nhớ và CPU rất thấp. Chúng có thể làm điều đó như thế nào? Các Adapter View khác nhau tuân theo những chiến lược khác nhau. Tuy nhiên, đây là những gì mà hầu hết chúng thường làm.- Chúng chỉ kết xuất những đối tượng
View
mà đã trên màn hình hoặc nó đang di chuyển vào màn hình. Bằng cách này, bộ nhớ tiêu thụ bởi một Adapter View có thể được cố định và độc lập với kích thước của tập dữ liệu. - Chúng cũng cho phép các nhà phát triển giảm thiểu công sức cho các hoạt động inflate layout và tái sử dụng các đối tượng
View
sẵn có đã di chuyển khỏi màn. Điều này sẽ giúp tiêu thụ CPU thấp.
3. Tạo một ArrayAdapter
Để tạo một Adapter, bạn cần những thứ sau đây:
- một tập hợp dữ liệu
- một tập tin có chứa Layout của các đối tượng
View
được tạo ra
Ngoài ra, vì lớp
ArrayAdapter
chỉ có thể làm việc với chuỗi, nên bạn cần phải chắc chắn rằng Layout của các đối tượng View
được tạo ra có chứa ít nhất một TextView
.Bước 1: Tạo bộ dữ liệu
Lớp
ArrayAdapter
có thể sử dụng cả mảng và các đối tượng List
như là bộ dữ liệu. Bây giờ, hãy sử dụng một mảng làm tập hợp dữ liệu.
1
2
3
4
5
6
7
| String[] cheeses = { "Parmesan" , "Ricotta" , "Fontina" , "Mozzarella" , "Cheddar" }; |
Bước 2: Tạo tập tin nguồn
Tạo một tập tin Layout XML mới mà phần tử gốc là một
LinearLayout
và đặt tên nó là item.xml. Kéo và thả một Large text widget vào trong đó và thiết lập giá trị thuộc tính id
của nó thành cheese_name. Tập tin Layout XML sẽ trông như thế này:
01
02
03
04
05
06
07
08
09
10
11
12
|
android:orientation = "vertical" android:layout_width = "match_parent" android:layout_height = "match_parent" android:padding = "@dimen/activity_horizontal_margin" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textAppearance = "?android:attr/textAppearanceLarge" android:text = "Large Text" android:id = "@+id/cheese_name" /> </ LinearLayout > |
Bước 3: Tạo Adapter
Trong Activity của bạn, tạo ra một đối tượng mới của lớp
ArrayAdapter
bằng cách sử dụng hàm xây dựng của nó. Đối với các đối số của nó, truyền vào tên của tập tin tài nguyên, định danh của TextView
, và một tham chiếu đến mảng. Adapter bây giờ đã sẵn sàng.
1
2
3
4
5
6
| ArrayAdapter new ArrayAdapter this , R.layout.item, R.id.cheese_name, cheeses ); |
4. Tạo một List
Để hiển thị một danh sách cuộn theo chiều dọc của các phần tử, bạn có thể sử dụng
ListView
. Để thêm nó vào Activity của bạn, bạn có thể kéo và thả nó vào trong tập tin layout XML của Activity hoặc tạo ra nó bằng cách sử dụng hàm xây dựng của nó trong code Java của bạn. Bây giờ, chúng ta thực hiện cái thứ hai.
1
| ListView cheeseList = new ListView( this ); |
Thông thường, không có các thành phần giao diện người dùng khác được đặt bên trong một Layout có chứa một
ListView
. Vì vậy, truyền ListView
vào phương thức setContentView()
của Activity để nó chiếm toàn bộ màn hình.
1
| setContentView(cheeseList); |
Để liên kết
ListView
với Adapter mà chúng ta đã tạo ra ở bước trước đó, hãy gọi phương thức setAdapter()
như hình dưới đây.
1
| cheeseList.setAdapter(cheeseAdapter); |
Nếu bạn chạy ứng dụng của bạn ngay bây giờ, bạn có thể xem nội dung của các mảng ở dạng một danh sách.
5. Tạo một Grid
Để hiển thị một danh sách lưới hai chiều cuộn theo chiều dọc của các phần tử, bạn có thể sử dụng
GridView
. Cả ListView
và GridView
là lớp con của lớp trừu tượng AbsListView
và chúng chia sẻ nhiều điểm tương đồng. Vì vậy, nếu bạn biết cách sử dụng một cái, thì bạn cũng sẽ biết cách sử dụng cái còn lại.
Sử dụng hàm xây dựng của lớp
GridView
để tạo ra một đối tượng mới và truyền nó vào phương thức setContentView()
của Activity.
1
2
| GridView cheeseGrid = new GridView( this ); setContentView(cheeseGrid); |
Để thiết lập số cột trong Grid, hãy gọi phương thức
setNumColumns()
của nó. Tôi sẽ cho nó hai cột.
1
| cheeseGrid.setNumColumns( 2 ); |
Thông thường, bạn sẽ cần điều chỉnh độ rộng của các cột và khoảng cách giữa chúng bằng cách sử dụng các phương thức
setColumnWidth()
, setVerticalSpacing()
và setHorizontalSpacing()
. Lưu ý rằng những phương thức này sử dụng pixel làm đơn vị của chúng.
1
2
3
| cheeseGrid.setColumnWidth( 60 ); cheeseGrid.setVerticalSpacing( 20 ); cheeseGrid.setHorizontalSpacing( 20 ); |
Bạn bây giờ có thể buộc
GridView
vào adapter mà chúng ta đã tạo ra trước đó bằng cách sử dụng phương thức setAdapter()
.
1
| cheeseGrid.setAdapter(cheeseAdapter); |
Chạy lại ứng dụng của bạn một lần nữa để xem
GridView
trông như thế nào.6. Thêm Event Listener
Chúng ta có thể lắng nghe các sự kiện chạm và chạm lâu vào các phần tử bên trong một Adapter View. Ví dụ, hãy thêm một listener sự kiện bấm vào
GridView
.
Tạo ra một đối tượng mới của một lớp nặc danh cài đặt giao diện
AdapterView.OnItemClickListener
và truyền nó vào phương thức setOnItemClickListener()
của đối tượng GridView
. Android Studio tự động tạo ra ngẫu nhiên phương thức onItemClick()
của giao diện. Bạn sẽ thấy rằng các tham số của phương thức bao gồm một số nguyên xác định vị trí của phần tử. Bạn có thể sử dụng số nguyên này để tìm thấy phần tử trong bộ dữ liệu mà người dùng đã nhấp vào.
Các code sau đây minh họa cách làm thế nào để hiển thị một thông báo đơn giản mỗi khi một phần tử trong
GridView
được nhấp.
01
02
03
04
05
06
07
08
09
10
11
12
13
| cheeseGrid.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int position, long rowId) { // Generate a message based on the position String message = "You clicked on " + cheeses[position]; // Use the message to create a Snackbar Snackbar.make(adapterView, message, Snackbar.LENGTH_LONG) .show(); // Show the Snackbar } }); |
Nếu bạn chạy ứng dụng và nhấp vào bất kỳ phần tử nào trong Grid, một thông báo sẽ xuất hiện ở dưới cùng của màn hình. Lưu ý rằng bạn có thể sử dụng code tương tự để lắng nghe các sự kiện nhấp lên các phần tử bên trong một
ListView
.
7. Thừa kế ArrayAdapter
Một
ArrayAdapter
chỉ có thể xử lý một TextView
bên trong layout của các đối tượng View
mà nó tạo ra. Để mở rộng khả năng của nó, bạn phải thừa kế nó. Tuy nhiên, trước khi chúng ta làm điều đó, chúng ta hãy tạo ra một tập hợp dữ liệu phức tạp hơn một chút.
Thay vì chuỗi, giả sử rằng tập hợp dữ liệu của chúng ta có chứa các đối tượng của lớp sau đây:
1
2
3
4
5
6
7
8
9
| static class Cheese { String name; String description; public Cheese(String name, String description) { this .name = name; this .description = description; } } |
Đây là tập hợp dữ liệu mà chúng ta sẽ sử dụng:
1
2
3
4
5
6
7
| Cheese[] cheeses = { new Cheese( "Parmesan" , "Hard, granular cheese" ), new Cheese( "Ricotta" , "Italian whey cheese" ), new Cheese( "Fontina" , "Italian cow's milk cheese" ), new Cheese( "Mozzarella" , "Southern Italian buffalo milk cheese" ), new Cheese( "Cheddar" , "Firm, cow's milk cheese" ), }; |
Như bạn thấy, lớp
Cheese
có chứa hai trường, name
và description
. Để hiển thị cả hai trường trong một List hoặc Grid, thì layout của các phần tử phải chứa hai TextView
.
Tạo một tập tin layout XML mới và đặt tên là custom_item.xml. Thêm Large text và một Small text vào nó. Thiết lập thuộc tính
id
của cái đầu tiên thành cheese_name và của cái thứ hai là cheese_description. Nội dung của tập tin layout XML bây giờ sẽ giống như thế này:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
android:orientation = "vertical" android:layout_width = "match_parent" android:layout_height = "match_parent" android:padding = "@dimen/activity_horizontal_margin" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textAppearance = "?android:attr/textAppearanceLarge" android:text = "Large Text" android:id = "@+id/cheese_name" /> < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textAppearance = "?android:attr/textAppearanceSmall" android:text = "Small Text" android:id = "@+id/cheese_description" /> </ LinearLayout > |
ArrayAdapter
cũng phải có khả năng xử lý hai TextView
. Xem lại Activity của bạn, tạo ra một lớp nặc danh mới thừa kế lớp ArrayAdapter
và override phương thức getView()
của nó. Hãy chắc chắn rằng bạn truyền mảng như là đối số vào hàm xây dựng của nó.
1
2
3
4
5
6
7
8
9
| ArrayAdapter new ArrayAdapter this , 0 , cheeses) { @Override public View getView( int position, View convertView, ViewGroup parent) { } }; |
Bên trong phương thức
getView()
, bạn phải sử dụng tham số position
như là một chỉ số của mảng và lấy phần tử ở chỉ số đó.
1
| Cheese currentCheese = cheeses[position]; |
Tham số thứ hai của phương thức
getView()
là những gì cho phép chúng ta tái sử dụng các đối tượng View
. Nếu bạn bỏ qua nó, hiệu suất của các adapter view của bạn sẽ thấp. Khi phương thức getView()
được gọi lần đầu tiên, thì convertView
là null
. Bạn phải khởi tạo nó bằng cách inflate tập tin tài nguyên xác định layout của các phần tử. Để làm như vậy, hãy lấy một tham chiếu đến một LayoutInflater
bằng cách sử dụng phương thức getLayoutInflater()
và gọi phương thức inflate()
của nó.
1
2
3
4
5
| // Inflate only once if (convertView == null ) { convertView = getLayoutInflater() .inflate(R.layout.custom_item, null , false ); } |
Tại thời điểm này, bạn có thể sử dụng
findViewById()
để có được một tham chiếu đến các TextView
bên trong layout và gọi phương thức setText()
để khởi tạo chúng bằng cách sử dụng dữ liệu từ mảng.
1
2
3
4
5
6
7
| TextView cheeseName = (TextView)convertView.findViewById(R.id.cheese_name); TextView cheeseDescription = (TextView)convertView.findViewById(R.id.cheese_description); cheeseName.setText(currentCheese.name); cheeseDescription.setText(currentCheese.description); |
Cuối cùng, trả về
convertView
vì vậy mà nó có thể được sử dụng để đưa bất kỳ adapter view kết hợp với adapter.
1
| return convertView; |
8. Sử dụng một View Holder
Phương thức
getView()
được gọi liên tục bởi Adapter View để phân phối chính nó. Vì vậy, bạn phải cố gắng giảm thiểu số lượng các hoạt động mà bạn thực hiện bên trong nó.
Ở bước trước, bạn có thể thấy rằng, mặc dù chúng ta đã chắc chắn rằng layout của các phần tử trong danh sách inflate chỉ một lần, nhưng phương thức
findViewById()
, chiếm nhiều CPU, được gọi mỗi khi phương thức getView()
được gọi.
Để tránh điều này và cải thiện hiệu suất của Adapter View, chúng ta cần lưu trữ các kết quả của phương thức
findViewById()
bên trong đối tượng convertView
. Để làm như vậy, chúng ta có thể sử dụng một đối tượng View Holder, nó không có gì khác hơn là một đối tượng của một lớp có thể lưu trữ các thành phần hiện diện trong layout.
Bởi vì layout có hai
TextView
, nên View Holder cũng phải có hai TextView
. Tôi đã đặt tên lớp là ViewHolder.
1
2
3
4
| static class ViewHolder{ TextView cheeseName; TextView cheeseDescription; } |
Trong phương thức
getView()
, sau khi bạn inflate layout, bây giờ bạn có thể khởi tạo đối tượng View Holder bằng cách sử dụng phương thức findViewById()
.
1
2
3
4
5
| ViewHolder viewHolder = new ViewHolder(); viewHolder.cheeseName = (TextView)convertView.findViewById(R.id.cheese_name); viewHolder.cheeseDescription = (TextView)convertView.findViewById(R.id.cheese_description); |
Để lưu trữ đối tượng View Holder trong
convertView
, sử dụng phương thức setTag()
của nó.
1
2
| // Store results of findViewById convertView.setTag(viewHolder); |
Và bây giờ, mỗi khi
getView()
được gọi, bạn có thể lấy đối tượng View Holder từ convertView
bằng cách sử dụng phương thức getTag()
và cập nhật các TextView
bên trong nó bằng cách sử dụng phương thức setText()
của chúng.
1
2
3
4
5
6
7
| TextView cheeseName = ((ViewHolder)convertView.getTag()).cheeseName; TextView cheeseDescription = ((ViewHolder)convertView.getTag()).cheeseDescription; cheeseName.setText(currentCheese.name); cheeseDescription.setText(currentCheese.description); |
Nếu bạn chạy ứng dụng của bạn ngay bây giờ, bạn có thể thấy
GridView
hiển thị hai dòng văn bản trong mỗi ô.Tổng kết
Trong hướng dẫn này, bạn đã biết cách làm thế nào để tạo ra một Adapter và sử dụng nó để phân phối các Adapter View khác nhau. Bạn cũng học được cách tạo Adapter tuỳ biến riêng của bạn. Mặc dù chúng ta chỉ tập trung vào các lớp
ArrayAdapter
, ListView
và GridView
, nhưng bạn có thể sử dụng các kỹ thuật tương tự cho các Adapter và Adapter View mà Android SDK cung cấp.
Android Support Library có bao gồm lớp
RecyclerView
. Nó hoạt động rất giống một Adapter View, nhưng nó không phải là một lớp con của lớp AdapterView
. Bạn nên xem xét việc sử dụng nó nếu bạn muốn tạo các danh sách phức tạp hơn, đặc biệt là những cái mà sử dụng tập tin layout cho các phần của chúng. Để tìm hiểu thêm về nó, bạn có thể tham khảo hướng dẫn này trên Envato Tuts+.
Để tìm hiểu thêm về lớp
AdapterView
và các lớp con của nó, bạn có thể tham khảo tài liệu hướng dẫn của nó.
Link: https://code.tutsplus.com/vi/tutorials/android-from-scratch-understanding-adapters-and-adapter-views--cms-26646