Category Archives: 2. Xử lý giao diện người dùng

Bài tập 23: Thực hành về Context Menu trong Android


Tiếp nối bài 22, Bài tập này bạn sẽ học về Context Menu một trong những control hữu dụng mà Android cung cấp.

– Đối với C# , VB, hay  Java để hiển thị Context Menu : Sau khi thiết lập Context Menu ta chỉ cần bấm chuột phải vào đổi tượng thì sẽ có Menu Popup ra theo đúng yêu cầu.

– Đối với Android: Chúng ta cũng phải đăng ký Context Menu cho đối tượng sau đó muốn hiển thị lên thì ta nhấn thật lâu vào đối tượng (long click ).

– Tương tự như với Menu, Ta có thể tạo Context menu trong XML hoặc trong Coding.

– Cách tạo Context Menu bằng XML thì nó y xì … như Menu … không khác 1 tí ADN nào cả, nên Tôi sẽ không nói lại cách tạo trong XML. Mà Tôi sẽ nói luôn cách đăng ký Context Menu cho đối tượng cũng như cách xử lý sự kiện cho từng Menu Item trong Context Menu.

– Ở đây Tôi làm một ví dụ đơn giản về Context Menu. Giao diện có 1 Button, khi nhấn thật lâu vào nó thì sẽ hiển thị Context Menu cho phép đổi màu chữ : Màu đỏ, màu xanh lá cây và xanh dương:

23_ctx_0– Ở hình trên là khi Context Menu hiển thị ra, Tôi chọn Red –> Màu của Button sẽ chuyển thành màu đỏ.

– Bạn xem cấu trúc thư mục của Project này và nội dung bên trong của Context menu:

23_ctx_1– Tiếp tục bạn Double click vào strings.xml , Tôi có định nghĩa một số color trong này:

23_ctx_2– Ở trong strings.xml , Tôi tạo 3 tag color : Red, Green, Blue; Nội dung bạn phải để dạng Hex Color. 3 màu này sẽ được triệu gọi trong hàm xử lý sự kiện khi người sử dụng chọn từng Menu Item trong Context Menu.

– Bạn xem nội dung MainActivity.java:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.Button;

public class MainActivity extends Activity {

Button btnCtx;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 btnCtx=(Button) findViewById(R.id.btnshowcontext);
 registerForContextMenu(btnCtx);
 }
 @Override
 public void onCreateContextMenu(ContextMenu menu, View v,
 ContextMenuInfo menuInfo) {
 // TODO Auto-generated method stub
 super.onCreateContextMenu(menu, v, menuInfo);
 getMenuInflater()
 .inflate(R.menu.my_context_menu, menu);
 }
 @Override
 public boolean onContextItemSelected(MenuItem item) {
 switch(item.getItemId())
 {
 case R.id.itemRed:
 btnCtx.setTextColor(
 getResources().getColor(R.color.clrred));
 break;
 case R.id.itemGreen:
 btnCtx.setTextColor(
 getResources().getColor(R.color.clrgreen));
 break;
 case R.id.itemBlue:
 btnCtx.setTextColor(
 getResources().getColor(R.color.clrblue));
 break;
 }
 return super.onContextItemSelected(item);
 }
}

– Bạn quan sát dòng lệnh:

+ Dòng lệnh 22 , onCreateContextMenu dùng để nạp Context Menu XML vào ứng dụng

+ Dòng lệnh 30, onContextItemSelected dùng để xử lý sự kiện.

+ Để đăng ký Context Menu cho đối tượng nào thì bạn làm giống như dòng 19:  registerForContextMenu(btnCtx);

getResources().getColor(R.color.clrred) dùng để lấy màu từ XML Resource

+ Như vậy bạn đã biết cách sử dụng Context Menu

+ Bạn có thể tải coding mẫu ở đây: http://www.mediafire.com/?8s368sl4z594y85

– Bài tập sau các bạn sẽ thực hành về Intent, Intent là linh hồn của Android, nó là hạt nhân chính trong Android, các bạn chú ý theo dõi.

Chúc các bạn thành công.

Bài tập 22: Thực hành về Menu trong Android


– Menu và Context menu là những control rất hữu dụng và nó rất được ưa chuộng, do đó chúng ta phải biết về nó để làm cho ứng dụng của Ta đạt hiệu suất cao hơn.

– Trước tiên Tôi sẽ trình bày về Menu:

+ Tùy vào từng phiên bản Android hay sản phẩm của hãng sản xuất mà chức năng hiển thị menu có khác nhau chút xíu.

+ Ở đây Tôi nói máy ảo thôi, để hiển thị Menu thì ta làm như sau:

22_menu_0– Nếu ứng dụng của bạn có sử dụng Menu thì chỉ cần click vào nút có chữ “Menu” là sẽ hiển thị ra.

0) Menu tự động phát sinh khi chúng ta tạo một Android Project:

– Bạn để ý là mỗi lần tạo một Android Project, nó sẽ tự tạo cho ta XML menu Resource đồng thời trong MainActivity cũng gọi sẵn hàm cho chúng ta để load Menu:

22_menu_1

– Như bạn thấy thì xml resource menu (kiến trúc bên trong nó ta sẽ tính sau) của ứng dụng được lưu trong thư mục menu, và mặc nhiên nó cùng tên với Layout.

– Bạn mở MainActivity lên: Thấy nó override phương thức onCreateOptionsMenu, dòng lệnh 18, 19 của Hàm này được dùng để tải Menu vào ứng dụng khi người dùng click vào nút “Menu”.

1) Cách tạo Menu:

a) Tạo Menu bằng XML resource:

– Giờ tôi sẽ cố tình hướng dẫn bạn tạo 1 XML menu resource khác đồng thời giúp bạn tải Menu Resource mới này vào ứng dụng khi nhấn nút “Menu” cũng như viết sự kiện cho các Menu Item:

+ Bấm chuột phải vào thư mục menu/ chọn New / chọn Android XML File:

22_menu_2+Màn hình New Android XML hiện lên, ta chọn Resource typeMenu giống hình dưới đây, đặt tên cho nó là “mymenu” rồi nhấn finish:

22_menu_3

+ Sau khi nhấn Finish, bạn quan sát lại thư mục menu của ứng dụng sẽ xuất hiện mymenu.xml:

22_menu_4+ Để tạo các Menu Item, bạn double click vào mymenu.xml:

22_menu_5+ Màn hình trên bạn để ý có 2 Tab: Layoutmymenu.xml, Tab Layout cho phép bạn tạo menu bằng giao diện, tab mymenu.xml cho phép bạn tạo menu bằng source code xml.

+ Bây giờ ta sẽ làm menu bằng giao diện, bạn nhấn vào nút “Add” ở phía bên trên :

22_menu_6+ Ở màn hình này bạn chọn Item, rồi nhấn OK:

22_menu_7

+ Khi tạo một Menu Item thì có 2 phần tử chính đó là Id và Title (Bạn xem phần Tôi tô màu vàng). Ta dựa vào Id để xử lý coding, bạn nên đặt Id nó cho khoa học, mang tính gợi nhớ. Như bạn thấy đó, Tôi đặt id cho Menu Item này là item_xemdssv thì id này sẽ được tự động cập nhập vào màn hình phía bên trái.

+ Các thuộc tính khác bạn tự nghiên cứu trên mạng, hoặc khi nào gặp vấn đề về nó Tôi sẽ trình bày sau.

+ Cứ như vậy bạn tuần tự tạo cho đầy đủ các Menu Item mà bạn mong muốn.

*** Trường hợp bạn muốn tạo Sub Menu Item (Menu Item chứa các Menu Item khác):

+ Tôi có tạo thêm 1 Menu Item là “Xem danh sách lớp“, bây giờ Tôi muốn tạo thêm 3 Sub Menu Item cho nó, bao gồm “Lớp DHTH1A”, “Lớp DHTH1B” và “Lớp DHTH1C”:

22_menu_8

*Theo Hình trên thì bạn chọn Menu bạn muốn tạo Sub Menu cho nó trước, ở đây là item_xemlop (số 1 Tôi đánh dấu)

*Sau đó bạn chọn nút “Add” :

– Mục “Create a new element at the top level, in Menu” để tạo Menu cùng cấp

– Mục Tôi tô màu vàng “Create a element in the selected element…” để tạo Sub Menu.

* Bấm OK để tiến hành tạo Sub Menu, màn hình sau sẽ xuất hiện:

22_menu_9

+ Tiếp tục để tạo Menu Item cho Sub – Menu ta chọn nút “Add“, mọi thứ sẽ lại y xì như tạo Menu Item cha của nó vậy. Muốn xóa thì chọn nút “Remove“. Khi bạn nhấn nút “Add” thì bạn sẽ thấy màn hình quen thuộc sau:

22_menu_10

+ Bạn chọn lựa như trên và nhấn OK, cứ lặp lại như vậy, mỗi lần tạo Menu Item thì bạn đặt Id và Title cho nó là xong:

22_menu_11

+ Muốn thay đổi thứ tự xuất hiện của các Menu Item bạn chọn nút “Down”, “Up”.

* Nếu muốn xem XML resource của nó như thế nào thì bạn chọn vào tab “mymenu.xml“:

22_menu_12

*** Bây giờ bạn xem cách gắn Menu vừa tạo vào ứng dụng như thế nào:

– Bạn mở MainActivity.java lên:

22_menu_13

Dòng lệnh 13, 14: getMenuInflater().inflate(R.menu.mymenu, menu); dùng để gắn Menu XML Resource vào ứng dụng, bạn chạy máy ảo lên và nhấn vào nút Menu, bạn sẽ thấy được kết quả:

22_menu_14+ Bây giờ ta viết Coding để xử lý sự kiện cho các Menu Item:

– Để viết sự kiện cho các Menu Item bạn cần Override phương thức onOptionsItemSelected, Trong phương thức này ta dựa và Id của các Menu Item để xử lý:


package tranduythanh.com;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater()
 .inflate(R.menu.mymenu, menu);
 return true;
 }
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 switch(item.getItemId())
 {
 case R.id.item_xemdssv:
 //Xử lý xem danh sách sinh viên
 break;
 case R.id.item_lopdhth1a:
 //xử lý xem thông tin lớp DHTH1A
 break;
 case R.id.item_lopdhth1b:
 break;
 case R.id.item_lopdhth1c:
 break;
 }
 return super.onOptionsItemSelected(item);
 }
}

– Bạn nhìn vào dòng lệnh 19 trở xuống, Ở đây Tôi dùng switch case để xử lý theo đúng Id mà người sử dụng chọn lựa. Tùy vào yêu cầu của bài toán mà chúng ta xử lý trong này. Tôi khuyên bạn nên viết từng hàm riêng theo nghiệp vụ rồi cứ thế mà gọi hàm theo đúng Menu Item.

– Bạn tải code mẫu ở đây: http://www.mediafire.com/?zawcff1ha6ap3ic

b) Tạo Menu bằng Coding (Runtime):

– Chúng ta có thể tạo Menu lúc Runtime (không cần dùng XML Resource):


public boolean onCreateOptionsMenu(Menu menu) {
 menu.add("Menu 1");
 menu.add("Menu 2");
 SubMenu sub3= menu.addSubMenu("Menu 3");
 sub3.add("File 1 Menu 3");
 sub3.add("File 2 Menu 3");
 sub3.add("File 3 Menu 3");
 return true;
 }

– Bạn nhìn thấy đấy, việc tạo Menu lúc Runtime rất dễ dàng. Nhưng nếu như bạn viết code tạo Menu như vậy thì việc xử lý gặp chút khó khăn vì bạn không biết Id của mỗi Menu Item.

– Do đó bạn nên viết lại theo cách sau:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;

public class MainActivity extends Activity {

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 }

@Override
 public boolean onCreateOptionsMenu(Menu menu) {
 //Đối số 1 là nhóm
 //Đối số 2 là Id cho Menu Item
 //Đối số 3 là thứ tự xuất hiện của Menu Item
 //Đối số 4 là tiêu đề cho Menu Item
 int itemId=113;
 menu.add(0, itemId, 0, "Menu 1");
 itemId=114;
 menu.add(0,itemId,1,"Menu 2");
 SubMenu sub3= menu.addSubMenu("Menu 3");
 itemId=115;
 sub3.add(0,itemId,0,"File 1 Menu 3");
 itemId=116;
 sub3.add(0,itemId,1,"File 2 Menu 3");
 itemId=117;
 sub3.add(0,itemId,2,"File 3 Menu 3");
 return true;
 }
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 switch(item.getItemId())
 {
 case 113:
 //Xử lý Menu 1
 break;
 case 114:
 //Xử lý Menu 2
 break;
 case 115:
 //Xử lý File 1 của Menu 3
 break;
 case 116:
 //Xử lý File 2 của Menu 3
 break;
 case 117:
 //Xử lý File 3 của Menu 3
 break;
 }
 return super.onOptionsItemSelected(item);
 }
}

– Như vậy mỗi lần tạo Menu Item bạn nên gán Id cho nó và xử lý theo Id này ở trong hàm onOptionsItemSelected.

– Bạn tải code mẫu ở đây: http://www.mediafire.com/?em82rb51dduvnou

– Bài tập này Tôi sẽ không làm ví dụ mẫu lớn, Tôi để dành khi học xong Context Menu Tôi Demo luôn.

– Bài tập sau Tôi sẽ hướng dẫn cách làm Context Menu.

– Chúc các bạn thành công.

Bài tập 21: Thực hành về Tab Selector trong Android


Tab Selector giống như Property Sheet trong Visual C, giống như Tab Control C#, hay trong Java:

21_tab_0

– Tôi sẽ làm một ví dụ cụ thể về Tab Selector để bạn có thể thực hành lại và hiểu được vấn đề.

– Đối với Android, mỗi Tab bạn nên thiết kế trên một Layout khác nhau, rồi trong Main Layout bạn include các tab đó vào (Tức là nếu như ứng dụng bạn có 3 Tab con thì sẽ tạo 3 Layout khác nhau rồi include chúng vào Main layout, chứ đừng thiết kế tất tần tật trong một Main Layout nó sẽ gây khó khăn trong việc sửa lỗi ).

– Tôi trình bày sơ qua lý thuyết về Tab selector:

+ Tab selector gồm có 3 phần: Tab Host, Tab WidgetsFrameLayout.

21_tab_1

+Tab Host: Là Container chính chứa các Tab buttons và Tab contents

+Tab Widget: Để định dạng cho các Tab buttons : Nhãn, Icon…

+FrameLayout: là Container để chứa các layout cho Tab contents, ta chỉ có thể dùng FrameLayout cho Tab contents, không thể dùng các loại Layout khác. Nếu bạn thắc mắc tại vì sao lại là FrameLayout mà không phải là các Layout khác? thì Tôi chỉ nói đơn giản thế này: Cho dù bạn có nhấn vào các Tab nào đi nữa thì layout tương ứng với mỗi Tab mà bạn vừa nhấn vào cũng chỉ xuất hiện cùng một chỗ trên màn hình điện thoại, điều này chỉ có FrameLayout mới giải quyết được.

*** Bây giờ bạn hãy xem hình minh họa về giao diện trong bài ví dụ Tab Selector của Tôi như sau:

21_tab_3– Như hình trên thì bạn thấy đó: Tab đầu tiên “1-CALCULATOR” là giao diện cho phép tính công trừ nhân chia, Tab thứ 2 “2-HISTORY” dùng để hiển thị danh sách các phép toàn đã thực hiện.

– Bạn xem cấu trúc tổng quan của ứng dụng:

21_tab_4-Vì ứng dụng của Tôi có 2 Tab nên Tôi sẽ tạo 2 tabs: tab1_layout.xmltab2_layout.xml, 2 tabs này sẽ được include vào main layout activity_main.xml, vậy tổng cộng Tôi có 3 Layout.

– Ta vào xem main layout (activity_main.xml):

– xem Outline để dễ tưởng tượng:

21_tab_5-Còn đây là source XML:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<TabHost
 android:id="@android:id/tabhost"
 android:layout_width="match_parent"
 android:layout_height="match_parent" >
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >
 <TabWidget
 android:id="@android:id/tabs"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </TabWidget>
 <FrameLayout
 android:id="@android:id/tabcontent"
 android:layout_width="match_parent"
 android:layout_height="match_parent" >
 <LinearLayout
 android:id="@+id/tab1"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 <include layout="@layout/tab1_layout"/>
 </LinearLayout>
 <LinearLayout
 android:id="@+id/tab2"
 android:layout_width="match_parent"
 android:layout_height="match_parent" >
 <include layout="@layout/tab2_layout"/>
 </LinearLayout>
 </FrameLayout>
 </LinearLayout>
 </TabHost>

</LinearLayout>

– Bạn nhìn vào dòng lệnh 30 và 36:

<include layout=”@layout/tab1_layout”/>

<include layout=”@layout/tab2_layout”/>

Đó chính là cách include một layout này vào trong một layout khác.

– Tiếp tục ta xem tab1_layout.xml, Tôi lấy lại bài tập trước về cộng trừ nhân chia:


<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/TableLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:stretchColumns="*" >

<TableRow
 android:id="@+id/tableRow1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="4"
 android:background="#5B8020"
 android:gravity="center"
 android:text="Cộng trừ nhân chia"
 android:textColor="#FFFFFF"
 android:textSize="20sp" />
 </TableRow>

<TableRow
 android:id="@+id/tableRow2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Số a:" />

<EditText
 android:id="@+id/editsoa"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="3"
 android:ems="10"
 android:inputType="number" >

<requestFocus />
 </EditText>
 </TableRow>

<TableRow
 android:id="@+id/tableRow3"
 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="Số b:" />

<EditText
 android:id="@+id/editsob"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="3"
 android:ems="10"
 android:inputType="number" />
 </TableRow>

<TableRow
 android:id="@+id/tableRow4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/btncong"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="+" />

<Button
 android:id="@+id/btntru"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="-" />

<Button
 android:id="@+id/btnnhan"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="*" />

<Button
 android:id="@+id/btnchia"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="/" />
 </TableRow>

<TableRow
 android:id="@+id/tableRow5"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/txtketqua"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="4"
 android:background="#5B8020"
 android:textColor="#FFFFFF"
 android:textSize="25sp" />
 </TableRow>

</TableLayout>

– Và xem tiếp tab2_layout.xml, đơn giản là chỉ có 1 ListView chứa danh sách các phép toán đã thực hiện bên Tab1:


<?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="match_parent"
 android:orientation="vertical" >

<ListView
 android:id="@+id/lvhistory"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </ListView>

</LinearLayout>

– Giờ bạn xem MainActivity để biết được cách cấu hình Tabhost:


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.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
 //Enum để thực hiện phép toán
 enum Operator
 {
 Cong,//phép cộng
 Tru,//phép trừ
 Nhan,//phép nhân
 Chia//phép chia
 }
 Button btncong,btntru,btnnhan,btnchia;
 EditText editsoa,editsob;
 TextView txtkq;
 ListView lvHistory;
 ArrayList<String>array_operator=new ArrayList<String>();
 ArrayAdapter<String>adapter=null;
 //Variable in listener
 OnClickListener myclick=new OnClickListener() {

@Override
 public void onClick(View arg0) {
 switch(arg0.getId())
 {
 case R.id.btncong:
 {
 processOperator(Operator.Cong);
 }
 break;
 case R.id.btntru:
 {
 processOperator(Operator.Tru);
 }
 break;
 case R.id.btnnhan:
 {
 processOperator(Operator.Nhan);
 }
 break;
 case R.id.btnchia:
 {
 processOperator(Operator.Chia);
 }
 }
 }
 };
 /**
 * Hàm xử lý phép toán theo operator
 * @param op
 */
 public void processOperator(Operator op)
 {
 String sa=editsoa.getText()+"";
 String sb=editsob.getText().toString();
 int a=Integer.parseInt(sa);
 int b=Integer.parseInt(sb);
 String kq="";
 switch(op)
 {
 case Cong:
 kq=a+" + "+b +" = "+(a+b);
 break;
 case Tru:
 kq=a+" - "+b +" = "+(a-b);
 break;
 case Nhan:
 kq=a+" * "+b +" = "+(a*b);
 break;
 case Chia:
 if(b!=0)
 kq=a+" / "+b +" = "+(a*1.0/b);
 else
 kq="b phai khac 0";
 break;
 default:
 kq="Invalid operator!";
 }
 txtkq.setText(kq);
 array_operator.add(kq);
 adapter.notifyDataSetChanged();
 }
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 loadTabs();
 doFormWidgets();
 }
 //Cấu hình tab
 public void loadTabs()
 {
 //Lấy Tabhost id ra trước (cái này của built - in android
 final TabHost tab=(TabHost) findViewById
 (android.R.id.tabhost);
 //gọi lệnh setup
 tab.setup();
 TabHost.TabSpec spec;
 //Tạo tab1
 spec=tab.newTabSpec("t1");
 spec.setContent(R.id.tab1);
 spec.setIndicator("1-Calculator");
 tab.addTab(spec);
 //Tạo tab2
 spec=tab.newTabSpec("t2");
 spec.setContent(R.id.tab2);
 spec.setIndicator("2-History");
 tab.addTab(spec);
 //Thiết lập tab mặc định được chọn ban đầu là tab 0
 tab.setCurrentTab(0);
 //Ở đây Tôi để sự kiện này để các bạn tùy xử lý
 //Ví dụ tab1 chưa nhập thông tin xong mà lại qua tab 2 thì báo...
 tab.setOnTabChangedListener(new
 TabHost.OnTabChangeListener() {
 public void onTabChanged(String arg0) {
 String s="Tab tag ="+arg0 +"; index ="+
 tab.getCurrentTab();
 Toast.makeText(getApplicationContext(),
 s, Toast.LENGTH_LONG).show();}
 });
 }
 //Khởi tạo các đối tượng và gán ADapter cho ListView
 public void doFormWidgets()
 {
 btncong=(Button) findViewById(R.id.btncong);
 btntru=(Button) findViewById(R.id.btntru);
 btnnhan=(Button) findViewById(R.id.btnnhan);
 btnchia=(Button) findViewById(R.id.btnchia);
 editsoa=(EditText) findViewById(R.id.editsoa);
 editsob=(EditText) findViewById(R.id.editsob);
 txtkq=(TextView) findViewById(R.id.txtketqua);
 lvHistory=(ListView) findViewById(R.id.lvhistory);
 btncong.setOnClickListener(myclick);
 btntru.setOnClickListener(myclick);
 btnnhan.setOnClickListener(myclick);
 btnchia.setOnClickListener(myclick);
 adapter=new ArrayAdapter<String>
 (this,
 android.R.layout.simple_list_item_1,
 array_operator);
 lvHistory.setAdapter(adapter);
 }
}

– Tôi có giải thích sơ qua trong coding cách tạo tab, bạn hãy cố gắng làm lại để hiểu nó.

– Bao nhiêu tab không quan trọng, cuối cùng thì cũng chỉ đưa về xử lý bình thường như chỉ có 1 màn hình.

– Bạn  có thể tải coding mẫu ở đây: http://www.mediafire.com/?0xyr6ooi066dhiw

– Bài tập kế tiếp bạn sẽ học về MenuContext menu, 2 control này rất ưa chuộng nên ta phải biết nó.

– Chúc bạn thành công.

Bài tập 20: Thực hành về DatePickerDialog và TimePickerDialog trong Android


– Bài tập này bạn sẽ được học về Time Selection. Một số bài tập trước đó Tôi cũng đã sử dụng nó rồi, nhưng trong bài tập này Tôi muốn giải thích nó rõ ràng hơn.

– Có nhiều loại Time Selection: AnalogClock, Chronometor, DatePickerDialog, TimePickerDialog. Nhưng Tôi chỉ giới thiệu 2 control cuối mà thôi, các control khác các bạn tự tìm hiểu thêm trên mạng:

20_time_0– Hình bên trái là DatePickerDialog (dùng để thiết lập ngày tháng năm), hình bên phải là TimePickerDialog( dùng để thiết lập giờ phút).

– Theo Tôi thì chúng ta không nên kéo thả các control này ra màn hình vì nó chiếm hết không gian của các control khác, với lại màn hình điện thoại quá nhỏ nên nó sẽ không phù hợp tí nào khi bạn cố tình kéo thả nó ra. Vì vậy Tôi khuyên bạn nên viết code động, cho một Button để người sử dụng chọn Ngày tháng hay giờ giấc, khi nhấn vào nó thì mới hiển thị các dialog tương ứng này lên.

– Tôi sẽ có một ví dụ cụ thể để thực hành về 2 control này, bạn hãy chú ý theo dõi và thực hành lại thật tốt.

Yêu cầu ứng dụng như sau:

–          Viết chương trình quản lý mục tiêu hoàn thành công việc hàng tuần: Lý do chúng ta làm việc không có quy củ, không nhất quán, gặp đâu làm đó, đang làm việc này thấy việc kia sảy ra lại nhảy qua việc đó làm để dở công việc cũ chưa xong, không theo quy trình định sẵn dẫn đến kết thúc một ngày mà cuối cùng không hoàn thành được việc gì cả.

–          Thiết kế giao diện như hình bên dưới và thực hiện một số chức năng:

20_time_1

  1. Khi chọn nút Date sẽ hiển thị DatePickerDialog – cập nhật Ngày hoàn thành
  2. Khi chọn nút Time sẽ hiển thị TimePickerDialog – cập nhật giờ hoàn thành
  3. Khi chọn nút Thêm công việc (Thêm CV), chương trình sẽ cập nhập vào ListView bên dưới màn hình
  4. Khi nhấn vào phần tử nào thì hiển thị chi tiết Nội Dung công việc phải hoàn thành
  5. Khi nhấn thật lâu vào phần tử nào trên ListView thì xóa phần tử (công việc) đó.

– Bài tập này Tôi đưa ra là tiền đề để làm phần Android Service cụ thể là Alarm Manager (Khi nào tới phần đó Tôi sẽ nhắc lại bài này). Bạn cứ tưởng tượng phần mềm này nó giống như chương trình báo thức đó bạn. Tới giờ là nó Beeng ! Beeng! la làng lên để nhắc nhở các bạn. Nhưng hiện giờ bạn hãy quên điều đó đi vì chúng ta cần phải đi từ từ và phải hiểu thật thấu đáo các thành phần căn bản trước đã, giờ bạn chỉ cần thao tác được 5 yêu cầu của Tôi nêu bên trên là đã đạt rồi.

– Ta sẽ giải quyết yêu cầu bài toán này như sau:

1) Trước tiên Tôi sẽ ôn tập lại Date, Calendar, SimpleDateFormat cho bạn:

– Cách sử dụng Date để xuất Ngày/Tháng / Năm:


public static void XuatNgayThangNam()
 {
 //Tạo đối tượng date sẽ lấy date hiện tại
 Date date = new Date();

 //Muốn xuất Ngày/Tháng/Năm , ví dụ 12/04/2013
 String strDateFormat = "dd/MM/yyyy";
 //tạo đối tượng SimpleDateFormat;
 SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
 //gọi hàm format để lấy chuỗi ngày tháng năm đúng theo yêu cầu
 System.out.println("Ngày hôm nay : " + sdf.format(date));
 }

– Tại thời điểm này thì Tôi gọi hàm trên  sẽ ra  kết quả:

Ngày hôm nay : 12/04/2013

– Cách sử dụng Date để xuất Giờ:Phút:Giây :


public static void XuatGioPhutGiay()
 {
 //Tạo đối tượng date sẽ lấy date hiện tại
 Date date = new Date();

 //Muốn xuất Giờ:Phút:Giây AM (PM)
 String strDateFormat12 = "hh:mm:ss a";
 String strDateFormat24 = "HH:mm:ss a";
 SimpleDateFormat sdf =null;
 //Tạo đối tượng SimpleDateFormat với định dạng 12
 sdf= new SimpleDateFormat(strDateFormat12);
 //1. gọi hàm format để lấy giờ:phút:giây loại 12
 System.out.println("Giờ định dạng 12 : " + sdf.format(date));
 //Tạo đối tượng SimpleDateFormat với định dạng 24
 sdf = new SimpleDateFormat(strDateFormat24);
 //2. gọi hàm format để lấy giờ:phút:giây loại 24
 System.out.println("Giờ định dạng 24 : " + sdf.format(date));
 }

– Khi gọi hàm trên thì Tôi sẽ có kết quả (tính tại thời điểm đó):

Giờ định dạng 12 : 04:35:08 PM
Giờ định dạng 24 : 16:35:08 PM

– Bạn quan sát dòng lệnh số 7 :  “hh:mm:ss a”, với hh là viết chữ thường thì bạn sẽ được định dạng giờ 12. ký tự a ở cuối dùng để hiện thị AM hay là PM tùy thuộc vào giờ lúc đó.

– Bạn quan sát dòng lệnh số 8: HH:mm:ss a”, với HH là viết chữ HOA thì bạn sẽ được định dạng giờ 24. ký tự a ở cuối dùng để hiện thị AM hay là PM tùy thuộc vào giờ lúc đó. Nhưng bạn nhớ một điều là khi sử dụng định dạng giờ là 24 thì chả ai gán kèm theo AM hay PM đằng sau làm gì. (Không biết Tôi có nói đúng không nhỉ? nếu không đúng thì các bạn cứ cho là Tôi nói đúng để an ủi cũng được) Nên đối với định dạng 24 giờ bạn nên bỏ ký tự a đằng sau ra.

– Chú ý bạn có thể kết hợp hiển thị Ngày/Tháng/Năm giờ hiện tại: “dd/MM/yyyy hh:mm:ss  a” 

– Cách sử dụng Calendar (class này bây giờ rất được ưa chuộng, bạn nên tập làm quen với nó):


public static void SudungCalendar()
 {
 //Lấy đối tượng Calendar ra, mặc định ngày hiện tại
 Calendar now = Calendar.getInstance();
 //Muốn xuất Giờ:Phút:Giây AM (PM)
 String strDateFormat12 = "hh:mm:ss a";
 String strDateFormat24 = "HH:mm:ss";
 SimpleDateFormat sdf =null;
 //Tạo đối tượng SimpleDateFormat với định dạng 12
 sdf= new SimpleDateFormat(strDateFormat12);
 //1. gọi hàm format để lấy giờ:phút:giây loại 12
 System.out.println("Giờ định dạng 12 : " +
                 sdf.format(now.getTime()));
 //Tạo đối tượng SimpleDateFormat với định dạng 24
 sdf = new SimpleDateFormat(strDateFormat24);
 //2. gọi hàm format để lấy giờ:phút:giây loại 24
 System.out.println("Giờ định dạng 24 : " +
                sdf.format(now.getTime()));

 String strDateFormat = "dd/MM/yyyy hh:mm:ss a";
 sdf = new SimpleDateFormat(strDateFormat);
 System.out.println("Bây giờ là: " + sdf.format(now.getTime()));

 System.out.println("Năm hiện tại : " + now.get(Calendar.YEAR));
 //Tháng tính từ 0 tới 11, nên phải + thêm 1
 System.out.println("Tháng hiện tại : " +
                       (now.get(Calendar.MONTH) + 1 ));
 System.out.println("Ngày hiện tại : " +
                       now.get(Calendar.DATE));

 System.out.println("Lấy giờ định dạng 12 là : "
 + now.get(Calendar.HOUR));
 System.out.println("Lấy giờ định dạng 24 là : "
 + now.get(Calendar.HOUR_OF_DAY));
 System.out.println("Phút hiện tại : " +
                       now.get(Calendar.MINUTE));
 System.out.println("Giây hiện tại : " +
                      now.get(Calendar.SECOND));
 System.out.println("Mili giây hiện tại: " +
                      now.get(Calendar.MILLISECOND));
 }

– Nhìn vào giải thích ở  trên thì bạn phải hiểu được là khi muốn lấy Năm, Tháng, Ngày, giờ ,Phút , giây …. thì phải sài hằng số nào của Calendar. Ráng đọc và hiểu (Tôi nghĩ là phải hiểu)

– Thực hiện chương trình trên thì bạn sẽ có kết quả như bên dưới (tính tại thời điểm Tôi ví dụ):

Giờ định dạng 12 : 05:02:50 PM
Giờ định dạng 24 : 17:02:50
Bây giờ là: 12/04/2013 05:02:50 PM
Năm hiện tại : 2013
Tháng hiện tại : 4
Ngày hiện tại : 12
Lấy giờ định dạng 12 là : 5
Lấy giờ định dạng 24 là : 17
Phút hiện tại : 2
Giây hiện tại : 50
Mili giây hiện tại: 527

– 2) – Bạn đã biết cách sử dụng Date, Calendar, SimpleDateFormat, Giờ Ta bắt đầu làm ứng dụng:

– Tạo Android Project tên tùy thích và thiết kế giao diện giống như yêu cầu bên trên:

– Bạn xem Outline để dễ tưởng tượng ra giao diện, bạn thích làm kiểu nào là tùy bạn:

20_time_2– Còn đây là source XML (activity_main.xml):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:gravity="center"
 android:text="Quản lý công việc hàng tuần"
 android:textColor="#FFFFFF" />

<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/textView2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Công việc:" />

<EditText
 android:id="@+id/editcongviec"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="2"
 android:ems="10" />

</TableRow>

<TableRow
 android:id="@+id/tableRow2"
 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="Nội dung:" />

<EditText
 android:id="@+id/editnoidung"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="2"
 android:ems="10"
 android:inputType="textMultiLine" >

<requestFocus />
 </EditText>

</TableRow>

<TableRow
 android:id="@+id/tableRow3"
 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="Ngày HT:" />

<TextView
 android:id="@+id/txtdate"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="1/3/2013"
 android:textColor="#FF0000" />

<Button
 android:id="@+id/btndate"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Date" />

</TableRow>

<TableRow
 android:id="@+id/tableRow4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView6"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Giờ HT:" />

<TextView
 android:id="@+id/txttime"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="8:30 AM"
 android:textColor="#FF0000" />

<Button
 android:id="@+id/btntime"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Time" />

</TableRow>
 </TableLayout>

<TextView
 android:id="@+id/textView9"
 android:layout_width="match_parent"
 android:layout_height="2dp"
 android:background="#E1E1E1" />

<Button
 android:id="@+id/btncongviec"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center"
 android:text="Thêm CV" />

<TextView
 android:id="@+id/textView8"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:text="Danh sách công việc:"
 android:textColor="#FFFFFF" />

<ListView
 android:id="@+id/lvcongviec"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </ListView>

</LinearLayout>

– Sau đây là nội dung xử lý các nghiệp vụ:

20_time_3– Class JobInWeek để định nghĩa một công việc phải hoàn thành trong một tuần.


package tranduythanh.com;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
 * Class được định nghĩa công việc phải hoành thành
 * trong một tuần
 * @author drthanh
 *
 */
public class JobInWeek {
 private String title;
 private String desciption;
 private Date dateFinish;
 private Date hourFinish;
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title = title;
 }
 public String getDesciption() {
 return desciption;
 }
 public void setDesciption(String desciption) {
 this.desciption = desciption;
 }
 public Date getDateFinish() {
 return dateFinish;
 }
 public void setDateFinish(Date dateFinish) {
 this.dateFinish = dateFinish;
 }
 public Date getHourFinish() {
 return hourFinish;
 }
 public void setHourFinish(Date hourFinish) {
 this.hourFinish = hourFinish;
 }
 public JobInWeek(String title, String desciption, Date dateFinish,
 Date hourFinish) {
 super();
 this.title = title;
 this.desciption = desciption;
 this.dateFinish = dateFinish;
 this.hourFinish = hourFinish;
 }
 public JobInWeek() {
 super();
 }
 /**
 * lấy định dạng ngày
 * @param d
 * @return
 */
 public String getDateFormat(Date d)
 {
 SimpleDateFormat dft=new
 SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
 return dft.format(d);
 }
 /**
 * lấy định dạng giờ phút
 * @param d
 * @return
 */
 public String getHourFormat(Date d)
 {
 SimpleDateFormat dft=new
 SimpleDateFormat("hh:mm a", Locale.getDefault());
 return dft.format(d);
 }
 @Override
 public String toString() {
 return this.title+"-"+
 getDateFormat(this.dateFinish)+"-"+
 getHourFormat(this.hourFinish);
 }
}

– Class MainActivity:


package tranduythanh.com;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import android.os.Bundle;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
/**
 * Class xử lý nghiệp vụ
 * @author drthanh
 *
 */
public class MainActivity extends Activity {
 TextView txtDate,txtTime;
 EditText editCv,editNd;
 Button btnDate,btnTime,btnAdd;
 //Khai báo Datasource lưu trữ danh sách công việc
 ArrayList<JobInWeek>arrJob=new ArrayList<JobInWeek>();
 //Khai báo ArrayAdapter cho ListView
 ArrayAdapter<JobInWeek>adapter=null;
 ListView lvCv;
 Calendar cal;
 Date dateFinish;
 Date hourFinish;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 getFormWidgets();
 getDefaultInfor();
 addEventFormWidgets();
 }
 /**
 * hàm dùng để load các control theo Id
 */
 public void getFormWidgets()
 {
 txtDate=(TextView) findViewById(R.id.txtdate);
 txtTime=(TextView) findViewById(R.id.txttime);
 editCv=(EditText) findViewById(R.id.editcongviec);
 editNd=(EditText) findViewById(R.id.editnoidung);
 btnDate=(Button) findViewById(R.id.btndate);
 btnTime=(Button) findViewById(R.id.btntime);
 btnAdd=(Button) findViewById(R.id.btncongviec);
 lvCv=(ListView) findViewById(R.id.lvcongviec);
 //Gán DataSource vào ArrayAdapter
 adapter=new ArrayAdapter<JobInWeek>
 (this,
 android.R.layout.simple_list_item_1,
 arrJob);
 //gán Adapter vào ListView
 lvCv.setAdapter(adapter);
 }
 /**
 * Hàm lấy các thông số mặc định khi lần đầu tiền chạy ứng dụng
 */
 public void getDefaultInfor()
 {
 //lấy ngày hiện tại của hệ thống
 cal=Calendar.getInstance();
 SimpleDateFormat dft=null;
 //Định dạng ngày / tháng /năm
 dft=new SimpleDateFormat("dd/MM/yyyy",Locale.getDefault());
 String strDate=dft.format(cal.getTime());
 //hiển thị lên giao diện
 txtDate.setText(strDate);
 //Định dạng giờ phút am/pm
 dft=new SimpleDateFormat("hh:mm a",Locale.getDefault());
 String strTime=dft.format(cal.getTime());
 //đưa lên giao diện
 txtTime.setText(strTime);
 //lấy giờ theo 24 để lập trình theo Tag
 dft=new SimpleDateFormat("HH:mm",Locale.getDefault());
 txtTime.setTag(dft.format(cal.getTime()));

editCv.requestFocus();
 //gán cal.getTime() cho ngày hoàn thành và giờ hoàn thành
 dateFinish=cal.getTime();
 hourFinish=cal.getTime();
 }
 /**
 * Hàm gán các sự kiện cho các control
 */
 public void addEventFormWidgets()
 {
 btnDate.setOnClickListener(new MyButtonEvent());
 btnTime.setOnClickListener(new MyButtonEvent());
 btnAdd.setOnClickListener(new MyButtonEvent());
 lvCv.setOnItemClickListener(new MyListViewEvent());
 lvCv.setOnItemLongClickListener(new MyListViewEvent());
 }
 /**
 * Class sự kiện của các Button
 * @author drthanh
 *
 */
 private class MyButtonEvent implements OnClickListener
 {
 @Override
 public void onClick(View v) {
 switch(v.getId())
 {
 case R.id.btndate:
 showDatePickerDialog();
 break;
 case R.id.btntime:
 showTimePickerDialog();
 break;
 case R.id.btncongviec:
 processAddJob();
 break;
 }
 }

}
 /**
 * Class sự kiện của ListView
 * @author drthanh
 *
 */
 private class MyListViewEvent implements
 OnItemClickListener,
 OnItemLongClickListener
 {

@Override
 public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
 int arg2, long arg3) {
 //Xóa vị trí thứ arg2
 arrJob.remove(arg2);
 adapter.notifyDataSetChanged();
 return false;
 }

@Override
 public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
 long arg3) {
 //Hiển thị nội dung công việc tại vị trí thứ arg2
 Toast.makeText(MainActivity.this,
 arrJob.get(arg2).getDesciption(),
 Toast.LENGTH_LONG).show();
 }

}
 /**
 * Hàm hiển thị DatePicker dialog
 */
 public void showDatePickerDialog()
 {
 OnDateSetListener callback=new OnDateSetListener() {
 public void onDateSet(DatePicker view, int year,
 int monthOfYear,
 int dayOfMonth) {
 //Mỗi lần thay đổi ngày tháng năm thì cập nhật lại TextView Date
 txtDate.setText(
 (dayOfMonth) +"/"+(monthOfYear+1)+"/"+year);
 //Lưu vết lại biến ngày hoàn thành
 cal.set(year, monthOfYear, dayOfMonth);
 dateFinish=cal.getTime();
 }
 };
 //các lệnh dưới này xử lý ngày giờ trong DatePickerDialog
 //sẽ giống với trên TextView khi mở nó lên
 String s=txtDate.getText()+"";
 String strArrtmp[]=s.split("/");
 int ngay=Integer.parseInt(strArrtmp[0]);
 int thang=Integer.parseInt(strArrtmp[1])-1;
 int nam=Integer.parseInt(strArrtmp[2]);
 DatePickerDialog pic=new DatePickerDialog(
 MainActivity.this,
 callback, nam, thang, ngay);
 pic.setTitle("Chọn ngày hoàn thành");
 pic.show();
 }
 /**
 * Hàm hiển thị TimePickerDialog
 */
 public void showTimePickerDialog()
 {
 OnTimeSetListener callback=new OnTimeSetListener() {
 public void onTimeSet(TimePicker view,
 int hourOfDay, int minute) {
 //Xử lý lưu giờ và AM,PM
 String s=hourOfDay +":"+minute;
 int hourTam=hourOfDay;
 if(hourTam>12)
 hourTam=hourTam-12;
 txtTime.setText
 (hourTam +":"+minute +(hourOfDay>12?" PM":" AM"));
 //lưu giờ thực vào tag
 txtTime.setTag(s);
 //lưu vết lại giờ vào hourFinish
 cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
 cal.set(Calendar.MINUTE, minute);
 hourFinish=cal.getTime();
 }
 };
 //các lệnh dưới này xử lý ngày giờ trong TimePickerDialog
 //sẽ giống với trên TextView khi mở nó lên
 String s=txtTime.getTag()+"";
 String strArr[]=s.split(":");
 int gio=Integer.parseInt(strArr[0]);
 int phut=Integer.parseInt(strArr[1]);
 TimePickerDialog time=new TimePickerDialog(
 MainActivity.this,
 callback, gio, phut, true);
 time.setTitle("Chọn giờ hoàn thành");
 time.show();
 }
 /**
 * Hàm xử lý đưa công việc vào ListView khi nhấn nút Thêm Công việc
 */
 public void processAddJob()
 {
 String title=editCv.getText()+"";
 String description=editNd.getText()+"";
 JobInWeek job=new JobInWeek(title, description, dateFinish, hourFinish);
 arrJob.add(job);
 adapter.notifyDataSetChanged();
 //sau khi cập nhật thì reset dữ liệu và cho focus tới editCV
 editCv.setText("");
 editNd.setText("");
 editCv.requestFocus();
 }

}

– Như đã nói ở các bài thực hành trước là chúng ta nên tách các hàm ra để dễ xử lý, thì trong bài trên cũng vậy. Tôi cũng đã ghi chú trực tiếp vào coding, càng về sau Tôi sẽ ghi chú ít đi, chỉ ghi chú những cái gì mới mà thôi.

– Trong đoạn code trên ta sẽ sử dụng 2 call back listener- mục đích là lắng nghe sự thay đổi dữ liệu trong Các dialog tương ứng:

+ Interface OnDateSetListener có phương thức trừu tường onDateSet,  trong phương  thức này ta xử lý sự thay đổi dữ liệu trong DatePickerDialog.

+ Interface OnTimeSetListener có phương thức trừu tượng onTimeSet, trong phương thức này ta xử lý sự thay đổi giờ – phút trong TimePickerDialog.

– 2 call back listener trên rất là hữu dụng, dựa vào nó mà ta luôn kiểm tra được sự thay đổi dữ liệu trong các Dialog.

– Bạn có thể tải đầy đủ code mẫu của chương trình ở đây: http://www.mediafire.com/?layng1pw9v83vqb

– Bài tập kế tiếp bạn sẽ học về Tab Selector (cũ), một control dùng để chia màn hình thành các phần tương tác khác nhau, nó cũng thú vị không  kém. Hoặc bạn có thể tìm hiểu control mới là  ActionBar trong Android 3.0 (API 11). Nếu đủ thời gian Tôi sẽ giới thiệu cả 2 loại này cho các bạn tham khảo.

– Chúc bạn thành công.

Bài tập 19: Thực hành Gridview trong Android


– Bài tập này bạn sẽ làm quen với control Gridview, cũng tương tự như ListView. Gridview cũng dựa vào Datasource và ArrayAdapter. ListView bạn làm như thế nào thì Gridview y xì.

– Điểm khác nhau là GridView có thiết lập số cột. Dữ liệu luôn đưa vào dưới dạng hình ống (mảng, list một chiều), nhưng dựa vào số cột ta thiết lập mà nó tự động ngắt hàng, xem hình minh họa:

19_grid_0– Bạn thấy đấy, Tôi có thể hiển thị Text hoặc hình ảnh vào GridView.

– Bạn có thể thiết lập số cột cho GridView theo hình dưới đây:

19_grid_1– Nếu bạn thiết lập android:numColumns=”3″,  Tức là Gridview sẽ ngắt dòng khi đủ 3 phần tử, nó chỉ khác chỗ này, còn việc đưa dữ liệu lên như thế nào thì nó y xì như làm với ListView.

– Ví dụ 1: Hiển thị văn bản lên GridView (xem hình Tôi đánh số 1):

– Bạn tạo một Android Project tên tùy thích, ở đây Tôi đặt tên: Vidu_Gridview_Text

– Đây là activity_main.xml cho ứng dụng:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >
 <TextView
 android:id="@+id/selection"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#8A9D6F"
 android:hint="Slected here" />
 <GridView
 android:id="@+id/gridView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:numColumns="3" >
 </GridView>
</LinearLayout>

– Bạn xem dòng 15 là id của GridView, Tôi để mặc định gridView1

– Dòng 18 có thuộc tính: android:numColumns= “3”, tức là dữ liệu sẽ được hiển thị trong Gridview với định dạng 3 cột.

– Tiếp theo bạn xem MainActivity.java:

package tranduythanh.com;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;

public class MainActivity extends Activity {

//Dùng mảng 1 chiều hoặc ArrayList để lưu một số dữ liệu
 String arr[]={"Ipad","Iphone","New Ipad",
 "SamSung","Nokia","Sony Ericson",
 "LG","Q-Mobile","HTC","Blackberry",
 "G Phone","FPT - Phone","HK Phone"
 };
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 //Tối tượng này dùng để hiển thị phần tử được chọn trong GridView
 final TextView selection=(TextView)
 findViewById(R.id.selection);
 final GridView gv=(GridView) findViewById(R.id.gridView1);
 //Gán DataSource vào ArrayAdapter
 ArrayAdapter<String>da=new ArrayAdapter<String>
 (
 this,
 android.R.layout.simple_list_item_1,
 arr
 );
 //gán Datasource vào GridView
 gv.setAdapter(da);
 //Thiết lập sự kiện cho GridView,
 gv.setOnItemClickListener(new AdapterView
 .OnItemClickListener() {
 public void onItemClick(AdapterView<?> arg0,
 View arg1, int arg2,
 long arg3) {
 //Hiển thị phần tử được chọn trong GridView lên TextView
 selection.setText(arr[arg2]);
 }
 });
 }
}

– Bạn thấy đó, cách sử dụng GridView là y xì như ListView, nên nếu như bạn đã rành về ListView rồi thì GridView hiển nhiên bạn cũng làm tốt.

– Thực thi chương trình bạn sẽ thấy như hình bên dưới:

19_grid_3

– Xem coding đầy đủ ở đây: http://www.mediafire.com/?v3ww92ys1s5jth0

– Ví dụ 2 sẽ phức tạp hơn, hiển thị danh sách hình ảnh có sẵn lên GridView, mỗi lần chọn vào hình ảnh nào thì sẽ hiển thị chi tiết ảnh đó trên một màn hình mới:

– Có thể Demo này đã có nhiều website và Ebooks làm rồi, ở đây Tôi cũng muốn demo lại cho các bạn.

– Bạn xem giao diện của ứng dụng trước:

19_grid_5– Bên trái là màn hình chính cho phép hiển thị danh sách hình ảnh vào GridView, mỗi lần chọn vào từng hình trong GridView thì sẽ mở hình được chọn đó vào một màn hình mới (ví dụ như khi chọn hình chú Cừu thì nó sẽ hiển thị ra như màn hình bên phải ), nhấn nút Back để trở về màn hình chính.

– Ở đây có một sự khác biệt lớn đó là chúng ta chỉ sử dụng 1 MainActivity, không hề tạo thêm bất kỳ một Activity nào khác, chúng ta chỉ thay đổi Layout mà thôi. Nên nó cũng là điểm nhấn của ứng dụng.

-Hãy tạo một Android Project tên: Vidu_Gridview_DisplayImage và xem cấu trúc của chương trình:

19_grid_4-Layout sẽ có 2 cái: activity_main.xml là của màn hình chính dùng để hiển thị danh sách hình ảnh. solo_picture.xml là dùng để hiển thị từng hình riêng lẻ.

– Tạo thêm thư mục drawble và kéo thả một số hình ảnh vào.

–  Phần class có 2 class: MainActivity và MyImageAdapter kế thừa từ BaseAdapter.

– Bây giờ ta đi vào chi tiết của từng cái:

– activity_main.xml:

19_grid_6– Bạn có thể nhìn vào hình trên để làm hoặc tải coding mẫu ở bên dưới.

-solo_picture.xml:

19_grid_7– Bây giờ ta vào các class xử lý nghiệp vụ:

19_grid_8– Thứ nhất là class MyImageAdapter:

+ class này sẽ kế thừa từ BaseAdapter, và dùng để hiển thị từng hình ảnh riêng lẻ:

package tranduythanh.com;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
/**
 * Class dùng để hiển thị từng hình ảnh riêng lẻ
 * @author drthanh
 *
 */
public class MyImageAdapter extends BaseAdapter {
 private Context mContext;
 private Integer []mThumbIds;
 public MyImageAdapter(Context c){
 mContext=c;
 }
 public MyImageAdapter(Context c,Integer []arrIds){
 mContext=c;
 mThumbIds=arrIds;
 }
 public int getCount()
 {
 return mThumbIds.length;
 }
 public Object getItem(int arg0)
 {
 return null;
 }
 public long getItemId(int arg0)
 {
 return 0;
 }
 /**
 * Cần override lại hàm này để hiển thị hình ảnh
 */
 public View getView(int arg0, View convertView, ViewGroup arg2) {
 ImageView imgView;
 if(convertView==null){
 imgView=new ImageView(mContext);
 //can chỉnh lại hình cho đẹp
 imgView.setLayoutParams(new GridView.LayoutParams(85, 85));
 imgView.setScaleType(ImageView.ScaleType.CENTER_CROP);
 imgView.setPadding(8, 8, 8, 8);
 }else{
 imgView=(ImageView) convertView;
 }
 //lấy đúng vị trí hình ảnh được chọn
 //gán lại ImageResource
 imgView.setImageResource(mThumbIds[arg0]);
 return imgView;
 }
}

– Thứ hai là class MainActivity:


package tranduythanh.com;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

public class MainActivity extends Activity
 implements OnItemClickListener
{
 TextView tvMsg;
 GridView gv;
 TextView tvSoloMsg;
 //mảng lưu danh sách các id hình ảnh có sẵn
 Integer[]mThumbIds;
 //Adapter cho GridView
 MyImageAdapter adapter=null;
 //Vì không tạo thêm 1 Activity nên lấy luôn 2 Id ở bên solo_picture.xml
 ImageView ivSoloPicture;
 Button btnBack;
 //Lưu Bundle backup cho MainActivity
 Bundle myBackupBundle;
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 //Lưu savedInstanceState trước vào myBackupBundle
 myBackupBundle=savedInstanceState;
 setContentView(R.layout.activity_main);
 tvMsg=(TextView) findViewById(R.id.tvMsg);
 //gán mảng các Id hình ảnh cho mThumbIds
 mThumbIds=new Integer[]{R.drawable.image1,R.drawable.image2,
 R.drawable.image3,R.drawable.image4,
 R.drawable.image5,R.drawable.image6,
 R.drawable.ic_launcher,R.drawable.lifecycle};
 gv=(GridView) findViewById(R.id.gridView1);
 //thiết lập Datasource cho Adapter
 adapter=new MyImageAdapter(this, mThumbIds);
 //gán Adapter vào Gridview
 gv.setAdapter(adapter);
 //thiết lập sự kiện để mở từng hình ảnh chitiết
 gv.setOnItemClickListener(this);
 }
 public void onItemClick(AdapterView<?> arg0,
 View arg1, int arg2, long arg3) {
 //gọi hàm xem hình ảnh chi tiết tại ví trí thứ arg2
 showdetail(arg2);
 }
 public void showdetail(int posistion)
 {
 //Không mở Activity mới mà chỉ thiết lập lại Layout
 setContentView(R.layout.solo_picture);
 //Vừa gọi hàm trên thì màn hình sẽ thay đổi qua cái mới
 //ta lấy các control trong layout mới này
 tvSoloMsg=(TextView) findViewById(R.id.tvSoloMsg);
 tvSoloMsg.setText("Image at "+posistion);
 ivSoloPicture=(ImageView) findViewById(R.id.imgSolo);
 //thiết lập hình ảnh đang chọn lên ImageView mới
 ivSoloPicture.setImageResource(mThumbIds[posistion]);
 btnBack=(Button) findViewById(R.id.btnBack);
 //Thiết lập sự kiện click Back để phục hồi lại MainActivity
 //bằng cách gọi lại onCreate(myBackupBundle);
 btnBack.setOnClickListener(new View.OnClickListener() {
 public void onClick(View arg0) {
 onCreate(myBackupBundle);
 }
 });
 }
}

– Khởi chạy ứng dụng bạn sẽ có kết quả như mong muốn.

– Bài tập này bạn đã biết cách sử dụng GridView, biết cách đưa hình ảnh vào GridView, đặc biệt biết thêm một kỹ thuật mới đó là thay đổi Layout để đổi màn hình không cần chạy Activity.

– Ngoài ra còn biết thêm về BaseAdapter.

– Bạn có thể tải code mẫu đầy đủ ở đây: http://www.mediafire.com/?3o2jva4mzgp2kj8

– Bài tập tiếp theo bạn sẽ được thực hành về TimePickerDialog DatePickerDialog, 2 control này cũng đáng phải lưu tâm vì nó cũng dược sử dụng thường xuyên trong Android.

– Chúc các bạn thành công

Bài tập 18: Cập nhật DataSource cho AutocompleteTextView lúc Runtime


– Trong bài tập 17 bạn đã biết cách sử dụng AutocompleteTextView  và MultiAutocompleteTextView nhưng DataSource được cố định sẵn trong Coding. Bài tập này Bạn sẽ học cách cập nhật DataSource lúc ứng dụng đang chạy (runtime).

– Tôi sẽ có một ví dụ cụ thể để bạn thực hành về trường hợp này, bạn hãy tập trung theo dõi và thực hành lại. Tôi nghĩ bài này rất thú vị.

– Bây giờ bạn mở AVD lên và nhấn tổ hợp phím ctrl+ F11 để xoay ngang màn hình, Tôi sẽ làm ví dụ về màn hình nằm ngang.

18_auto_0– Bạn xem màn hình trên Tôi chia ra làm 2. Bên trái cho phép nhập thông tin sinh viên, bên phải cho phép hiển thị danh sách sinh viên đã nhập. Bạn chú ý là ListView Tôi làm là Custom Layout (Mỗi dòng Lớn có 2 loại dữ liệu: mã và tên cùng 1 dòng. giới tính, năm sinh, quê quán cùng  1 dòng và chữ nhỏ hơn có màu đó in nghiêng).

– Ngày sinh dùng DatePickerDialog

– Nơi sinh dùng AutoCompleteTextView. Mỗi lần nhấn “Nhập Sv” thì chương trình sẽ cập nhật động nơi sinh vào DataSource của nó, chú ý là không cho phép trùng lắp. Trong các lần nhập nơi sinh tiếp theo thì nó phải tự động lọc giúp người sử dụng nhập lẹ hơn.

– Ta bắt đầu vào chi tiết ứng dụng.

1- Đây là cấu trúc thư mục trong ứng dụng:

18_auto_3-Nhìn vào cấu trúc thì bạn thấy có 2 Layout, layout chính là activity_main.xml,  còn layout sinhvien_item_layout.xml là Tôi dùng để custom lại ListView (nó sẽ được xử lý trong class MyArrayAdapter).

2- Thiết kế giao diện với màn hình nằm ngang (xem hình):

18_auto_1

– Dưới đây là Outline activity_main.xml:

18_auto_2– Nếu như nhìn vào Outline mà bạn có thể thiết kế được giao diện như trên thì Tôi nghĩ bạn đã thật sự hiểu về Layout, tuy nhiên Tôi vẫn cung cấp Source XML, bạn xem tham khảo


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".MainActivity" >

<LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:background="#FFF0E1"
 android:orientation="vertical" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:text="Nhập Thông Tin Sinh Viên:"
 android:textColor="#FFFFFF" />

<TableLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 >

<TableRow
 android:id="@+id/tableRow2"
 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ã:" />

<EditText
 android:id="@+id/editMa"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="2" >

<requestFocus />
 </EditText>

</TableRow>

<TableRow
 android:id="@+id/tableRow3"
 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:" />

<EditText
 android:id="@+id/editTen"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="2" />

</TableRow>

<TableRow
 android:id="@+id/tableRow4"
 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:" />

<CheckBox
 android:id="@+id/chkGt"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_span="2"
 android:text="Là Nữ" />

</TableRow>

<TableRow
 android:id="@+id/tableRow5"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView6"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Ngày sinh:" />

<EditText
 android:id="@+id/editNgaySinh"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:inputType="date"
 android:text="25/09/1989" />

<Button
 android:id="@+id/btnNgaySinh"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="..." />

</TableRow>

<TableRow
 android:id="@+id/tableRow6"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView7"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Nơi sinh:" />

<AutoCompleteTextView
 android:id="@+id/autoCompleteNS"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:completionThreshold="1"
 android:layout_span="2"
 android:ems="10" />

</TableRow>

<TableRow
 android:id="@+id/tableRow7"
 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:gravity="center"
 android:text="Nhập SV" />

</TableRow>

</TableLayout>
 </LinearLayout>

<LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:background="#EAEAEA"
 android:orientation="vertical" >

<TextView
 android:id="@+id/textView2"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#000040"
 android:text="Danh Sách Sinh Viên:"
 android:textColor="#FFFFFF" />

<ListView
 android:id="@+id/lvsinhvien"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_weight="1" >
 </ListView>

</LinearLayout>

</LinearLayout>

– còn đây là của sinhvien_item_layout.xml:


<?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="match_parent"
 android:orientation="vertical" >

<TextView
 android:id="@+id/txtMaVaTen"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:textSize="15sp" />

<TextView
 android:id="@+id/txtThongTinKhac"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:textColor="#800000"
 android:textSize="10sp"
 android:textStyle="italic" />

</LinearLayout>

-3 -Bạn xem cấu trúc class xử lý nghiệp vụ:

18_auto_4

– class Student:


package tranduythanh.com;

import java.util.Date;
/**
 * Class dùng để lưu trữ thông tin của sinh viên
 * id: Mã
 * name: Tên
 * gender: giới tính, true là nữ
 * birthday: lưu năm sinh
 * placeOfBirth: nơi sinh
 * @author drthanh
 *
 */
public class Student {
 private String id;
 private String name;
 private boolean gender;
 private Date birthday;
 private String placeOfBirth;
 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;
 }
 public Date getBirthday() {
 return birthday;
 }
 public void setBirthday(Date birthday) {
 this.birthday = birthday;
 }
 public String getPlaceOfBirth() {
 return placeOfBirth;
 }
 public void setPlaceOfBirth(String placeOfBirth) {
 this.placeOfBirth = placeOfBirth;
 }
 public Student(String id, String name, boolean gender, Date birthday,
 String placeOfBirth) {
 super();
 this.id = id;
 this.name = name;
 this.gender = gender;
 this.birthday = birthday;
 this.placeOfBirth = placeOfBirth;
 }
 public Student() {
 super();
 }

}

– 4 class MyArrayAdapter (class dùng để Custom Listview):


package tranduythanh.com;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/**
 * Đây là class dùng để custom layout
 * Bạn đã được học trước đó
 * Class này sẽ lấy layout: sinhvien_item_layout.xml
 * @author drthanh
 *
 */
public class MyArrayAdapter extends ArrayAdapter<Student> {

Activity context;
 int resourceId;
 ArrayList<Student>arrStudent;
 public MyArrayAdapter(Activity context, int resource,
 ArrayList<Student> objects) {
 super(context, resource, objects);
 this.context=context;
 this.resourceId=resource;
 this.arrStudent=objects;
 }
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 if(convertView==null)
 {
 //gắn Layout vào Activity
 convertView= context.getLayoutInflater().inflate(resourceId, null);
 }
 //Lấy Textview để lưu mã và tên
 TextView txtMaTen=(TextView) convertView.findViewById(R.id.txtMaVaTen);
 //Lấy TextView để lưu giới tính, năm sinh, nơi sinh
 TextView txtKhac=(TextView) convertView.findViewById(R.id.txtThongTinKhac);
 //Lấy sinh viên thứ position
 Student s=arrStudent.get(position);
 txtMaTen.setText(s.getId()+" - "+s.getName());
 //Dùng SimpleDateFormat để định dạng ngày tháng dd/MM/YYYY -> 22/12/2012
 SimpleDateFormat dft=new SimpleDateFormat("dd/MM/yyyy",Locale.getDefault());
 txtKhac.setText((s.isGender()?"Nữ-":"Nam-")+
 dft.format(s.getBirthday())+" - "+
 s.getPlaceOfBirth());
 return convertView;
 }
}

-5- class MainActivity để xử lý nghiệp vụ:


package tranduythanh.com;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;

import android.os.Bundle;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

ListView lvSinhvien;
 //Cặp đôi để Custom ListView
 MyArrayAdapter adapterSinhvien;
 ArrayList<Student> arrSinhvien=new ArrayList<Student>();

 EditText editMa,editTen,editNamsinh;
 CheckBox chkGender;
 Button btnNamsinh,btnNhapsv;

 AutoCompleteTextView autoTextNs;
 //Cặp đôi để dùng cho AutoCompleteTextView
 ArrayList<String>arrAuto=new ArrayList<String>();
 ArrayAdapter<String>adapterAuto;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 getFormWidgets();
 addEventsForWidgets();
 //fakeData();
 }
 /**
 * hàm lấy các widgets
 */
 public void getFormWidgets()
 {
 editMa=(EditText) findViewById(R.id.editMa);
 editTen=(EditText) findViewById(R.id.editTen);
 editNamsinh=(EditText) findViewById(R.id.editNgaySinh);
 chkGender=(CheckBox) findViewById(R.id.chkGt);
 autoTextNs=(AutoCompleteTextView) findViewById(R.id.autoCompleteNS);
 btnNamsinh=(Button) findViewById(R.id.btnNgaySinh);
 btnNhapsv=(Button) findViewById(R.id.btnNhap);

 lvSinhvien=(ListView) findViewById(R.id.lvsinhvien);
 //Gán DataSource cho Adapter ListView
 adapterSinhvien=new MyArrayAdapter(this,
 R.layout.sinhvien_item_layout,
 arrSinhvien);
 //Gán Adapter vào ListView
 lvSinhvien.setAdapter(adapterSinhvien);
 }
 /**
 * Hàm thiết lập sự kiện cho Button
 */
 public void addEventsForWidgets()
 {
 btnNamsinh.setOnClickListener(new OnClickListener() {

 @Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 processBirthday();
 }
 });
 btnNhapsv.setOnClickListener(new OnClickListener() {

 @Override
 public void onClick(View v) {
 processInput();
 }
 });
 }
 /**
 * Hàm hiển thị DatePickerDialog để chọn năm sinh
 */
 public void processBirthday()
 {
 OnDateSetListener callBack=new OnDateSetListener() {

 @Override
 public void onDateSet(DatePicker arg0, int arg1, int arg2, int arg3) {
 editNamsinh.setText(arg3+"/"+arg2+"/"+arg1);
 }
 };
 //Ở đây Tôi chưa xử lý lấy ngày tháng năm trên EditText đưa vào DatePicker
 //Bạn tự làm
 DatePickerDialog dateDialog=new DatePickerDialog(this, callBack, 1989, 9, 25);
 dateDialog.setTitle("Birthday");
 dateDialog.show();
 }
 /**
 * Hàm xử lý nhập dữ liệu từ giao diện
 */
 public void processInput()
 {
 String ma=editMa.getText()+"";
 String ten=editTen.getText()+"";
 String ns=editNamsinh.getText()+"";
 String nois=autoTextNs.getText()+"";
 boolean gt=chkGender.isSelected();
 SimpleDateFormat dft=new SimpleDateFormat("dd/MM/yyyy",Locale.getDefault());
 try {
 Student s=new Student(ma, ten, gt, dft.parse(ns), nois);
 arrSinhvien.add(s);
 //Thêm mới xong thì gọi hàm dưới cập nhập lại giao diện
 adapterSinhvien.notifyDataSetChanged();
 //Xử lý cho Autocomplete về nơi sinh
 processAutoComplete(nois);
 } catch (ParseException e) {
 Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
 }
 }
 /**
 * Hàm xử lý Autocomplete Nơi sinh
 * @param data
 */
 public void processAutoComplete(String data)
 {
 //Chạy từ đầu chí cuối nếu trùng thì thoát khỏi hàm
 for(int i=0;i<arrAuto.size();i++)
 {
 String x=arrAuto.get(i);
 if(x.trim().equalsIgnoreCase(data.trim()))
 return;
 }
 //nếu xuống đây được tức là chưa tồn tại-> đừa vào arrAuto
 arrAuto.add(data);
 //Đưa vào ADapter
 adapterAuto=new ArrayAdapter<String>(this,
 android.R.layout.simple_list_item_1,
 arrAuto);
 //đưa Adapter vào AutoComplete
 autoTextNs.setAdapter(adapterAuto);
 }
 public void fakeData()
 {
 Student s1=new Student("1", "Đoàn Ái Nương", true, new Date(1980-1900, 2, 2), "TP. Hồ Chí Minh");
 Student s2=new Student("2", "Nguyễn Thùy Trang", true, new Date(1982-1900, 3, 3), "Lâm Đồng");
 Student s3=new Student("3", "Hoàng Văn Phúc", false, new Date(1970-1900, 4, 4), "Hà Nội");
 Student s4=new Student("4", "Đinh Hồng Lợi", false, new Date(1972-1900, 4, 4), "Bắc Giang");
 Student s5=new Student("5", "Nguyễn Hoàng Uyên", true, new Date(1970-1900, 4, 4), "Huê");
 arrSinhvien.add(s1);
 arrSinhvien.add(s2);
 arrSinhvien.add(s3);
 arrSinhvien.add(s4);
 arrSinhvien.add(s5);
 adapterSinhvien.notifyDataSetChanged();
 }
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.activity_main, menu);
 return true;
 }

}

– Bạn xem Tôi giải thích trong coding luôn rồi.

– Chú ý là bạn có thể yêu cầu ứng dụng chạy theo màn hình nằm ngang trong code (bạn viết trong onCreate):

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

– Bạn hãy cố gắng làm bài này vì Tôi cảm thấy nó rất hay về ý tưởng thiết kế và kỹ thuật lập trình.

– Bạn có thể Tải code mẫu tại đây: http://www.mediafire.com/?m0jeop2fb83ib3u

– Bài tập tiếp theo bạn sẽ được thực hành về GridView, một control cũng cần phải lưu tâm.

– Chúc bạn thành công

Bài tập 17: Thực hành về AutocompleteTextView và MultiAutocompleteTextView


– Đối với các thiết bị di động, việc hỗ trợ nhập dữ liệu nhanh cho người sử dụng là điều rất cần thiết.

– Android hỗ trợ 2 control này giúp chúng ta làm được điều đó. Bạn để ý là danh sách hiển thị lên nó tương tự như Spinner do đó một số bạn sẽ tưởng lầm là Spinner khi quan sát chưa kỹ.

– Tôi ví dụ một trường hợp cụ thể như sau: Bạn viết ứng dụng yêu cầu nhập vào quê quán, giả sử đất nước Việt Nam mình có 63 tỉnh thành, trong đó có các tỉnh như: Hà Nội, Huế, Hà Giang, Hà Nam Ninh,… bất kỳ tỉnh nào đó có chữ H hoặc một nhóm tỉnh thành nào đó có cùng một số ký tự đầu. Như vậy ứng dụng phải Thông minh tự đưa ra lời đề nghị nhập tỉnh thành theo đúng ký tự mà họ muốn nhập, xem hình dưới:

17_auto_0

– Như hình trên: Bạn chỉ cần nhập ký tự h đầu tiên, nó sẽ lọc ra các tỉnh thành (hay thành phố) có ký tự đầu là h.

– Bạn nhớ đây không phải là Spinner vì bạn nhìn vào tưởng nó là Spinner. Mà nó là AutoCompleteTextView.

– Vậy 2 control này nó ở đâu? xem hình:

17_auto_1– Bạn xem cấu trúc XML của giao diện (activity_main.xml):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".AutoCompleteTextViewActivity" >
 <TextView
 android:id="@+id/selection"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#5576BE"
 android:text="TextView"
 android:textColor="#FFFFFF"
 android:textSize="20sp" />

<AutoCompleteTextView
 android:id="@+id/editauto"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:completionThreshold="1"
 android:ems="10" >

<requestFocus />
 </AutoCompleteTextView>

<MultiAutoCompleteTextView
 android:id="@+id/multiAutoCompleteTextView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:completionThreshold="1"
 android:ems="10" />

</LinearLayout>

– Ở trên Tôi kéo 2 control ra luôn. Vì Tôi muốn demo code của 2 control này trong một chỗ để bạn dễ so sánh.

– Bạn nhìn vào dòng lệnh số 21 và 31:

android:completionThreshold= “1”

– Mục đích của nó là thiết lập số ký tự bắt đầu lọc trong AutoComplete. Ở đây Tôi nhập là số 1 tức là chỉ cần 1 ký tự là nó bắt đầu lọc, còn nếu như bạn sửa thành 3 thì bạn nhập tới 3 ký tự vào nó mới bắt đầu lọc.

– Xem class xử lý (MainActivity.java):

package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView;
import android.widget.TextView;

public class MainActivity extends Activity
 implements TextWatcher /*TextWatcher để xử lý sự kiện TextChange */
 {
 TextView selection;
 //Khai báo 2 CompleteTextView
 AutoCompleteTextView singleComplete;
 MultiAutoCompleteTextView multiComplete;
 //Khởi tạo mảng tạm để Test
 String arr[]={"hà nội","Huế","Sài gòn",
 "hà giang","Hội an","Kiên giang",
 "Lâm đồng","Long khánh"};
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 selection =(TextView) findViewById(R.id.selection);
 //lấy đối tượng AutoCompleteTextView ra
 singleComplete=(AutoCompleteTextView) findViewById(R.id.editauto);
 //Thiết lập để lắng nghe TextChange
 singleComplete.addTextChangedListener(this);
 //Thiết lập ArrayADapter
 singleComplete.setAdapter(
 new ArrayAdapter<String>
 (
 this,
 android.R.layout.simple_list_item_1,
 arr
 ));
 //Lấy đối tượng MultiAutoCompleteTextView ra
 multiComplete=(MultiAutoCompleteTextView)
 findViewById(R.id.multiAutoCompleteTextView1);
 //Thiết lập ArrayADapter
 multiComplete.setAdapter(
 new ArrayAdapter<String>
 (
 this,
 android.R.layout.simple_list_item_1,
 arr
 ));
 //Đối với MultiAutoCompleteTextView bắt buộc phải gọi dòng lệnh này
 multiComplete.setTokenizer(new MultiAutoCompleteTextView
 .CommaTokenizer());
 }
 //Khi chọn trong AutoCompleteTextView hàm này sẽ tự động phát sinh
 public void onTextChanged(CharSequence arg0, int arg1,
 int arg2, int arg3) {
 selection.setText(singleComplete.getText());
 }
 public void afterTextChanged(Editable arg0) {
 }
 public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
 int arg3) {
 }
}

– Tôi giải thích thêm:

+ Việc gán DataSource vào ArrayAdapter rồi gán ArrayAdapter vào cho ListView như thế nào thì nó y xì như vậy đối với AutoCompleteTextView.

+ Đối với MultiAutoCompleteTextView cũng vậy, nó chỉ yêu cầu thêm dòng lệnh:

setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer())

– Như vậy nếu bạn đã hiểu ListView thì không có lý do gì mà lại không hiểu CompleteTextView.

– Bạn tải code mẫu ở đây:http://www.mediafire.com/?dos0a2v0bh6hp2b

– Bài tập sau Tôi sẽ hướng dẫn các bạn cập nhật DataSource lúc Runtime cho AutocompleteTextView.

– Chúc các bạn thành công.

Bài tập 16: Kết hợp Spinner với ListView trong Android


bài tập 15 bạn đã làm quen được với Spinner, trong bài tập này bạn sẽ làm một ví dụ về cách kết hợp giữa Spinner với ListView. Thường thì 2 control này đi với nhau sẽ tạo thành cặp bài trùng, Spinner dùng để lưu trữ danh mục còn ListView lưu trữ danh sách của từng danh mục. Ở đây Tôi có làm một ví dụ về quản lý sản phẩm, bạn xem hình:

16_combine_spin_lv_0– Bạn quan sát hình Tôi chụp ứng dụng, phần trên cùng là Danh mục các sản phẩm được lưu vào Spinner, Khi bạn chọn vào nó thì sẽ xổ ra danh sách như bên dưới. khi bạn chọn danh mục nào thì nó sẽ load các sản phẩm thuộc danh mục đó.

– Ví dụ bây giờ bạn chọn số 1 là SamSung, nó sẽ load toàn bộ sản phẩm là SamSung vào ListView bên dưới:

16_combine_spin_lv_1– Nếu bạn chọn 2- Iphone thì nó sẽ load toàn bộ sản phẩm là IPhone vào ListView bên dưới:

16_combine_spin_lv_2– Chương trình cung cấp nút “Nhập SP“, khi người sử dụng nhập thông tin cho sản phẩm và nhấn nút này thì chương trình sẽ lưu sản phẩm vào đúng với danh mục được chọn trong Spinner đồng thời cập nhật vào ListView bên dưới.

– Bạn cần có ArrayList + ArrayAdapter cho Spinner

– Và cần có ArrayList + ArrayAdapter cho ListView

-> Tức là bạn phải có 2 cặp (4 đối tượng trên)

– Ví dụ này Tôi viết thuần hướng đối tượng, và có hơi phá cách một chút so với quy tắc hướng đối tượng thông thường, đó là trong lớp Sản phẩm Tôi cho phép nó tham chiếu trực tiếp tới đối tượng Danh mục chứa nó. Như vậy thì đứng tại danh mục nào cũng có thể lấy được toàn bộ danh sách sản phẩm của nó, và đứng tại một sản phẩm bất kỳ nào cũng biết được nó thuộc danh mục nào.

– Bạn xem cấu trúc chương trình:

16_combine_spin_lv_3– Ở trên bạn thấy có 3 class: Goods, Product, Catalog: Product và Catalog sẽ kế thừa từ Goods,  Goods sẽ có Id và Name. Sản phẩm và danh mục cũng phải có Id và Name nên nó kế thừa từ Goods là đều hợp lý.

– Bạn xem mô hình lớp:

16_combine_spin_lv_4-Bạn xem cấu trúc XML cho phần giao diện (activity_main.xml):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:gravity="center"
 android:text="Quản lý sản phẩm"
 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/textView2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Danh mục:" />

<Spinner
 android:id="@+id/spDanhmuc"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

</TableRow>

<TableRow
 android:id="@+id/tableRow2"
 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ã Sp:" />

<EditText
 android:id="@+id/editId"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:ems="10" >

<requestFocus />
 </EditText>

</TableRow>

<TableRow
 android:id="@+id/tableRow3"
 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 Sp:" />

<EditText
 android:id="@+id/editName"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:ems="10" />

</TableRow>

<TableRow
 android:id="@+id/tableRow4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/btnInput"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_column="1"
 android:text="Nhập SP" />

</TableRow>
 </TableLayout>

<TextView
 android:id="@+id/textView5"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:gravity="center"
 android:text="Danh sách sản phẩm theo danh mục"
 android:textColor="#FFFFFF"
 android:textSize="15sp" />

<ListView
 android:id="@+id/lvsanpham"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </ListView>

</LinearLayout>

– Tương tự như các bài tập trước, chúng ta phải đặt id cho các control. Bài tập này bạn tự nhìn vào để xem Tôi đặt Id như thế nào, Tôi không nhắc lại nữa.

– Chúng ta lần lượt xem nội dung coding của các class trong phần xử lý nghiệp vụ:

– 1) Class Goods:


package tranduythanh.com;
/**
 * Class này là class cha của Product và Catalog
 * vì Product và Catalog đều có Id và Name
 * nên Tôi tạo class này để sử dụng lại code
 * @author drthanh
 *
 */
public class Goods {
 //Id để lưu mã
 //Name để lưu tên
 private String id;
 private String name;
 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 Goods(String id, String name) {
 super();
 this.id = id;
 this.name = name;
 }
 public Goods() {
 super();
 }
 public String toString() {
 return this.id+" - "+this.name;
 }
}

– 2 – class Product:


package tranduythanh.com;
/**
 * Class này để lưu thông tin sản phẩm
 * nó kế thừ từ Goods để lấy mã và tên
 * Tôi cho nó tham chiếu tới Catalog
 * để nó có thể biết được nó thuộc danh mục nào
 * @author drthanh
 *
 */
public class Product extends Goods{
 //Lấy tham chiếu để lập trình cho lẹ
 private Catalog Dmuc;

public Catalog getDmuc() {
 return Dmuc;
 }

public void setDmuc(Catalog dmuc) {
 Dmuc = dmuc;
 }

public Product(String ma, String name, Catalog dmuc) {
 super(ma, name);
 Dmuc = dmuc;
 }

public Product(String ma, String name) {
 super(ma, name);
 }

public Product() {
 super();
 }

}

– 3 – class Catalog:


package tranduythanh.com;

import java.util.ArrayList;
/**
 * Class dùng để lưu trữ thông tin danh mục
 * và danh sách các sản phẩm thuộc danh mục
 * @author drthanh
 *
 */
public class Catalog extends Goods {
 private ArrayList<Product>listSp=null;
 public Catalog(String ma, String name) {
 super(ma, name);
 this.listSp=new ArrayList<Product>();
 }

public Catalog() {
 super();
 this.listSp=new ArrayList<Product>();
 }
 /**
 * kiểm tra sản phẩm đã tồn tại trong danh mục hay chưa
 * @param p
 * @return true nếu tồn tại
 */
 public boolean isDuplicate(Product p)
 {
 for(Product p1: listSp)
 {
 if(p1.getid().trim().equalsIgnoreCase(p.getid().trim()))
 return true;
 }
 return false;
 }
 /**
 * thêm 1 sản phẩm vào danh mục
 * thêm thành công =true
 * @param p
 * @return
 */
 public boolean addProduct(Product p)
 {
 boolean isDup=isDuplicate(p);
 if(!isDup)
 {
 p.setDmuc(this);
 return listSp.add(p);
 }
 return !isDup;
 }
 public ArrayList<Product>getListProduct()
 {
 return this.listSp;
 }
 public int size()
 {
 return listSp.size();
 }
 public Product get(int i)
 {
 return listSp.get(i);
 }
}

– 4 – class SpinnerAndListViewActivity – xử lý nghiệp vụ trong Activity:


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.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;

public class SpinnerAndListViewActivity extends Activity {

Spinner spinDm;
 EditText editma,editten;
 Button btnNhap;
 ListView lvSp;
 //cặp đối tượng dùng cho Spinner
 ArrayList<Catalog> arraySpinner=new ArrayList<Catalog>();
 ArrayAdapter<Catalog>adapterSpinner=null;
 //Cặp đối tượng dùng cho ListView
 ArrayList<Product>arrayListview=new ArrayList<Product>();
 ArrayAdapter<Product>adapterListview=null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 getWidgetsControl();
 fakeDataCatalog();
 addEventsForFormWidgets();
 }
 /**
 * Hàm lấy các control theo Id
 */
 private void getWidgetsControl()
 {
 spinDm=(Spinner) findViewById(R.id.spDanhmuc);
 editma=(EditText) findViewById(R.id.editId);
 editten=(EditText) findViewById(R.id.editName);
 btnNhap=(Button) findViewById(R.id.btnInput);
 lvSp=(ListView) findViewById(R.id.lvsanpham);

 //Cấu hình cho Spinner
 adapterSpinner=new ArrayAdapter<Catalog>(this,
 android.R.layout.simple_spinner_item,
 arraySpinner);

 adapterSpinner.setDropDownViewResource
 (android.R.layout.simple_spinner_dropdown_item);
 spinDm.setAdapter(adapterSpinner);

 //Cấu hình cho ListView
 adapterListview=new ArrayAdapter<Product>(this,
 android.R.layout.simple_list_item_1,
 arrayListview);
 lvSp.setAdapter(adapterListview);
 }
 /***
 * Hàm giả dữ liệu, tạo 3 danh mục mặc định cho Spinner
 */
 private void fakeDataCatalog()
 {
 Catalog cat1=new Catalog("1", "SamSung");
 Catalog cat2=new Catalog("2", "Iphone");
 Catalog cat3=new Catalog("3", "IPad");
 arraySpinner.add(cat1);
 arraySpinner.add(cat2);
 arraySpinner.add(cat3);
 adapterSpinner.notifyDataSetChanged();
 }
 /**
 * Hàm gán sự kiện cho Button và Spinner
 */
 private void addEventsForFormWidgets()
 {
 btnNhap.setOnClickListener(new OnClickListener() {

 @Override
 public void onClick(View arg0) {
 addProductForCatalog();
 }
 });
 spinDm.setOnItemSelectedListener(new OnItemSelectedListener() {

@Override
 public void onItemSelected(AdapterView<?> arg0, View arg1,
 int arg2, long arg3) {
 //mỗi lần chọn danh mục trong Spinner thì cập nhập ListView
 loadListProductByCatalog(arraySpinner.get(arg2));
 }

@Override
 public void onNothingSelected(AdapterView<?> arg0) {
 // TODO Auto-generated method stub

 }

 });
 }
 /**
 * Hàm thêm một sản phẩm vào cho danh mục được chọn trong Spinner
 */
 private void addProductForCatalog()
 {
 Product p=new Product();
 p.setid(editma.getText()+"");
 p.setName(editten.getText()+"");
 Catalog c= (Catalog) spinDm.getSelectedItem();
 c.addProduct(p);
 //Mỗi lần thêm xong thì cập nhập lại ListView
 loadListProductByCatalog(c);
 }
 /**
 * Lọc danh sách sản phẩm theo danh mục và update lại ListView
 * @param c
 */
 private void loadListProductByCatalog(Catalog c)
 {
 //xóa danh sách cũ
 arrayListview.clear();
 //lấy danh sách mới từ Catalog chọn trong Spinner
 arrayListview.addAll(c.getListProduct());
 //cập nhật lại ListView
 adapterListview.notifyDataSetChanged();
 }
}

– Tôi đã giải thích ở ngay bên trong mã lệnh, bạn có thể đọc và ráng hiểu coding Tôi viết bên trên.

– Bạn có thể tải code mẫu đầy đủ ở đây: http://www.mediafire.com/?i4lev5ek8944kop

– Bài tập tiếp theo bạn sẽ học AutoCompleteTextview MultiAutoCompleteTextview. 2 control này bản chất cũng giống như EditText nhưng nó hỗ trợ người sử dụng nhập dữ liệu được nhanh hơn, làm cho ứng dụng trở lên User Friendly hơn, bạn hãy chú ý theo dõi.

– Chúc các bạn thành công.

Bài tập 15: Thực hành về Spinner trong Android


– Trong bài tập này các bạn sẽ thực hành cách sử dụng Spinner.

Spinner tương tự như  ComboBox trong C#, tương tự  như JComboBox trong Java.

– Nếu bạn đã hiểu về ListView thì việc hiểu Spinner là chuyện thường.

– Cách đổ dữ liệu lên Spinner là y xì như đổ lên ListView, nó chỉ khác một chỗ duy nhất trong ArrayAdapter đó là ta phải gọi setDropDownViewResource.

– Bạn xem hình ví dụ dưới đây về Spinner:

15_spin_0– Ví dụ trên làm rất đơn giản, bạn chỉ việc kéo 2 control: TextView và Spinner vào ứng dụng (xem activity_spinner.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".SpinnerActivity" >
 <TextView
 android:id="@+id/selection"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#007380"
 android:hint="selected here"
 android:textColor="#ff003c" />
 <Spinner
 android:id="@+id/spinner1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" />
</LinearLayout>

– Ở đây Tôi đặt Id cho spinner là spinner1 (nhìn dòng lệnh 16).

– Coding SpinnerActivity.java:


package tranduythanh.com;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class SpinnerActivity extends Activity {

//Tạo một mảng dữ liệu giả
 String arr[]={
 "Hàng điện tử",
 "Hàng hóa chất",
 "Hàng gia dụng"};
 TextView selection;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_spinner);
 selection =(TextView) findViewById(R.id.selection);
 //Lấy đối tượng Spinner ra
 Spinner spin=(Spinner) findViewById(R.id.spinner1);
 //Gán Data source (arr) vào Adapter
 ArrayAdapter<String> adapter=new ArrayAdapter<String>
 (
 this,
 android.R.layout.simple_spinner_item,
 arr
 );
 //phải gọi lệnh này để hiển thị danh sách cho Spinner
 adapter.setDropDownViewResource
 (android.R.layout.simple_list_item_single_choice);
 //Thiết lập adapter cho Spinner
 spin.setAdapter(adapter);
 //thiết lập sự kiện chọn phần tử cho Spinner
 spin.setOnItemSelectedListener(new MyProcessEvent());
 }
 //Class tạo sự kiện
 private class MyProcessEvent implements
 OnItemSelectedListener
 {
 //Khi có chọn lựa thì vào hàm này
 public void onItemSelected(AdapterView<?> arg0,
 View arg1,
 int arg2,
 long arg3) {
 //arg2 là phần tử được chọn trong data source
 selection.setText(arr[arg2]);
 }
 //Nếu không chọn gì cả
 public void onNothingSelected(AdapterView<?> arg0) {
 selection.setText("");
 }
 }
}

– Bạn xem Tôi giải thích dưới này:

15_spin_1

– Bạn thấy đó android.R.layout.simple_spinner_item dùng để hiển thị phần tử bạn chọn lên spinner. Tương tự như trong ListView bạn có thể custom lại

– Dòng lệnh dưới: android.R.layout.simple_list_item_single_choice để hiện thị danh sách các phần tử trong Spinner khi bạn nhấn vào xem Spinner. Bạn phải gọi hàm setDropDownViewResource nếu không nó sẽ lỗi chương trình khi bạn nhấn vào xem. Bạn có thể dùng layout khác nhau, chẳng hạn bạn có thể thay thế bằng : android.R.layout.simple_spinner_dropdown_item

– Như vậy bạn đã làm quen được với Spinner, bạn có thể load code mẫu ở đây: http://www.mediafire.com/?1pmikmscb30po3s

– Ban đầu Tôi tính kết hợp ListView và Spinner trong bài tập này luôn, nhưng vì thấy nó hơi phức tạp nên Tôi đã tách ra một bài tập thực hành riêng, trong bài tập tới bạn sẽ học phần này.

– Chúc bạn thành công

Bài tập 14: Thực hành về Custom Layout cho ListView trong Android


bài tập 13 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ó:


<?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:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 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:


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:

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:


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://www.mediafire.com/?16tw6xf55nivr8k

– 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