Android – Một số View cơ bản – Phần 2

Rate this post

Trong phần này chúng ta tiếp tục tìm hiểu một số lớp View cơ bản là ProgressBarListView.

ProgressBar

ProgressBar hiển thị một thanh ngang hoặc một hình tròn biểu diễn tiến trình hoạt động của một hành động nào đó.

Ví dụ 1:

Chúng ta sẽ thiết kế một ProgressBar  và một TextView hiển thị tiến trình hoạt động theo %.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent">
    <ProgressBar 
       android:id="@+id/pbId" 
       android:layout_width="fill_parent" 
       android:layout_height="wrap_content" 
       style="?android:attr/progressBarStyleHorizontal" 
       android:layout_margin="10dp" />       
    <TextView 
        android:id="@+id/tvId" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:layout_margin="10dp" />
</LinearLayout>

Trong file main.xml chúng ta thiết kế một thẻ ProgressBar và một TextView. Thuộc tính style quy định kiểu “vẽ” của ProgressBar lên màn hình, ở đây progressBarStyleHorizontal nghĩa là vẽ kiểu thanh ngang. Mặc định thì ProgressBar sẽ vẽ theo kiểu hình tròn.

package com.phocode;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.util.Log;

public class MainActivity extends Activity
{
    private ProgressBar pb;
    private TextView tv;
    private int prg = 0;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        pb = (ProgressBar) findViewById(R.id.pbId);
        tv = (TextView) findViewById(R.id.tvId);
 
        new Thread(myThread).start();
    }

    private Runnable myThread = new Runnable()
    { 
        @Override
        public void run() 
        {
            while (prg < 100)
            {
                try
                {
                    hnd.sendMessage(hnd.obtainMessage());
                    Thread.sleep(100);
                }
                catch(InterruptedException e) 
                {  
                    Log.e("ERROR", "Thread was Interrupted");
                }
            }

            runOnUiThread(new Runnable() { 
                public void run() {
                    tv.setText("Finished");
                }
            });          
        }
    
        Handler hnd = new Handler()
        {    
            @Override
            public void handleMessage(Message msg) 
            {
                prg++;
                pb.setProgress(prg);

                String perc = String.valueOf(prg).toString();
                tv.setText(perc+"% completed");
            }
        };
    };
}

Chúng ta sẽ điều khiển tiến trình hoạt động của ProgressBar trong một luồng khác.

new Thread(myThread).start();

Ở đây chúng ta sử dụng một luồng khác để thực hiện công việc tính toán, còn luồng chính sẽ cập nhật ProgressBar, lý do sử dụng 2 luồng khác nhau là để chúng có thể chạy song song với nhau.

@Override
public void run() 
{
    while (prg < 100)
    {
        try
        {
            hnd.sendMessage(hnd.obtainMessage());
            Thread.sleep(100);
        }
        catch(InterruptedException e) 
        {  
            Log.e("ERROR", "Thread was Interrupted");
        }
    }

    runOnUiThread(new Runnable() { 
        public void run() {
            tv.setText("Finished");
        }
    });          
}

Công việc tính toán ở đây chỉ đơn giản là tăng giá trị của một biến từ 0 đến 100, mỗi lần tăng thì nghỉ 100 mili giây.

Cơ chế của hệ điều hành Android có hơi khác các hệ điều hành khác là những biến nào được khai báo trong một Activity chỉ có thể truy xuất được trong Activity đó, biến myThread tuy thuộc lớp Activity chính nhưng thực chất nó chạy một luồng của riêng nó, không đụng chạm gì tới Activity chính cả, do đó chúng ta không thể thao tác trực tiếp với các biến prg hay biến TextView tv bên trong phương thức run() được. Để có thể thao tác với các biến của một đối tượng Activity từ một luồng khác, chúng ta phải gọi chúng bên trong phương thức runOnUiThread() hoặc bên trong một đối tượng Handler.

runOnUiThread(new Runnable() { 
    public void run() {
        tv.setText("Finished");
    }
});   

Biến thuộc luồng nào thì chỉ có thể truy xuất được từ luồng đó, ở đây chúng ta chỉnh sửa biến tv nên chúng ta truy xuất nó bên trong phương thức runOnUiThread().

Handler hnd = new Handler()
{    
    @Override
    public void handleMessage(Message msg) 
    {
        prg++;
        pb.setProgress(prg);

        String perc = String.valueOf(prg).toString();
        tv.setText(perc+"% completed");
    }
};

Như đã nói, ngoài cách dùng phương thức runOnUiThread(), chúng ta cũng có thể truy xuất các biến đó thông qua một đối tượng Handler. Ở đây chúng ta cập nhật biến prg và thiết lập giá trị của biến đó cho ProgressBar.

Screenshot_2016-05-21-16-00-23

Ví dụ 2:

Trong ví dụ này, chúng ta sẽ hiển thị ProgressBar ở dạng hình tròn.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" >
    <ProgressBar 
        android:id="@+id/pbId" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"/>    
    <TextView 
        android:id="@+id/tvId" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="Please wait..." />        
</LinearLayout>

Ở đây chúng ta không thiết lập biến style cho ProgressBar nữa và như thế ProgressBar sẽ hiển thị hình tròn thay vì thanh ngang. Ngoài ra TextView cũng không hiển thị số % tiền trình hoạt động nữa nên chúng ta thiết lập “cứng” thuộc tính text luôn.

package com.phocode;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.View;
import android.util.Log;

public class MainActivity extends Activity
{
    private ProgressBar pb;
    private TextView tv;
    private int prg = 0;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        pb = (ProgressBar) findViewById(R.id.pbId);
        tv = (TextView) findViewById(R.id.tvId);
 
        new Thread(myThread).start();
    }

    private Runnable myThread = new Runnable()
    { 
        @Override
        public void run() 
        {
            while (prg < 100)
            {
                try
                {
                    hnd.sendMessage(hnd.obtainMessage());
                    Thread.sleep(100);
                }
                catch(InterruptedException e) 
                {  
                    Log.e("ERROR", "Thread was Interrupted");
                }
            }

            runOnUiThread(new Runnable() { 
                public void run() {
                    tv.setText("Finished");
                    pb.setVisibility(View.GONE); 
                }
            });          
        }
    
        Handler hnd = new Handler()
        {    
            @Override
            public void handleMessage(Message msg) 
            {
                prg++;
                pb.setProgress(prg);
            }
        };
    };
}

Đoạn code trong file MainActivity.java cũng tương tự như ví dụ trước.

runOnUiThread(new Runnable() { 
    public void run() {
        tv.setText("Finished");
        pb.setVisibility(View.GONE); 
    }
}); 

Chỉ khác là ở đây chúng ta không cập nhật text cho TextView theo % mà chỉ canh khi nào tiến trình hoàn tất thì mới cập nhật thôi. Ngoài ra sau khi hoàn tất công việc, chúng ta cho ẩn ProgressBar đi bằng phương thưc setVisibility().

Screenshot_2016-05-21-15-06-18

ListView

ListView hiển thị dữ liệu dưới dạng một danh sách các item, chúng ta có thể dùng tay cuộn danh sách lên xuống nếu danh sách quá dài. ListView sử dụng dữ liệu được cung cấp bởi lớp Adapter.

Ví dụ 1:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent">  
     
    <ListView 
        android:id="@+id/lvId" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" />  
    
</LinearLayout> 

Trong file main.xml chúng ta khai báo một thẻ ListView.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:padding="10dp" android:textSize="20sp">
</TextView>

File row.xml sẽ định nghĩa cách các hàng trong ListView được hiển thị như thé nào, ví dụ như các hàng có độ lớn bao nhiêu, kích cỡ chữ bao nhiêu. Ở đây sp là đơn vị dùng cho kích thước font trong Android.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">MainActivity</string>
    <string-array name="languages">
        <item>Python</item>
        <item>Java</item>
        <item>Ruby</item>
        <item>C++</item>
    </string-array>        
</resources>

Trong file strings.xml chúng ta định nghĩa các item sẽ được dùng cho ListView trong biến string-array.

package com.phocode;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;  
import android.widget.ListView;  

public class MainActivity extends Activity
{
    private ListView lv;  
    private ArrayAdapter<String> la; 

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        setupUI();
    }

    public void setupUI()
    { 
        lv = (ListView) findViewById(R.id.lvId);  
        String[] languages = getResources().getStringArray(R.array.languages); 
        lv.setAdapter(new ArrayAdapter<String>(this, R.layout.row, languages));
    }
}

Trong file MainActivity.java chúng ta kết nối tất cả những thứ trên lại với nhau.

String[] languages = getResources().getStringArray(R.array.languages);

Dòng code trên sẽ lấy dữ liệu của các item trong file strings.xml đưa vào mảng languages.

lv.setAdapter(new ArrayAdapter<String>(this, R.layout.row, languages));

Lớp ArrayAdapter có nhiệm vụ kết nối file row.xml, dữ liệu trong mảng languages vào đối tượng ListView.

Screenshot_2016-05-21-15-23-13

Ví dụ 2:

Trong ví dụ này chúng ta sử dụng lớp ListActivity thay cho lớp Activity thường. Lớp ListActivity là lớp Activity nhưng có sẵn một đối tượng ListView. Bởi vì ListView rất thường được dùng để hiển thị riêng trong một màn hình Activity, do đó Android cho ra đời lớp ListActivity để đơn giản hóa việc thiết kế cho các coder. Cũng chính vì vậy ở đây chúng ta cũng sẽ không dùng đến file main.xml để thiết kế.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:padding="10dp" android:textSize="20sp">
</TextView>

File row.xml định nghĩa cách ListView hiển thị, giống như ví dụ trước.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">MainActivity</string>
</resources>

File strings.xml chỉ lưu tiêu đề của ứng dụng chứ không còn lưu danh sách các item nữa.

package com.phocode;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;  
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ListView;  
import android.widget.TextView;

public class MainActivity extends ListActivity 
     implements OnItemClickListener, OnItemSelectedListener
{       
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
      
        ArrayAdapter<String> la = new ArrayAdapter<String>(this, R.layout.row);  
        la.add("Python");
        la.add("Java");
        la.add("Ruby");
        la.add("C++");

        setListAdapter(la);

        ListView lv = getListView();
        lv.setOnItemClickListener(this);
        lv.setOnItemSelectedListener(this);
    }

    public void onItemClick(AdapterView<?> parent, View view,
        int position, long id) 
    {        
        String language = ((TextView) view).getText().toString();
        setTitle(language);
    }

    public void onItemSelected(AdapterView<?> parent, View view,
        int position, long id) 
    {        
        String language = ((TextView) view).getText().toString();
        setTitle(language);
    }

    public void onNothingSelected(AdapterView<?> parent) 
    {        
       
    }
}

Thay vào đó chúng ta sẽ thiết kế các item cho ListView trong Java. Ngoài ra chúng ta bắt sự kiện click vào item trên ListView nữa.

public class MainActivity extends ListActivity 
     implements OnItemClickListener, OnItemSelectedListener

Lớp MainActivity giờ đây sẽ kế thừa lớp ListActivity và implement 2 giao diện là OnItemClickListenerOnItemSelectedListener, tổng cộng có 3 phương thức trừu tượng cần được code.

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    ...
}

Ở đây phương thức onCreate() sẽ không có dòng setContentView() vì mặc định lớp ListActivity đã có sẵn View của riêng nó là ListView rồi, ListView này có kích thước phủ toàn bộ màn hình.

ArrayAdapter<String> la = new ArrayAdapter<String>(this, R.layout.row);  
la.add("Python");
la.add("Java");
la.add("Ruby");
la.add("C++");

Chúng ta tạo đối tượng ArrayAdapter như bình thường để truyền dữ liệu vào ListView.

setListAdapter(la);

Phương thức setListAdapter() sẽ gắn dữ liệu của đối tượng Adapter với đối tượng ListView.

ListView lv = getListView();
lv.setOnItemClickListener(this);
lv.setOnItemSelectedListener(this);

Chúng ta thiết lập các listener cho ListView.

public void onItemClick(AdapterView<?> parent, View view,
    int position, long id) 
{        
    String language = ((TextView) view).getText().toString();
    setTitle(language);
}

Giao diện OnItemClickListener có phương thức trừu tượng là onItemClick(), ở đây chúng ta làm công việc là thiết lập đoạn text trên thanh tiêu đề là text của item được click.

public void onItemSelected(AdapterView<?> parent, View view,
    int position, long id) 
{        
    String language = ((TextView) view).getText().toString();
    setTitle(language);
}

public void onNothingSelected(AdapterView<?> parent) 
{        
    
}

Giao diện OnItemSelectedListener có 2 phương thức trừu tượng, tuy nhiên chúng ta chỉ code phương thức onItemSelected() thôi, ở đây chúng ta cũng chỉ đơn giản là thiết lập lại tiêu đề của ứng dụng bằng tiêu đề của item được chọn trong ListView.

Screenshot_2016-05-21-15-47-17
0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments