Friday, March 25, 2016

Bài 16. Thực hành về Custom Layout cho ListView trong Android [Căn bản]

bài 15 bạn đã được thực hành với ListView control. Trong bài tập này bạn sẽ học cách Custom lại layout cho ListView trong ứng dụng Android của bạn. Tôi nghĩ bài tập này nó rất quan trọng và thực tế bởi vì trong các ứng dụng Android có liên quan tới ListView thì đa phần chúng ta phải custom lại cho đúng với yêu cầu của khách hàng.
Tôi có làm một ví dụ về quản lý nhân viên với giao diện bên dưới đây:

14_custom_0 

– Bạn quan sát là phần danh sách nhân viên bên dưới là Custom Layout.
– Mỗi dòng trong ListView sẽ có 3 đối tượng: ImageView, TextViewCheckbox.
– Khi nhập nhân viên nếu người sử dụng chọn Nữ thì sẽ hiển thị hình là con gái, nếu chọn Nam thì hiển thị hình là con trai (bạn nhìn danh sách hình nhỏ nhỏ 16×16 ở ListView).
– Mã và tên của nhân viên sẽ được hiển thị vào TextView
– Checkbox cho phép người sử dụng checked (nhằm đánh dấu những nhân viên muốn xóa, ở đây cho phép xóa nhiều nhân viên)
– Bạn để ý Tôi có thêm 1 ImageButton có hình màu Chéo đỏ, nó dùng để xóa tất cả các nhân viên được Checked trong ListView, sau khi xóa thành công thì phải cập nhật lại ListView.
– Để làm được điều trên thì ta sẽ kế thừa từ ArrayAdapteroverride phương thức getView, cụ thể:
– Bạn xem Cấu trúc chương trình quản lý nhân viên:

14_custom_1 

– Tôi tạo thêm thư mục drawable và kéo thả 3 icon mà Tôi sử dụng vào (bạn cũng tạo thư mục tên y xì vậy). Nhớ là tên hình phải viết liền và chữ thường đầu tiên.
– Trong thư mục layout: Tôi tạo thêm my_item_layout.xml dùng để Custom lại ListView, dưới đây là cấu trúc XML của nó:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="horizontal" >
 
<ImageView
 android:id="@+id/imgitem"
 android:layout_width="22dip"
 android:layout_height="22dip"
 android:paddingLeft="2dp"
 android:paddingRight="2dp"
 android:paddingTop="2dp"
 android:layout_marginTop="4dp"
 android:contentDescription="here"
 android:src="@drawable/ic_launcher" />
 
<TextView
 android:id="@+id/txtitem"
 android:layout_height="wrap_content"
 android:layout_width="0dip"
 android:layout_weight="2"
 android:layout_marginTop="4dp"
 android:paddingLeft="2dp"
 android:paddingRight="2dp"
 android:paddingTop="2dp"
 android:textSize="15sp" />
 
<CheckBox
 android:id="@+id/chkitem"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />
 
</LinearLayout>
– Ta sẽ dựa vào các id trong này để xử lý trong hàm getView của class mà ta kế thừa từ ArrayAdapter (các id trên là imgitem đại diện cho hình là Nữ hay Nam, txtitem dùng để hiển thị mã và tên nhân viên, chkitem dùng để xử lý Checked)
– Bạn xem activity_main.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >
 
<TextView
 android:id="@+id/textView2"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:gravity="center"
 android:text="Quản lý nhân viên"
 android:textColor="#FFFFFF"
 android:textSize="20sp" />
 
<TableLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:stretchColumns="*"
 >
 
<TableRow
 android:id="@+id/tableRow1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView3"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Mã NV:" />
 
<EditText
 android:id="@+id/editMa"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:ems="10" >
 
<requestFocus />
 </EditText>
 
</TableRow>
 
<TableRow
 android:id="@+id/tableRow2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Tên NV:" />
 
<EditText
 android:id="@+id/editTen"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:ems="10" />
 
</TableRow>
 
<TableRow
 android:id="@+id/tableRow3"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView5"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Giới tính:" />
 
<RadioGroup
 android:id="@+id/radioGroup1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:orientation="horizontal" >
 
<RadioButton
 android:id="@+id/radNu"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:checked="true"
 android:text="Nữ" />
 
<RadioButton
 android:id="@+id/radNam"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Nam" />
 </RadioGroup>
 
</TableRow>
 
<TableRow
 android:id="@+id/tableRow4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<Button
 android:id="@+id/btnNhap"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_column="1"
 android:text="Nhập NV" />
 
</TableRow>
 </TableLayout>
 
<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="9"
 android:background="#008000"
 android:layout_marginTop="2dp"
 android:text="Danh sách nhân viên:"
 android:textColor="#FFFFFF"
 android:textSize="20sp" />
 
<ImageButton
 android:id="@+id/btndelete"
 android:layout_width="30dip"
 android:layout_height="30dip"
 android:src="@drawable/deleteicon" />
 
</LinearLayout>
 
<ListView
 android:id="@+id/lvnhanvien"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </ListView>
 
</LinearLayout>
– Layout main này chính là giao diện chính của ứng dụng.
– Dưới đây là các class hỗ trợ xử lý nghiệp vụ:

14_custom_2 

– Class Employee dùng để lưu trữ thông tin nhân viên: Mã nhân viên, tên nhân viên, giới tính
– Class MyArrayAdapter kế thừa từ ArrayAdapter, mục đích của nó là giúp chúng ta Custom lại layout cho ListView.
– Cuối cùng MainActivity.
– Bây giờ ta vào chi tiết từng class:

1) class Employee:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package tranduythanh.com;
 
public class Employee {
 private String id;
 private String name;
 private boolean gender;
 public String getId() {
 return id;
 }
 public void setId(String id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public boolean isGender() {
 return gender;
 }
 public void setGender(boolean gender) {
 this.gender = gender;
 }
 @Override
 public String toString() {
 return this.id+"-"+this.name;
 }
}
2) class MyArrayAdapter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package tranduythanh.com;
 
import java.util.ArrayList;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
 
public class MyArrayAdapter extends
 ArrayAdapter<Employee>
{
 Activity context=null;
 ArrayList<Employee>myArray=null;
 int layoutId;
 /**
 * Constructor này dùng để khởi tạo các giá trị
 * từ MainActivity truyền vào
 * @param context : là Activity từ Main
 * @param layoutId: Là layout custom do ta tạo (my_item_layout.xml)
 * @param arr : Danh sách nhân viên truyền từ Main
 */
 public MyArrayAdapter(Activity context,
 int layoutId,
 ArrayList<Employee>arr){
 super(context, layoutId, arr);
 this.context=context;
 this.layoutId=layoutId;
 this.myArray=arr;
 }
 /**
 * hàm dùng để custom layout, ta phải override lại hàm này
 * từ MainActivity truyền vào
 * @param position : là vị trí của phần tử trong danh sách nhân viên
 * @param convertView: convertView, dùng nó để xử lý Item
 * @param parent : Danh sách nhân viên truyền từ Main
 * @return View: trả về chính convertView
 */
 public View getView(int position, View convertView,
 ViewGroup parent) {
 /**
 * bạn chú ý là ở đây Tôi không làm:
 * if(convertView==null)
 * {
 * LayoutInflater inflater=
 * context.getLayoutInflater();
 * convertView=inflater.inflate(layoutId, null);
 * }
 * Lý do là ta phải xử lý xóa phần tử Checked, nếu dùng If thì
 * nó lại checked cho các phần tử khác sau khi xóa vì convertView
 * lưu lại trạng thái trước đó
 */
 LayoutInflater inflater=
 context.getLayoutInflater();
 convertView=inflater.inflate(layoutId, null);
 //chỉ là test thôi, bạn có thể bỏ If đi
 if(myArray.size()>0 && position>=0)
 {
 //dòng lệnh lấy TextView ra để hiển thị Mã và tên lên
 final TextView txtdisplay=(TextView)
 convertView.findViewById(R.id.txtitem);
 //lấy ra nhân viên thứ position
 final Employee emp=myArray.get(position);
 //đưa thông tin lên TextView
 //emp.toString() sẽ trả về Id và Name
 txtdisplay.setText(emp.toString());
 //lấy ImageView ra để thiết lập hình ảnh cho đúng
 final ImageView imgitem=(ImageView)
 convertView.findViewById(R.id.imgitem);
 //nếu là Nữ thì lấy hình con gái
 if(emp.isGender())
 imgitem.setImageResource(R.drawable.girlicon);
 else//nếu là Nam thì lấy hình con trai
 imgitem.setImageResource(R.drawable.boyicon );
 }
 //Vì View là Object là dạng tham chiếu đối tượng, nên
 //mọi sự thay đổi của các object bên trong convertView
 //thì nó cũng biết sự thay đổi đó
 return convertView;//trả về View này, tức là trả luôn
 //về các thông số mới mà ta vừa thay đổi
 }
}
– Đây là class quan trọng nhất, mới nhất; dùng  để custom layout.
3) class MainActivity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package tranduythanh.com;
 
import java.util.ArrayList;
 
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RadioGroup;
 
public class MainActivity extends Activity {
 
ArrayList<Employee>arrEmployee=new ArrayList<Employee>();
 //Sử dụng MyArrayAdapter thay thì ArrayAdapter
 MyArrayAdapter adapter=null;
 ListView lvNhanvien=null;
 
 Button btnNhap;
 ImageButton btnRemoveAll;
 EditText editMa,editTen;
 RadioGroup genderGroup;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 btnNhap=(Button) findViewById(R.id.btnNhap);
 btnRemoveAll=(ImageButton) findViewById(R.id.btndelete);
 editMa=(EditText) findViewById(R.id.editMa);
 editTen=(EditText) findViewById(R.id.editTen);
 genderGroup=(RadioGroup) findViewById(R.id.radioGroup1);
 
 lvNhanvien=(ListView) findViewById(R.id.lvnhanvien);
 arrEmployee=new ArrayList<Employee>();
 //Khởi tạo đối tượng adapter và gán Data source
 adapter=new MyArrayAdapter(
 this,
 R.layout.my_item_layout,// lấy custom layout
 arrEmployee/*thiết lập data source*/);
 lvNhanvien.setAdapter(adapter);//gán Adapter vào Lisview
 
 btnNhap.setOnClickListener(new OnClickListener() {
 
 @Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 xulyNhap();
 }
 });
 btnRemoveAll.setOnClickListener(new OnClickListener() {
 
 @Override
 public void onClick(View arg0) {
 xulyXoa();
 }
 });
 }
 //gọi hàm xử lý nhập thông tin nhân viên
 public void xulyNhap()
 {
 String ma=editMa.getText()+"";
 String ten=editTen.getText()+"";
 boolean gioitinh=false;//Nam =false
 if(genderGroup.getCheckedRadioButtonId()==R.id.radNu)
 gioitinh=true;
 //Tạo một employee
 Employee emp=new Employee();
 emp.setId(ma);
 emp.setName(ten);
 emp.setGender(gioitinh);
 //Đưa vào danh sách
 arrEmployee.add(emp);
 //gọi hàm cập nhật giao diện
 adapter.notifyDataSetChanged();
 //Sau khi update thì xóa trắng dữ liệu và cho editma focus
 editMa.setText("");
 editTen.setText("");
 editMa.requestFocus();
 }
 //hàm xử lý xóa
 public void xulyXoa()
 {
 //ta nên đi ngược danh sách, kiểm tra phần tử nào checked
 //thì xóa đúng vị trí đó ra khỏi arrEmployee
 for(int i=lvNhanvien.getChildCount()-1;i>=0;i--)
 {
 //lấy ra dòng thứ i trong ListView
 //Dòng thứ i sẽ có 3 phần tử: ImageView, TextView, Checkbox
 View v=lvNhanvien.getChildAt(i);
 //Ta chỉ lấy CheckBox ra kiểm tra
 CheckBox chk=(CheckBox) v.findViewById(R.id.chkitem);
 //Nếu nó Checked thì xóa ra khỏi arrEmployee
 if(chk.isChecked())
 {
 //xóa phần tử thứ i ra khỏi danh sách
 arrEmployee.remove(i);
 }
 }
 //Sau khi xóa xong thì gọi update giao diện
 adapter.notifyDataSetChanged();
 }
}
– Bây giờ bạn thực hiện chương trình và nhập một số nhân viên, rồi checked rồi nhấn xóa:
14_custom_3 

– Bạn hãy tìm hiểu thêm trên mạng về cách xử lý các phần tử khi custom, ở đây Tôi chưa xử lý xong: Ví dụ bạn chưa thể chọn được vào từng phần tử trong ListView (cho dù bạn có nghiến răng ngoáy mạnh ngón tay vào thì nó cũng không lung lay, Nếu bạn bỏ CheckBox đi thì lại được)… Nên bạn tìm hiểu thêm phần xử lý này (how to selected item in custom ListView).
– Bạn có thể tải toàn bộ coding mẫu ở đây:http://adf.ly/1Yl0PC
– Bạn nên làm tốt bài này vì nó rất quan trọng và hay.
– Trong các bài tập tiếp theo bạn sẽ được thực hành về Spinner, GridView, kết hợp Spinner với ListView, kết hợp Spinner với GridView.
– Chúc các bạn thành công

No comments:

Post a Comment