在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时,要记住一点,代码千变万化,万变不离其宗,代码稍微一改就会不一样,但其调用的实质是一样的。希望这篇博客能给大家一点帮助,若有解释不合理的地方,希望大家提出宝贵的意见。