各位大侠,先请看下面一段非常非常简单的代码:
package com.zsh;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class TestAndroid3Activity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Button button=new Button(this);
button.setText("啊啊啊");
setContentView(button);
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
button.setText("aa");
button.invalidate();
try{
Thread.sleep(5000);
}catch (Exception e) {
// TODO: handle exception
}
button.setText("bb");
}
});
}
}
整个系统就这么一个文件。想实现的功能是一点button,先替换自己的文字为aa,过5秒后再替换自己的文字为bb。可是实际运行效果为点了后没反应,过5秒后直接替换为bb(因为先替换为aa然后瞬间替换为bb看不到aa的缘故)。我想问一下android底层绘制的机制到底是什么呢?如果是在当前线程绘制的话应该按顺序执行先替换完然后invalidate显示给用户然后再去睡。。请问为什么?请详细讲解下android绘制,顺便问一下想实现我的功能应该怎么做。
[quote]你好,你说的“UI主线程捕捉到这个消息的时候才会去刷新 button 的值”那请问目前这个线程是不是UI主线程?如果是,谈何捕捉消息呢?[/quote]
打个比方吧,假设主线程本来做的事就是从 A 到 B 再到 C,但当它在从 B 到 C 的过程中时,你在中间插了一脚了(就比如说点击了一个按钮,设这个操作为 D),然后它就改道先至 D 然后再去 C。之后,它又在 A B C 间循环。那么,不管 A B C 中哪一个包含了重画当前界面的操作,都必须等你的 D 的操作完成之后才能被再次执行。
不是线性的往下执行的,是异步执行的,上面即使不执行完,按照你这么做,也会执行后一步吧。
你看看这篇文章
[url]http://www.pin5i.com/showtopic-android-ui-view-trying-to-draw.html[/url]
你用Thread.sleep(),应该是不可以的。
你尝试用handler.sendEmptyMessageDelayed(0, 5000);来实现吧,我已经验证过了,是可行的。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
Android程序中可以使用的界面刷新方法有两种,分别是利用Handler和利用postInvalidate()来实现在线程中刷新界面。
你这个需求,只需要这么改写一下就OK了:
[code="java"]
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
button.setText("aa");
new Thread(new Runnable() {
try{
Thread.sleep(5000);
}catch (Exception e) {
// TODO: handle exception
}
button.setText("bb");
}).start();
}
[/code]
另外你也可以使用 handler 或者 view.postInvalidate()方法来搞定。
而你说的那个问题的原因是: Android 不是线程安全的,所有的绘制工作都是由 主线程(即UI线程)来实现,所以,当你把内容改为 aa 的后,UI线程就被你睡眠了,整个程序就HOLD在这块了,当5秒时间过了之后,UI线程把内容改为了“bb”,你就自然而然地看不到"aa"字样了,就算看到了,也只是一瞬间的事。这事其实跟底层绘制没啥关系,只要你明白我刚才说的就OK了。
用Handler 来实现吧
[quote]你好,如果在主线程中是线性执行的,那应该等button赋值为aa完全执行完毕了展示给用户了再去睡啊,不可能先睡了再去赋值为aa吧[/quote]
你给 button 赋值了 "aa"是没有问题,但是UI线程这个时候并没有立刻刷新当前界面呢啊,而当它想去刷新的时候,却已经被睡眠了,那它只好HOLD住在那等待睡眠时间过了再去刷新
也就是说,你给button设置了新的值之后,UI线程并没有立刻知道这个事情,而是你在改变值的时候,发出了一个类似于 "text changed" 的消息,UI主线程捕捉到这个消息的时候才会去刷新 button 的值。这一过程,肯定是比你的
[quote]button.setText("aa");
button.invalidate(); /////////// ①
try{
Thread.sleep(5000); ///////////// ②
}catch (Exception e) {
// TODO: handle exception
} [/quote]
中 ① 和 ②之间的间隔长的