Học lập trình Android - Tạo Custom ListView
Với những bài học trước của khóa học lập trình android cơ bản, chúng ta đã bước đầu làm quen đc với control ListView .
Trong những ví dụ cơ bản thì ta có thể sử dụng trực tiếp ListView do android cung cấp. Nhưng trên thực tế, khi chúng ta muốn tạo ListView theo yêu cầu thì việc sử dụng ListView của android lại không hiệu quả. Do đó, hôm nay chúng ta sẽ học cách Custom ListView theo ý muốn của mình.
Hãy cùng xét một ví dụ để thấy rõ được sự khác biệt khi chúng ta sử dụng ListView có sẵn và Custom ListView.
Giới thiệu du lịch việt nam (hiện thị danh sách các danh nhân). Giao diện chính của chương trình (thiết kế giao diện và xử lý button chúng tôi sẽ đề cập ở một bài khác):
Khi bạn nhân vào nút Vietnamese trên giao diện sẽ hiện ra danh sách như sau:
Với danh sách hiện thị này chúng ta sẽ so sánh kết quả đạt được khi ta sử dụng hai trường hợp ListView trên.
1. Sử dụng ListView có sẵn tạo giao diện cơ bản.
a. ListView với các mục không đổi
Hãy mở tập tin res/values/strings.xml và chỉnh sửa nó để chúng ta có được nội dung như sau:
Mã: <?xml version=”1.0″ encoding=”utf-8″?>
<resources>
<string name=”app_name”>VietnamTourism</string>
<string name=”menu_settings”>Settings</string>
<!– mảng các danh nhân Việt Nam –>
<string-array name=”vietnamese”>
<item >Hồ Chí Minh</item> <item >Trần Hưng Đạo</item>
<item >Ngô Bảo Châu</item> <item >Nguyễn Huệ</item>
<item >Võ Nguyên Giáp</item> <item >Hoàng Diệu</item>
<item >Đặng Thái Sơn</item> <item >Nguyễn Trãi</item>
<item >Trịnh Công Sơn</item> <item >Nguyễn Du</item>
</string-array>
</resources>
Chúng ta có một mảng chưa tên của 10 danh nhân, mảng này có tên là là Vietnamese. Chúng ta sẽ tạo giao diện cho màn hình chứa danh sách các danh nhân như hình 1.
Trong thư mục res/laout tạo tập tin activity_vietnamese.xml và nhập vào nội dung cho nó như sau:
Mã: <?xml version=”1.0″ encoding=”utf-8″?>
<ListView
xmlns:android=”http://schemas.android.com/apk/res/android”
android:id=”@+id/listViewVietnamese”
android:layout_width=”match_parent”
android:layout_height=”wrap_content” >
</ListView>
Mở tập tin này ở chế độ design (Graphical Layout) chúng ta sẽ thấy hiển thị dạng như sau:
Vẫn trong tập tin res/layout/activity_vietnamese.xml. Để chỉ định ListView này hiển thị mảng vietnamese (đã khai báo trong tập tin res/values/strings.xml ngay trên) chúng ta sử dụng thuộc tính entries của thẻ ListView như sau:
Mã: <?xml version=”1.0″ encoding=”utf-8″?>
<ListView
xmlns:android=”http://schemas.android.com/apk/res/android”
android:id=”@+id/listViewVietnamese”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:entries=”@array/vietnamese”>
</ListView>
Mã: <?xml version=”1.0″ encoding=”utf-8″?>
<ListView
xmlns:android=”http://schemas.android.com/apk/res/android”
android:id=”@+id/listViewVietnamese”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:entries=”@array/vietnamese”>
</ListView>
b. Tạo mới một Activity để mở listview đã tạo sắn và mở nó thông qua Intent
Trong lập trình Android, mỗi màn hình được sử lý bởi một thành phần gọi là Activity. Ở khía cạnh lập trình, để tạo ra một Activity chúng ta tạo ra một lớp kế thừa từ lớp Activity có sẵn trong package android.app.
Trong project VietnamTourism, tạo mới một class có tên VietnameseActivity kế thừa từ lớp Activity:
Kế thừa phương thức onCreat() để có được mã nguồn:
package com.danweb.vietnamtourism;
import android.app.Activity; import android.os.Bundle;
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);
}}
Chúng ta phải khái báo tất cả các Activity trong cùng 1 project. Mở tập tin AndroidManifest.xml ở chế độ code, khai báo VietnameseActivity bằng thẻ <activity> nằm trong thẻ <application> như sau:
<activity android:name=”VietnameseActivity”></activity>
Để người dùng nhấn nút “Vietnammese ” trên giao diện của MainActivity sẽ thực hiện mở VietnameseActivity. Chúng ta thêm phương thức void_openVietnameseActivity() vào MainActivity:
private void _openVietnameseActivity(){/*để mở VietnameseActivity, chúng ta cần 1 đối tượng Intent gắn với lớp VietnameseActivity*/
Intent itVietnamese = new Intent(getApplicationContext(), VietnameseActivity.class);
//dùng phương thức startActivity() để thực hiện mở
startActivity(itVietnamese);
}
Intent itVietnamese = new Intent(getApplicationContext(), VietnameseActivity.class);
//dùng phương thức startActivity() để thực hiện mở
startActivity(itVietnamese);
}
Trở lại phương thức onCreat(), chúng ta sử sửa chút code khi người dùng bấm vào nút Vietnamese sẽ gọi phương thức_openVietnameseActivity() này.
Mã nguồn tạm thời của class ManiActivity:
package com.danweb.vietnamtourism;
import android.os.Bundle; import android.app.Activity;
import android.content.Intent;import android.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomButton vietnamese = (CustomButton) findViewById(R.id.iconVietnamese);
vietnamese.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//gọi phương thức mở VietnameseActivit_openVietnameseActivity();
}
}); }
private void _openVietnameseActivity(){
/*để mở VietnameseActivity, chúng ta cần 1 đối tượng Intent gắn với lớp VietnameseActivity*/
Intent itVietnamese = new Intent(getApplicationContext(), VietnameseActivity.class);
//dùng phương thức startActivity() để thực hiện mở
startActivity(itVietnamese);
}}
c. Dữ liệu động và ListView
Trên thực tế thì chúng ta phải thường xuyên tạo ra các ListView từ các nguồn sữ liệu động (như là truy vấn từ CSDL, qua Web-service…).
Với vid dụ này chúng ta sẽ tạo một mảng động chứa các phần từ, sử dụng đối tượng ArrayAdapter để đặt dữ liệu trong mảng này lên một ListView.
Mở tập tin VietnameseActivity.java và thêm vào các nội dung để có được 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ột mảng động chứa các phần tử là String
ArrayList<string> vietnamese = new ArrayList<string>();
//thêm các phần tử vào mảng động vietnamese
vietnamese.add(“Hồ Chí Minh”); vietnamese.add(“Trần Hưng Đạo”);
vietnamese.add(“Ngô Bảo Châu”); vietnamese.add(“Nguyễn Huệ”);
vietnamese.add(“Võ Nguyên Giáp”); vietnamese.add(“Hoàng Diệu”);
vietnamese.add(“Đặng Thái Sơn”); vietnamese.add(“Nguyễn Trãi”);
vietnamese.add(“Trịnh Công Sơn”); vietnamese.add(“Nguyễn Du”);
//tạo một ArrayAdapter giúp đặt dữ liệu vào ListView
Context ct= getApplicationContext();int itemView = android.R.id.text1;
int listLayout = android.R.layout.simple_list_item_1;
ArrayAdapter<string> vnAdapter = new ArrayAdapter<string>(ct, listLayout, itemView, vietnamese);
//lấy về ListView đã đặt trên activity_vietnamese.xml ListView listViewVietnamese = (ListView)findViewById(R.id.listViewVietnamese);
listViewVietnamese.setAdapter(vnAdapter); //sử dụng vnAdapter cho listViewVietnamese
}}</string></string></string></string>
Mở tập tin activity_vietnamese.xml và bỏ đi thuộc tính entries vì chúng ta không còn cần dùng đến mảng tĩnh trong tập tin strings.xml nữa: android:entries=”@array/vietnamese”
2. Tạo Custom – ListView sinh động hơn
Chúng ta sẽ tạo một giao diện sinh động hơn cho ListView ở trên, hiện thị ListView mới như hình dưới đây:
Ở ListView này, mối item có những điều khác biệt so với trước:
– Background của mỗi item 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.
Chúng ta có thể tạo được những hiệu ứng nhu ư trên với những khả năng Android cung cấ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” như: hình nền (background), đường viền (stroke), ….
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:
Mã: <?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>
ở mỗi Item trong ListView không đơn thuần chỉ là một String mà nnos bao gồm nhiều thứ hơn thế. Chúng ta sẽ xây dựng 1 class cho khái niệm danh nhân với 4 thuộc tính chúng ta đã liệt kê.
tạo lớp ListItem có code đơn giản như sau:
Mã: 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;
}}
Tạo danh sách các danh nhân:
Mã:
//tạo mảng động chứa các phần tử là ListItem
ArrayList<listitem> celebrities = new ArrayList<listitem>();
//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”));
</listitem></listitem>
Các bạn chú ý, nhớ add hình ảnh các danh nhân vào thư mục Drawable
Chúng ta sẽ phải trinh bày bố cục cho 1 item trong ListView có đầy đủ 4 phần như nhận xét, dó đó chúng ta phài tạo một layout có bố cục 4 phần như sau:
Mã:
<?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>
Nếu ở chế độ design mà cho sự hiện thi như hình bên dưới thì chúng ta đã thành công:
/*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;
}}
Tạo danh sách các danh nhân:
Mã:
//tạo mảng động chứa các phần tử là ListItem
ArrayList<listitem> celebrities = new ArrayList<listitem>();
//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”));
</listitem></listitem>
Các bạn chú ý, nhớ add hình ảnh các danh nhân vào thư mục Drawable
Chúng ta sẽ phải trinh bày bố cục cho 1 item trong ListView có đầy đủ 4 phần như nhận xét, dó đó chúng ta phài tạo một layout có bố cục 4 phần như sau:
Mã:
<?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>
Nếu ở chế độ design mà cho sự hiện thi như hình bên dưới thì chúng ta đã thành công:
Trên thực tế để hiển thị như hình, thì chúng ta cần xây dựng lớp View tương ứng co mỗi item trên ListView. Để làm được đ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:
Với lớp này, chúng ta viết code 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);
}}
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:
Chúng ta viết code như sau:
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<listitem> {
private List<listitem> _listItems; private Context _context;
public VietnameseAdapter(Context context, inttextViewResourceId,List<listitem> 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;}}
</listitem></listitem></listitem>
Ở 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 chúng ta đã tạo ra các thành phần Custom như:
- 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);
Toàn bộ mã code lớp VietnameseActivity:
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<listitem> celebrities = new ArrayList<listitem>();
//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);
}}
</listitem></listitem>
Như vậy chúng ta đã tìm hiểu khái quát xong về ListView và Custom ListView, tuy nội dụng còn khá đơn giản nhưng hy vọng cũng đã có thể hiểu được phần nào về khái niệm quan trọng này khi học lập trình Android.
Nguôn : danweb.vn
0 nhận xét: