Android初級開發----用Handle機制更新UI介面
阿新 • • 發佈:2018-12-31
- 引入:
之前對Android講義的一個小專案——–隨指尖移動的小球進行修改,利用View中的invalidate()方法實現每次觸碰螢幕更新(UI執行緒)小球的位置並且附加判斷大小,實現觸碰更新小球大小隨之更改顏色.
API引入:
每次執行invalidate()方法都會呼叫ondraw()
ondraw()用於重新整理介面此處為canvas(畫布)
2. 簡單原始碼:
變色小球1.0
新建一個DrawView類繼承View
package com.example.drawview_without_thread;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
public class DrawView extends View{
public float currentX = 200 ; //球起始位置
public float currentY = 500;
// String touchsign = "Never touch";
boolean range = true; //大小判斷標記
Paint p = new Paint(); //畫筆
public DrawView(Context context) { //建構函式1 直接定義檢視 //final DrawView draw = new DrawView(this);
super(context);
}
public DrawView(Context context,AttributeSet set) { //建構函式2 傳入AttributeSet set在佈局檔案xml中載入屬性
super(context,set);
}
int [] colors = new int[]{
Color.RED,
Color.BLUE,
Color.GREEN,
Color.MAGENTA,
Color.YELLOW,
};
int size = 10; //初始半徑10
static int colornum = 0; //大小,顏色下標序號
@Override
public void onDraw(Canvas canvas){ //canvas 畫布
super.onDraw(canvas);
if (size >= 350) {
range = false; //大小判斷
}if(size <= 20) {
range = true;
}
//半徑視情況 改變
if (range) {
size += 15;
Log.d("DrawTest", "半徑遞增:"+ size );
}
else {
size -= 15;
Log.d("DrawTest", "半徑遞減:"+ size );
}
colornum = ++colornum % colors.length; //每次觸碰滾動換色
p.setColor(colors[colornum]);
Log.d("Color", "新畫筆顏色"+colornum);
//設定位置,大小,顏色
canvas.drawCircle(currentX, currentY, size, p); //設定點的半徑和位置
}
@Override
//touch reaction
public boolean onTouchEvent(MotionEvent event){
currentX = event.getX();
currentY = event.getY();
//touchsign = "touched"; //改變表示已經觸碰
invalidate(); //用來重新整理View,系統會自動回撥 View的onDraw()方法
// 只要是view的子類,都會從view中繼承invalidate和postInvalidate這兩個方法。
//
// 當invalidate方法被呼叫的時候,就是在告訴系統,當前的view發生改變,應該儘可能快的來進行重繪。
//
// 這個方法僅能在UI(主)執行緒中被呼叫。如果想要在工作執行緒中進行重新整理介面,那麼其他的方法將會被呼叫,這個方法就是postInvalidate方法。
return true;
}
}
為了讓每次點選都改變小球位置,通過一個顏色陣列colors來獲取當前週期性改變的顏色,於是我想為什麼不額外建立一個執行緒實現改變顏色呢?
問題引入:
Android制訂了一條規則:它只允許(主)User Interface執行緒修改Activity裡的UI元件,當程式第一次啟動,Main Thread主要負責處理與UI相關的事件,如按鈕和使用者螢幕點選事件.
- 引用Handler類
官方文件解釋
Handler
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Handler : 解決,處理.這裡可以理解為訊息處理器.
大概的意思是,一個Handler可以讓你傳送訊息,這個訊息會推入MessageQueue(訊息佇列)裡等待處理. - MessageQueue管家——–Looper
Looper類中的loop()方法會不斷的取出訊息佇列的Message傳送給Handler,並且將Message傳遞給Handler的handleMessage()方法中. - handleMessage() 常用結構:
大概流程可以參考此圖:
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) //接受通過Looper從MessageQueue佇列提取對應傳送過來的訊息.
{
if (msg.what == 0x123) {
//新執行緒要做的事...
}
super.handleMessage(msg);
}
};
// 激發事件
@Override
public void run() {
Log.d("Sent", "向處理器傳送訊息");
handler.sendEmptyMessage(0x123);
}
}).start();
通過對msg的判斷新訊息是否符合該處理操作,如果符合則對應處理.
所以利用額外的一個執行緒實現週期性改變,如今不用每次點選螢幕只要啟動執行緒就會在原地週期改變顏色.注意的是,我們不是新建一個執行緒來改變UI介面,而是新建一個執行緒來發送訊息給Handler,讓位於主執行緒的Handler更新介面
變色小球2.0
修改DrawView的部分程式碼,並且在MainActivity新建Handler處理器,相關程式碼如下:
修改DrawView:
@Override
public void onDraw(Canvas canvas){ //canvas 畫布
super.onDraw(canvas);
if (size >= 350) {
range = false; //大小判斷
}if(size <= 20) {
range = true;
}
//半徑視情況 改變
if (range) {
size += 15;
Log.d("DrawTest", "半徑遞增:"+ size );
}
else {
size -= 15;
Log.d("DrawTest", "半徑遞減:"+ size );
}
Log.d("Color", "新畫筆顏色"+colornum);
//設定位置,大小,顏色
canvas.drawCircle(currentX, currentY, size, p); //設定點的半徑和位置
}
public void thread_change() {//通過UI執行緒的handler來呼叫
colornum = ++colornum % colors.length; //週期性滾動換色
p.setColor(colors[colornum]);
Log.d("DrawTest", "新執行緒重設畫筆顏色");
invalidate(); //加入了原地更新介面的方法
}
修改MainActivity:
加入Handler
//處理器
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) //接受通過Looper從MessageQueue佇列提取對應傳送過來的訊息.
{
if (msg.what == 0x123) {
draw.thread_change();
}
super.handleMessage(msg);
}
};
//按鈕
Button shine = (Button)findViewById(R.id.shine);
shine.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d("DrawTest", "點選按鈕");
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Log.d("DrawTest", "向處理器傳送訊息");
handler.sendEmptyMessage(0x123);
}
}, 0, 500); //新執行緒用timer週期性的向handler傳送訊息,此處也可以通過start()來啟動新執行緒
}
});
此時handleMessage()就是在主執行緒執行的,所以可以進行UI操作.
參考來源:<瘋狂android講義>p208,<第一行程式碼>p348