Android總結Handler異步更新UI界面(轉載)

轉載地址:https://blog.csdn.net/qq_21004057/article/details/51582412

本篇文章通過三種方式來實現UI控件的更新,Handler異步更新UI在安卓開發中最常用也非常實在。這篇文章注重實現思路,所以我就不在界面方面進行美化了,都是最原始的控件。有需要的可以收藏下,。雖說搜索引擎上關於Handler消息機制的文章已經數不盡數了,但是我寫這篇文章也是希望在開發中能幫助自己記憶起Handler的用法。

學會使用Handler來更新UI,由於在主線程中直接更新UI會阻塞線程,造成假死現象,所以我們通常採用Handler消息機制在UI線程中來更新UI控件。至於Handler消息機制,在這裏簡單介紹一下。本來還打算寫一種的,這裏就不詳細說了,通過在子線程使用Bundle封裝屬性到Message數據中,其次在Handler中解封裝得到Message數據再顯示到控件中。其原理與方法三無太大差別。

Handler消息機制原理簡介:通過Handler對象向消息隊列中Message Queue中發送消息Message,通過Looper對象來管理Queue中的Message。具體的大家可以查看Handler的源碼。

好了,看到我們的效果圖,三種方式實現的最終效果一致。

項目UI界面實現:3個Button,1個EditText,1個TextView。

項目實現原理:Handler機制實現UI更新。

項目邏輯實現:通過點擊按鈕獲取輸入框的時間並顯示在一個TextView上,然後通過點擊開始計時按鈕開始倒計時,可以通過停止計時按鈕停止計時。

實現方式一:Handler+Timer+TimerTask


通過該方式也是比較實用的,顧名思義,TimerTask計時器任務。由於Timer和TimerTask是同時出現的,TimerTask實現了Runnable接口,並且要求實現run方法。

首先,我們先編寫我們的佈局文件activity_main.xml,三種實現方式統一使用了該佈局。

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <EditText 
        android:id="@+id/inputTime"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:hint="@null"/>
    <Button
        android:id="@+id/ensureTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點擊" />
 
    <TextView
        android:id="@+id/showTime"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
 
    <Button
        android:id="@+id/startTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="開始計時" />
    <Button
        android:id="@+id/stopTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止計時" />
    
    
</LinearLayout>

接下來就是我們的Activity實現步驟了。三種方式實現代碼如下:

MainActivity.java

package com.mero.countTime;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
    private EditText inputTime;
    private TextView showTime;
    private Button ensureTime,startTime,stopTime;
    private Timer timer = null;
    private TimerTask task = null;
    private int i;//顯示的倒計時數字
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//實例化控件
    }
 
        /**實例化控件方法*/
 
    private void initView() {
        inputTime = (EditText) findViewById(R.id.inputTime);
        showTime = (TextView) findViewById(R.id.showTime);
        ensureTime = (Button) findViewById(R.id.ensureTime);
        startTime = (Button) findViewById(R.id.startTime);
        stopTime = (Button) findViewById(R.id.stopTime);
 
        /**註冊監聽事件*/
        ensureTime.setOnClickListener(this);
        startTime.setOnClickListener(this);
        stopTime.setOnClickListener(this);
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
    /**當選擇點擊按鈕的監聽事件*/
        case R.id.ensureTime:
            showTime.setText(inputTime.getText().toString());
            i=Integer.parseInt(inputTime.getText().toString());
            break;
    /**當選擇開始計時按鈕的監聽事件*/
 
        case R.id.startTime:
            startTime();
            break;
        case R.id.stopTime:
            stopTime();
            break;
        }
    }    
    /**當選擇停止計時按鈕的監聽事件*/
 
    private Handler handler=new Handler(){
    /**重寫handleMessage方法*/
        @Override
        public void handleMessage(Message msg) {
            showTime.setText(msg.arg1+"");
            startTime();//執行計時方法
        }
    };
    /**開始計時方法*/
 
    private void startTime(){
        timer = new Timer();
        task = new TimerTask() {
            @Override
            public void run() {
                i--;
                Message message = handler.obtainMessage();//獲取Message對象
                message.arg1 = i;//設置Message對象附帶的參數
                handler.sendMessage(message);//向主線程發送消息
            }
        };
        timer.schedule(task, 1000);//執行計時器事件
    };
    /**停止計時方法*/
 
    private void stopTime(){
        timer.cancel();//註銷計時器事件
    };  
}

實現方式二:Handler+postDelayed+post
MainActivity.java

package com.mero.countTime;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
    private EditText inputTime;//聲明輸入框
    private TextView showTime;//聲明用於顯示當前計時的時間
    private Button ensureTime,startTime,stopTime;//聲明計時按鈕,停止計時按鈕和點擊按鈕
    private int i;//顯示的倒計時數字
    private Runnable update;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//實例化控件
    }
 
    /**實例化控件方法*/
 
    private void initView() {
        inputTime = (EditText) findViewById(R.id.inputTime);
        showTime = (TextView) findViewById(R.id.showTime);
        ensureTime = (Button) findViewById(R.id.ensureTime);
        startTime = (Button) findViewById(R.id.startTime);
        stopTime = (Button) findViewById(R.id.stopTime);
        /**註冊監聽事件*/
        ensureTime.setOnClickListener(this);
        startTime.setOnClickListener(this);
        stopTime.setOnClickListener(this);
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.ensureTime:
            showTime.setText(inputTime.getText().toString());//獲取輸入框上的時間並設置到顯示文本控件上
            i=Integer.parseInt(inputTime.getText().toString());
            break;
        case R.id.startTime:
            startTime();//開始計時
            handler.post(update);
            break;
        case R.id.stopTime:
            stopTime();//停止計時
            break;
        }
    }
    final Handler handler=new Handler();
 
    /**開始計時方法*/
    
    private void startTime(){
        update=new Runnable(){
            @Override
            public void run() {
                i--;
                showTime.setText(i+"");
                handler.postDelayed(update, 1000);//每隔1s將線程提交到線程隊列中
            }    
        };
    }
 
    /**停止計時方法*/
    
    private void stopTime(){
        handler.removeCallbacks(update);//移除Runnable對象
    };  
}

實現方式三:Handler+Thread
MainActivity.java

package com.mero.countTime;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
    private EditText inputTime;
    private TextView showTime;
    private Button ensureTime,startTime,stopTime;
    private int i;//顯示的倒計時數字
    private boolean flag;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//實例化控件
    }
    private void initView() {
        inputTime = (EditText) findViewById(R.id.inputTime);
        showTime = (TextView) findViewById(R.id.showTime);
        ensureTime = (Button) findViewById(R.id.ensureTime);
        startTime = (Button) findViewById(R.id.startTime);
        stopTime = (Button) findViewById(R.id.stopTime);
 
        /**註冊監聽事件*/
 
        ensureTime.setOnClickListener(this);
        startTime.setOnClickListener(this);
        stopTime.setOnClickListener(this);
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
 
        /**點擊按鈕事件監聽*/
 
        case R.id.ensureTime:
            showTime.setText(inputTime.getText().toString());
            i=Integer.parseInt(inputTime.getText().toString());
            break;
 
        /**開始按鈕事件監聽*/
 
        case R.id.startTime:
            flag=true;
            startTime();
            break;
 
        /**停止按鈕事件監聽*/
 
        case R.id.stopTime:
            stopTime();
            break;
        }
    }
    final Handler handler=new Handler(){
        public void handleMessage(Message msg) {
            int p=msg.what;
            showTime.setText(p+"");
        };
    };
    /**開始計時方法*/
    private void startTime() {
 
        /**開啓一個新線程*/
 
        new Thread(){
                    public void run() {
    
    /**每睡眠1秒後發送Message給Handler處理*/
 
                    for(int j=i;j>=0;j--){
                        if(flag==true){
                            try {
                                Thread.sleep(1000);
                                Message msg=new Message();
                                msg.what=j;//設置Message附帶的參數
                                handler.sendMessage(msg);//發送Message對象給Handler
                                i=j;//將當前的時間傳遞給全局時間變量
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                        
                    }
                }
            }.start();
    }
    
    /**停止計時方法,通過設置boolean標誌爲false來停止*/
    
    @SuppressLint("NewApi")
    private void stopTime(){
        flag=false;
    };  
}


通過上面的代碼,我們來總結一下。


方法一:簡單實用,尤其定時刷新控件,效果非常good,使用簡單。注意Timer和TimerTask必須同時使用。使用Timer的schedule(task,delayed)方法提交TimerTask線程消息。由Handler處理線程消息。通過Timer.cancel(task)方式進行移除線程任務。

方法二:同方式一,簡單實用,原理實質一致。通過實例化Runnable對象來構造實現run創建新線程,在新線程中不斷將線程加入Looper池中進行處理。在主線程中通過post提交線程進行處理。通過handler.removeCallbacks(runnable)方式移除線程任務。

方法三:很經典實用的一種方式。通過for循環加上線程睡眠不斷創建新消息。缺點是不易於控制,本文通過標誌進行控制。

好了,本篇文章就到此結束了。如果還有什麼問題的話,可以在下面留言。大家共同討論共同進步。謝謝閱讀 !
--------------------- 
作者:Mero技術博客 
來源:CSDN 
原文:https://blog.csdn.net/qq_21004057/article/details/51582412 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章