在Android實際開發中,我們常常需要進行View的刷新(例如:自定義控件等),Android中爲我們提供了兩組刷新View的方法—-Invalidate和postInvalidate,前者是在UI線程中更新View,而後者是在非UI線程中更新VIew,兩組方法的具體區別如下介紹:
我們都知道,在Android中,更新UI的操作要在UI線程(也就是主線程)中進行,因爲Android UI操作並不是線程安全的,但是這些操作必須在UI線程中調用(單線程模式,這裏就不在詳細介紹了,可以自己上網查閱)。
Invalidate()方法:
對於初學者來說,我們常用的更新UI的方式不外乎就是利用Handler機制,在工作線程中執行耗時操作等,然後發消息給UI線程,進行UI操作。以下我們以自定義控件來介紹該用法(自定義控件,實現一個圓環不停的刷新大小,由小變大):
自定義控件,繪製一個圓環,並更新其大小“`
public class MyView extends View {
/**
* 畫筆
*/
private Paint mPaint ;
/**
* 自定義控件半徑
*/
private int radiu ;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化畫筆
initPaint() ;
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 初始化畫筆方法
*/
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
mPaint.setStyle(Style.STROKE) ;
mPaint.setColor(Color.RED) ;
mPaint.setStrokeWidth(20) ;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 執行繪製
canvas.drawCircle(150, 150, radiu, mPaint) ;
}
public synchronized void setRadiu(int radiu) {
this.radiu = radiu ;
// 刷新,即重新繪製
invalidate() ;
}
}
然後在MainActivity中:
public class MainActivity extends Activity {
/**
* 自定義控件對象
*/
private MyView myView ;
/**
* 自定義控件的半徑
*/
private int radiu ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView = (MyView) findViewById(R.id.myview) ;
// 開啓線程
new Thread(new Runnable() {
@Override
public void run() {
try {
// 死循環,不停的刷新View
while(true) {
// 當半徑小於200時,開始自增
if(radiu <= 200) {
radiu += 10 ;
// 發送消息給Handler處理
mHandler.obtainMessage().sendToTarget() ;
} else {
radiu = 0 ;
}
// 每隔40毫秒睡一次
Thread.sleep(40) ;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start() ;
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 設置自定義控件的半徑
myView.setRadiu(radiu) ;
};
} ;
protected void onDestroy() {
super.onDestroy() ;
// 界面銷燬後移除Handler的引用
mHandler.removeCallbacksAndMessages(null) ;
};
}
以上這種方式即爲在非UI線程中執行邏輯代碼,然後利用Handler機制發送消息通知UI線程進行更新UI的操作。
postInvalidate()方法:
而postInvalidate()可以直接在非UI線程中刷新,不需要使用Handler機制,代碼邏輯也相對比較簡單:
public class MyView extends View implements Runnable {
/**
* 畫筆
*/
private Paint mPaint ;
/**
* 自定義控件半徑
*/
private int radiu ;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化畫筆
initPaint() ;
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 初始化畫筆方法
*/
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
mPaint.setStyle(Style.STROKE) ;
mPaint.setColor(Color.RED) ;
mPaint.setStrokeWidth(20) ;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 執行繪製
canvas.drawCircle(150, 150, radiu, mPaint) ;
}
@Override
public void run() {
while(true) {
try {
if(radiu <= 150) {
radiu += 10 ;
postInvalidate() ;
} else {
radiu = 0 ;
}
Thread.sleep(40) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在MainActivity中,開啓線程更新View:
public class MainActivity extends Activity {
/**
* 自定義控件對象
*/
private MyView myView ;
/**
* 自定義控件的半徑
*/
private int radiu ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView = (MyView) findViewById(R.id.myview) ;
// 開啓線程刷新界面
new Thread(myView).start() ;
}
以上兩種方式,是我在利用自定義控件時使用的,可能對於不熟悉自定義控件的初學者來說看着會有點亂,其實仔細閱讀以下,就會發現,並不是那麼難,第一種方式中,我們選擇利用Handler機制來處理,利用消息機制來通知UI線程更新View操作,所以,調用Invalidate()的方法(即setRadiu())是運行在UI線程的,對於第二種方法,我們將更新View的方法放在了一個子線程中(即Runnable接口的run方法中),然後在調用該自定義控件時開啓一個線程來操作,所以postInvalidate()方法是運行在子線程的。
我們在學習Android時,要記住一點,代碼千變萬化,萬變不離其宗,代碼稍微一改就會不一樣,但其調用的實質是一樣的。希望這篇博客能給大家一點幫助,若有解釋不合理的地方,希望大家提出寶貴的意見。