Android程序中可以使用的界面刷新方法有两种,分别是利用Handler与Invalidate()的组合和利用postInvalidate()来实现在线程中刷新界面。
每一次调用他们,他们就会自动去执行View中的OnDraw()一次,从而达到刷新界面的效果,所以如果你想不断的刷新界面则最好是把调用此两个方法的语句放在一个Thread中,判断这线程是否已经中断while (!Thread.currentThread().isInterrupted()) {},没有不断的执行调语句来刷新界面
下面是网络一篇关于介绍此问题的一文import
android.app.Activity;
004 | import android.os.Bundle; |
005 | import android.os.Handler; |
006 | import android.os.Message; |
007 | import android.view.KeyEvent; |
008 | import android.view.MotionEvent; |
010 | public class
Activity01 extends
Activity |
012 | private
static final
int REFRESH = 0x000001 ; |
015 | private
GameView mGameView = null ; |
017 | /** Called when the activity is first created. */ |
019 | public
void onCreate(Bundle savedInstanceState) |
021 | super .onCreate(savedInstanceState); |
024 | this .mGameView =
new GameView( this ); |
027 | setContentView(mGameView); |
030 | new
Thread( new
GameThread()).start(); |
066 | class
GameThread implements
Runnable |
070 | while
(!Thread.currentThread().isInterrupted()) |
076 | catch
(InterruptedException e) |
078 | Thread.currentThread().interrupt(); |
081 | mGameView.postInvalidate(); |
116 | public
boolean onTouchEvent(MotionEvent event) |
122 | public
boolean onKeyDown( int
keyCode, KeyEvent event) |
128 | public
boolean onKeyUp( int
keyCode, KeyEvent event) |
133 | case
KeyEvent.KEYCODE_DPAD_UP: |
137 | case
KeyEvent.KEYCODE_DPAD_DOWN: |
144 | public
boolean onKeyMultiple( int
keyCode, int
repeatCount, KeyEvent event) |
为了弄清这问题我自己测试一些问题,下面是问题的总结,方便他日参考:
class Threadss implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while (!Thread.currentThread().isInterrupted()) {
Message smg=new Message();
smg.what=1;
MainActivity.this.handle.sendMessage(smg);
Log.i("Threadss->", "run");
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
public class MyGraphics extends View implements Runnable{ //自定义View
private Paint paint=null; //声明画笔对象
public MyGraphics(Context context) {
super(context);
// TODO Auto-generated constructor stub
paint=new Paint(); //构建对象
// new Thread(this).start(); //开启线程
}
@Override
protected void onDraw(Canvas canvas) { //重载onDraw方法
// TODO Auto-generated method stub
super.onDraw(canvas);
paint.setColor(Color.RED); //设置画笔颜色
canvas.drawColor(Color.WHITE);
canvas.drawLine(50, 50, 450, 50, paint); //绘制直线
canvas.drawRect(100, 100, 200, 600, paint); //绘制矩形
canvas.drawRect(300, 100, 400, 600, paint); //绘制矩形
Log.i("----------------", "+++++++++++");
}
@Override
public void run() { //重载run方法
// TODO Auto-generated method stub
while(!Thread.currentThread().isInterrupted())
{
try
{
Thread.sleep(100);
}
catch(InterruptedException e)
{
Thread.currentThread().interrupt();
}
postInvalidate(); //更新界面
}
}
public void test(){
postInvalidate();
Log.i("----------test-----", "+++++++++++");
}
}
public void onCreate(Bundle savedInstanceState) { 这是测试方案一
super.onCreate(savedInstanceState);
setContentView(new MyGraphics(this));
new Thread(new Threadss()).start();
handle=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
new MyGraphics(MainActivity.this).invalidate();
}
};
我这样也是能过一个线程不断的发送信息(log信息显示是在不停的打印Threadss->", "run"),接收信息,来不断刷新界面,但是运行结果却没有达到这个效果,它只执行了一次onDraw()方法,并没有不断的执行此ondraw方法,而执行ondraw方法是在setContentView(new MyGraphics(this));这里执行的,那为什么没在执行下面 new MyGraphics(MainActivity.this).invalidate();来刷新界面了,刚开始我也是弄了很久,到弄了很久才想清楚出现此问题的原因,这原因是类的创建对象有关,这两句话的都是各自创建一个MyGraphics实例对象,他们并不是同一个对象,所以不会达到不断执行,如果要不断的刷新界面的正解写法是:
private MyGraphics mGameView;
public void onCreate(Bundle savedInstanceState) { 这是测试方案一
super.onCreate(savedInstanceState);
mGameView=new mGameView(this);
setContentView(mGameView);
new Thread(new Threadss()).start();
handle=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
mGameView.invalidate();
}
};
方案二:
private MyGraphics mGameView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGameView=new MyGraphics(this);
setContentView(mGameView);
mGameView.test();
}或是
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyGraphics(this));
new MyGraphics(this).test();
}
通过调用类的另一个方法,在另一个方法中刷新界面(写postInvalidate(); ),它都会去执行OnDraw()一次,从而达到刷新界面的效果 ,打印的结果都是
07-25 02:26:51.123: I/----------test-----(477): +++++++++++
07-25 02:26:51.323: I/----------------(477): +++++++++++
方案三:
@Override
protected void onDraw(Canvas canvas) { //重载onDraw方法
// TODO Auto-generated method stub
super.onDraw(canvas);
paint.setColor(Color.RED); //设置画笔颜色
canvas.drawColor(Color.WHITE);
canvas.drawLine(50, 50, 450, 50, paint); //绘制直线
canvas.drawRect(100, 100, 200, 600, paint); //绘制矩形
canvas.drawRect(300, 100, 400, 600, paint); //绘制矩形
Log.i("----------------", "+++++++++++");
postInvalidate(); 在我这里类里面写成invalidate() 也可行,
}
把postInvalidate()或是invalidate()
方法写在OnDraw()方法内,也会不断的执行OnDraw()方法(递归效果),
下面的文章转自网络上的,在这里记录一下以便日后自己查询:
Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
// 在onCreate()中开启线程
new Thread(new GameThread()).start();、
// 实例化一个handler
Handler myHandler = new Handler() {
// 接收到消息后处理
public void handleMessage(Message msg) {
switch (msg.what) {
case Activity01.REFRESH:
mGameView.invalidate(); // 刷新界面
break;
}
super.handleMessage(msg);
}
};
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = Activity01.REFRESH;
// 发送消息
Activity01.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
2,使用postInvalidate()刷新界面
使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 使用postInvalidate可以直接在线程中更新界面
mGameView.postInvalidate();
}
}
}
View 类中postInvalidate()方法源码如下,可见它也是用到了handler的:
- public void postInvalidate() {
- postInvalidateDelayed(0);
- }
- public void postInvalidateDelayed(long delayMilliseconds) {
- // We try only with the AttachInfo because there's no point in invalidating
- // if we are not attached to our window
- if (mAttachInfo != null) {
- Message msg = Message.obtain();
- msg.what = AttachInfo.INVALIDATE_MSG;
- msg.obj = this;
- mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
- }
- }
除了onCreate()是运行在UI线程上的,其实其他大部分方法都是运行在UI线程上的,其实其实只要你没有开启新的线程,你的代码基本上都运行在UI线程上。
android
Handler.post(action) 这是android提供的一种机制,handler对象将通过post方法,将里面的Runnable对象放到UI执行队列中,UI消费这个队列,调用Runnable的run方法。这里并不生成新的线程。
Android中在绘图中的多线程中,invalidate和postInvalidate这两个方法是用来刷新界面的,调用这两个方法后,会调用onDraw事件,让界面重绘。
在一个没有使用线程的小游戏中想刷新一下时间进度,想到用Timer。于是写了一段代码:
nStartRoundTime = System.currentTimeMillis();
nT1 = new Timer();
nT1.schedule(new TimerTask(){ //计划运行时间间隔
public void run(){
refreshTimePaint(); //过3秒调用一下refreshTimePaint()
}
},0,3000);
public void refreshTimePaint(){
invalidate(); //使用invalidate();刷新
System.out.println(System.currentTimeMillis());
System.out.println(nGameState);
}
同时我也将System.currentTimeMillis()打印在View上。
运行一下,发现并不是预期那样, System.out.println的结果在Log里面都有变化,但是View却没有反应。 不但View上面没有被刷新,甚至连原来的触屏事件都没有反映了。
去网上查了一下,得到的一些解释有这些:
The best thing is to use Handler with delayed messages.
And Timer works fine, the problem is that a Timer runs in a separate thread, and so you are trying to modify a view owned by another thread (the main thread that originally created it).
What I think is happening is you're falling off the UI thread. There is a single "looper" thread which handles all screen updates. If you attempt to call "invalidate()" and you're not on this thread nothing will happen.
Try using "postInvalidate()" on your view instead. It'll let you update a view when you're not in the current UI thread.
于是把refreshTimePaint()的代码改成:
public void refreshTimePaint(){
this.postInvalidate(); //使用postInvalidate();刷新
System.out.println(System.currentTimeMillis());
System.out.println(nGameState);
}
这样View就能自动刷新了~~~
这里有几个网页做参考:
http://stackoverflow.com/questions/522800/android-textview-timer
http://groups.google.com/group/android-developers/browse_thread/thread/5baf5a3eaa823b7b?pli=1
http://groups.google.com/group/android-developers/msg/f5765705b8c59d66
分享到:
