1. 程式人生 > >Android總結Handler非同步更新UI介面(轉載)

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 
版權宣告:本文為博主原創文章,轉載請附上博文連結!