header-object

Tạo Custom View trong lập trình Android - Phần 5

6. Dùng Custom-ListView làm cho ListView sinh động hơn

Trước tiên, chúng ta sẽ đi thực hiện tạo ra một ListView có sự hiển thị như sau:

vietnamtourism h3

Chúng ta trước tiên sẽ chú ý kĩ tới mỗi item (mục) trong ListView này và nhận thấy một vài điều như sau:

  • Nền của mỗi item (background) nhạt dần từ dưới lên trên (gradient) tạo cảm giác các item “nổi” lên.
  • Mỗi item bao gồm có 4 thành phần gồm: Hình đại diện, họ tên, năm sinh – năm mất và lĩnh vực thành danh của mỗi người.

Với nhận xét thứ nhất ở bên trên, Android cung cấp khả năng cho phép chúng ta dùng các tập tin .xml để khai báo ra các thành phần gọi là “drawable”. Ví dụ như một hình nền (backgroud), một đường viền (stroke)…

Để làm điều đó, tạo tập tin list_items_bg.xml trong thư mục res/drawable và nhập cho nó có nội dung như sau:

<?xml version="1.0" encoding="utf-8"?>
  <shape xmlns:android="http://schemas.android.com/apk/res/android" 
  android:shape="rectangle">
  	<gradient 
  		android:startColor="#f0f0f0"
  		android:endColor="#dddddd"
  		android:angle="-90"
  	/>
	</shape>

Với nhận xét thứ 2, dễ thấy ở đây mỗi item trong ListView không còn đơn thuần là một String nữa mà bao gồm nhiều thứ hơn thế. Chúng ta sẽ đi xây dựng 1 class cho khái niệm danh nhân (có nghĩa gồm 4 thuộc tính như liệt kê ở nhận xét thứ 2 trên).

Để thực hiện điều này, tạo lớp ListItem có code đơn giản như sau:

package com.danweb.vietnamtourism;
/**
 * lớp tương ứng với 1 item trong ListView
 */
public class ListItem {
	public String 	name;
	public int 		photo;
	public String 	life;
	public String 	career;
	
	public ListItem(String name, int photo, String life, String carrer){
		this.name 		= name;
		this.photo 		= photo;
		this.life 		= life;
		this.career 	= carrer;
	}
}
    

Bây giờ, chúng ta có thể dễ dàng tạo ra các danh nhân tương tự phần trước bằng đoạn mã như sau:

//tạo mảng động chứa các phần tử là ListItem
ArrayList celebrities = new ArrayList();
//thêm các phần tử vào mảng động celebrities
celebrities.add(new ListItem("Hồ Chí Minh", 	R.drawable.hcm, "1890 - 1969", "Revolutionary"));
celebrities.add(new ListItem("Trần Hưng Đạo", 	R.drawable.thd, "1232? - 1300", "Commander, Poet"));
celebrities.add(new ListItem("Ngô Bảo Châu", 	R.drawable.nbc, "1972 -", "Mathematician"));
celebrities.add(new ListItem("Nguyễn Huệ", 		R.drawable.nh, "1753 - 1792", "Emperor"));
celebrities.add(new ListItem("Võ Nguyên Giáp", 	R.drawable.vng, "1911 -", "Commander, Politician"));
celebrities.add(new ListItem("Hoàng Diệu", 		R.drawable.hd, "1828 - 1882", "Governor"));
celebrities.add(new ListItem("Đặng Thái Sơn", 	R.drawable.dts, "1958 -", "Pianist"));
celebrities.add(new ListItem("Nguyễn Trãi", 	R.drawable.nt, "1380 – 1442", "Poet, Politician"));
celebrities.add(new ListItem("Trịnh Công Sơn", 	R.drawable.tcs, "1939 – 2001", "Musician, Painter, Poet"));
celebrities.add(new ListItem("Nguyễn Du", 		R.drawable.nd, "1766 – 1820,", "Poet"));

Một chú ý là chúng ta cần phải có các hình ảnh tương ứng về các danh nhân trong thư mục drawable đã. Các hình ảnh này các bạn có thể tải tại đây (ok.zip)

Về mặt trình bày, rõ ràng chúng ta cần phải tạo một layout có bố cục gồm 4 thành phần như trong nhận xét 2. Chúng ta sẽ thực hiện điều này trong một tập tin layout xml như sau:

Tạo tập tin có tên list_item.xml trong thư mục res/layout và nhập cho nó nội dung như sau:

<?xml version="1.0"encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  	android:layout_width="match_parent"
  	android:layout_height="wrap_content"
  	android:background="@drawable/list_items_bg"
  	android:orientation="horizontal">
  
  <ImageView 
  	android:layout_width="0dp"
  	android:layout_weight="1"
  	android:padding="5dp"
  	android:layout_height="match_parent"
  	android:id="@+id/itemPhoto"
  	android:src="/@drawable/tcs"
  />
	<LinearLayout 
  	android:layout_width="0dp"
  	android:layout_weight="4"
  	android:layout_height="wrap_content"
  	android:padding="5dp"
  	android:orientation="vertical">
 <TextView 
  	android:id="@+id/itemName"
  	android:layout_width="match_parent"
  	android:layout_height="wrap_content"
  	android:text="Name"
  	android:textAppearance="?android:attr/textAppearanceLarge" />
  <TextView 
  	android:id="@+id/itemCarrer"
  	android:layout_width="match_parent"
  	android:layout_height="wrap_content"
  	android:text="Carrer"
  	android:textAppearance="?android:attr/textAppearanceSmall" />
  <TextView 
  	android:id="@+id/itemLife"
  	android:layout_width="match_parent"
  	android:layout_height="wrap_content"
  	android:text="Life"
  	android:textAppearance="?android:attr/textAppearanceSmall" />
  
  </LinearLayout>
</LinearLayout>

Chuyển qua chế độ design, nếu bạn làm đúng thì tập tin layout này cho ta sự hiển thị như sau:

vietnamtourism h4

Cho tới thời điểm này mọi việc vẫn khá đơn giản. Chúng sẽ phức tạp hơn một chút trong phần tiếp ngay sau đây.

Chúng ta đã có một tập tin layout cho mỗi thành phần trên ListView, thực tế chúng ta phải đi xây dựng lớp View tương ứng với mỗi thành phần trên ListView. Để làm điều này hãy tạo một lớp mới có tên VietnameseView kế thừ từ lớp LinearLayout như sau:

vietnamtourism h5

Code cho lớp này được viết như sau:

package com.danweb.vietnamtourism;
import android.app.Service;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
 * lớp tương ứng với 1 item trên Custom-ListView 
 */
public class VietnameseView extends LinearLayout {
	//các thuộc tính tương ứng với các View Widget 
	//trong tập tin list_item.xml
	public ImageView 	photo;
	public TextView 	name;
	public TextView 	life;
	public TextView 	carrer;
	public VietnameseView(Context context) {
		super(context);
		
		//đọc tập tin list_item.xml để lấy các thành phần
		LayoutInflater linflater = (LayoutInflater) ((VietnameseActivity)context).getSystemService(Service.LAYOUT_INFLATER_SERVICE);
		linflater.inflate(R.layout.list_item, this);
		
		//lấy các thành  phần tương ứng
		this.photo 	= (ImageView) findViewById(R.id.itemPhoto);
		this.name 	= (TextView) findViewById(R.id.itemName);
		this.life 	= (TextView) findViewById(R.id.itemLife);
		this.carrer = (TextView) findViewById(R.id.itemCarrer);
	}
	
	/**
	 * phương thức đặt data vào trong phần tử VietnameseView
	 * @param item
	 */
	public void setListItem(ListItem item){
		this.photo.setImageResource(item.photo);
		this.name.setText(item.name);
		this.life.setText(item.life);
		this.carrer.setText(item.career);
	}
}

Các bạn có thể dọc các dòng ghi chú để nắm rõ nội dung thực hiện của mỗi dòng lệnh. Nếu có thắc mắc gì rất hoan nghênh các bạn đặt câu hỏi thắc mắc trên Fanpage của Dân Web.

Như chúng ta đã làm trong phần trên, để đặt một ArrayList<String> lên trang thì chúng ta đã sử dụng một ArrayAdapter<String>. Tương tự, để đặt một ArrayList<ListItem> lên trang thì chúng ta cần có một ArrayAdapter<ListItem>, đây là một lớp chưa có sẵn và chúng ta phải đi tạo ra nó. Lớp này như ta đã biết có nhiệm vụ là đặt các phần tử trong mảng lên trên ListView.

Tạo lớp VietnameseAdapter kế thừa từ lớp có sẵn ArrayAdapter như sau

vietnamtourism h6

Chúng ta sẽ viết mã cho nó ngay sau đây:

package com.danweb.vietnamtourism;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
public class VietnameseAdapter extends ArrayAdapter {
	
	private List 	_listItems;
	private Context 		_context;
	public VietnameseAdapter(Context context, int textViewResourceId,List objects) {
		super(context, textViewResourceId, objects);
		this._context 	= context;
		this._listItems = objects;
	}
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		VietnameseView view = new VietnameseView(this._context);
		view.setListItem(this._listItems.get(position));
		return view;
	}
}

Ở lớp VietnameseAdapter này chúng ta cần chú ý nhất là phương thức getView(), đây là phương thức được Android gọi thực thi để hiển thị item lên ListView khi duyệt qua mảng chứa dữ liệu cần hiển thị (trong trường hợp này là this._listItems).

Tham số đầu tiên position chính là vị trí của phần tử dữ liệu hiện thời. Như vậy, mỗi lần Android gọi phương thức getView() chúng ta có thể dựa vào tham số position mà nó truyền vào để biết cần phải hiển thị nội dung tương ứng gì.

Đến đây thì các thành phần phải “Custom” đã được tạo ra. Ôn lại một chút để chúng ta có cái nhìn tổng quát. Như vậy từ đầu tới giờ chúng ta đã đi tạo ra các lớp:

  • ListItem: lớp đối tượng mà mỗi đối tượng chứa dữ liệu của một item cần hiển thị (giống String trong trường hợp ListView thông thường).
  • VietnameseView: lớp đối tượng xử lý đọc tập tin list_item.xml để trình bày giao diện cho mỗi item trong ListView.
  • VietnameseAdapter: lớp đối tượng kết nối giữa ListView với mảng chứa các Listitem (giống lớp ArrayAdapter trong trường hợp ListView thông thường).

Bây giờ, quay lại tập tin VietnameseActivity, sửa lại mã nguồn như sau:

//tạo một đối tượng adapter mới tạo
VietnameseAdapter newAdapter = new VietnameseAdapter(this, listViewVietnamese.getId(), celebrities);
//sử dụng newAdapter cho listViewVietnamese
listViewVietnamese.setAdapter(newAdapter);

Như vậy, toàn bộ mã của lớp này tính đến thời điểm này như sau:

package com.danweb.vietnamtourism;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class VietnameseActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		/*tập tin res/layout/activity_vietnamese.xml làm 
		layout hiển thị cho activity này*/
		setContentView(R.layout.activity_vietnamese);
		
		//tạo mảng động chứa các phần tử là ListItem
		ArrayList celebrities = new ArrayList();
		
		//thêm các phần tử vào mảng động celebrities
		celebrities.add(new ListItem("Hồ Chí Minh", 	R.drawable.hcm, "1890 - 1969", "Revolutionary"));
		celebrities.add(new ListItem("Trần Hưng Đạo", 	R.drawable.thd, "1232? - 1300", "Commander, Poet"));
		celebrities.add(new ListItem("Ngô Bảo Châu", 	R.drawable.nbc, "1972 -", "Mathematician"));
		celebrities.add(new ListItem("Nguyễn Huệ", 		R.drawable.nh, "1753 - 1792", "Emperor"));
		celebrities.add(new ListItem("Võ Nguyên Giáp", 	R.drawable.vng, "1911 -", "Commander, Politician"));
		celebrities.add(new ListItem("Hoàng Diệu", 		R.drawable.hd, "1828 - 1882", "Governor"));
		celebrities.add(new ListItem("Đặng Thái Sơn", 	R.drawable.dts, "1958 -", "Pianist"));
		celebrities.add(new ListItem("Nguyễn Trãi", 	R.drawable.nt, "1380 – 1442", "Poet, Politician"));
		celebrities.add(new ListItem("Trịnh Công Sơn", 	R.drawable.tcs, "1939 – 2001", "Musician, Painter, Poet"));
		celebrities.add(new ListItem("Nguyễn Du", 		R.drawable.nd, "1766 – 1820,", "Poet"));
		
		//lấy về ListView đã đặt trên activity_vietnamese.xml
		ListView listViewVietnamese = (ListView) findViewById(R.id.listViewVietnamese);
		
		//tạo một đối tượng adapter mới tạo
		VietnameseAdapter newAdapter = new VietnameseAdapter(this, listViewVietnamese.getId(), celebrities);
		
		//sử dụng newAdapter cho listViewVietnamese
		listViewVietnamese.setAdapter(newAdapter);
	}
}

Bấm F11 để kiểm tra kết quả trên Emulator, bấm nút Vietnamese trên màn hình đầu tiên chúng ta thu được màn hình thứ 2 chứa Custom-ListView mới tạo như sau:

vietnamtourism h7

7. Kết luận và tải Source-code

Như vậy chúng ta đã đi qua một chủ đề rất thú vị và cần thiết là Custom-ListView. Tuy nội dung hơi dài một chút nhưng hi vọng các bạn nắm rõ các kiến thức cần thiết để áp dụng vào các ứng dụng cụ thể của mình.

Các bạn có thể tải ứng dụng hoàn chỉnh tại đây (VietnamTourism1.zip)

Đào Ngọc Giang

Bình luận  

 
0 #13 minh nhật thứ 7-06-15 16:31
bài hương 1dẫn hay thì ko nói rồi ,mà sao trong code 1 đường trên web này một nẽo vậy , có chỗ sai chẳng biết sai cái gì té ra sai cái viết hoa với viết thường .
Trích dẫn
 
 
0 #12 trong vinh Thứ 3-05-15 15:31
có cách nào mà lấy được ảnh với những thông tin đó thông qua đường dẫn thay vi nếu làm như thế này có ít nhân vất nếu cố nhiều nhân vật thì sao.ứng dụng sẽ rất nặng.
Trích dẫn
 
 
0 #11 Khánh thứ 7-03-15 10:17
Mấy bài này hay quá, bác chia sẻ nhiều thêm cho a e học hỏi đi, bác có bài bào hướng dẫn làm 1 ứng dụng hoàn chỉnh ko ạ, post cho a e học hỏi với
Trích dẫn
 
 
0 #10 Minh Anh 0909 thứ 7-11-14 10:50
Cảm ơn anh Đào Ngọc Giang, chuỗi bài hay và cần thiết!
Trích dẫn
 
 
0 #9 chau nguyen Thứ 6-11-14 18:25
sao soure code khong tai duoc vay ban?
Trích dẫn
 
 
+2 #8 Jus Jus Thứ 5-07-14 16:44
Trích dẫn Jus Jus:
@Đào Ngọc Giang: getView() được gọi đi gọi lại trong suốt quá trình listview được chạy. Chứ không phải là gọi bằng đúng số lượng phần tử trong listView đâu. Bạn có thể test bằng cách thêm câu lệnh Log.d() vào trong hàm getView và xem nó chạy bao nhiêu lần.

Nếu viết như bạn thì nếu listView có 1000 item, thì nó sẽ phải tạo ra 1000 item_view (và thực tế là sẽ nhiều hơn nữa), và như vậy thì ứng dụng của chúng ta sẽ ko đủ bộ nhớ và bị scrash ngay.
Vì vậy listView nó có cơ chế sử dụng lại các item_view đã được tạo ra (chính là tham số convertView trong hàm getView), nếu hiện tại màn hình chỉ hiển thị số item_view là 7, thì số view thực sự được tạo ra chỉ có khoảng 7 hoặc 8 view thôi (nếu hiểu được cách thức làm việc của listview và code đúng)
Đây là bài hướng dẫn giải thích về cách thức hoạt động của listView
http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
Trích dẫn
 
 
0 #7 Jus Jus Thứ 5-07-14 16:35
@Đào Ngọc Giang: getView() được gọi đi gọi lại trong suốt quá trình listview được chạy. Chứ không phải là gọi bằng đúng số lượng phần tử trong listView đâu. Bạn có thể test bằng cách thêm câu lệnh Log.d() vào trong hàm getView và xem nó chạy bao nhiêu lần.
Trích dẫn
 
 
0 #6 Con co bebe Thứ 5-01-14 15:23
bạn ơi cho mình hỏi, có Source-code xem video trong androi ko?
Trích dẫn
 
 
0 #5 Sand Thứ 5-10-13 19:19
Tiếc em chẳng biết gì về lập trình Android cả :sad: :sad: :sad:
Trích dẫn
 
 
+1 #4 Đào Ngọc Giang Thứ 6-07-13 21:37
@HaiHack việc xây dựng tập tin list_item.xml để chúng ta dễ dàng trong sử dụng. Có thể dùng được ngay các thuộc tính như layout_margin, textSize... Nếu không dùng xml thì bạn có thể tự tạo ra các phần tử bằng cách dùng các lớp tương ứng như LinearLayout, TextView, ImageView...
Trích dẫn
 

Thêm ý kiến


Security code
Làm mới


2

Facebook

Thống kê truy cập

Hiện có 760 khách đang truy cập
2491921