Android: 如何利用Handler、Thread更新视图
原文地址
转载请注明原文出处
===============================================================================
总是感觉android中UI更新很让人纠结!自己小结一下,算是抛砖引玉。读这篇文章之前,假设你已经明白线程、handler的使用。
1. 在onCreate()方法中开启线程更新UI
view plain print ?
- publicclassMasterActivityextendsActivity{
- TextViewtv=null;
- Buttonbtn=null;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv=(TextView)findViewById(R.id.text);
- btn=(Button)findViewById(R.id.btn);
- /*onCreate中开启新线程,更新UI。没有报错或者异常信息!*/
- Threadthread=newThread(newRunnable(){
- @Override
- publicvoidrun(){
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv.setText("updateUIissuccess!");
- btn.setText("updateUIissuccess!");
- }});
- thread.start();
- }
随便折腾,不会报错或者异常!以为开启的线程和UI线程(主线程)是同一个线程,但是很不幸,他们的线程id根本是风牛马不相及!
不知道为什么在这里开启子线程更新UI就没有问题!真的想不明白????
2. 在activity如onResume、onStart、反正是以on开头的回调方法
view plain print ?
- @Override
- protectedvoidonRestart(){
- super.onRestart();
- /*onRestart中开启新线程,更新UI*/
- Threadthread=newThread(newRunnable(){
- @Override
- publicvoidrun(){
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv.setText("updateUIissuccess!");
- btn.setText("updateUIissuccess!");
- }});
- thread.start();
- }
不好意思,按下返回按钮在启动程序,或者按Home健再启动程序,就这么折腾几下,就会包异常!信息如下:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
意思是:只有主线程才可以更新UI。
解决办法:加上postInvalidate()方法。
view plain print ?
- @Override
- protectedvoidonRestart(){
- super.onRestart();
- /*onRestart中开启新线程,更新UI*/
- Threadthread=newThread(newRunnable(){
- @Override
- publicvoidrun(){
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv.postInvalidate();
- btn.postInvalidate();
- tv.setText("updateUIissuccess!");
- btn.setText("updateUIissuccess!");
- }});
- thread.start();
- }
postInvalidate()方法,源码:
view plain print ?
- publicvoidpostInvalidate(){
- postInvalidateDelayed(0);
- }
- publicvoidpostInvalidateDelayed(longdelayMilliseconds){
- //WetryonlywiththeAttachInfobecausethere'snopointininvalidating
- //ifwearenotattachedtoourwindow
- if(mAttachInfo!=null){
- Messagemsg=Message.obtain();
- msg.what=AttachInfo.INVALIDATE_MSG;
- msg.obj=this;
- mAttachInfo.mHandler.sendMessageDelayed(msg,delayMilliseconds);
- }
- }
其实,是调用了Handler的处理消息的机制!该方法可以在子线程中直接用来更新UI。还有一个方法invalidate(),稍候再说!
3. 在Button的事件中开启线程,更新UI
view plain print ?
- publicclassMasterActivityextendsActivity{
- TextViewtv=null;
- Buttonbtn=null;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv=(TextView)findViewById(R.id.text);
- btn=(Button)findViewById(R.id.btn);
- btn.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- Threadthread=newThread(newRunnable(){
- @Override
- publicvoidrun(){
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv.setText("updateUIissuccess!");
- btn.setText("updateUIissuccess!");
- }});
- thread.start();
- }
- });
- }
Sorry,报错!即使你加上postInvalidate()方法,也会报这个错误。
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
a. 开启一个线程,在run方法中通知Handler
b. Handler中使用handleMessage方法更新UI。
view plain print ?
- publicclassMasterActivityextendsActivity{
- TextViewtv=null;
- Buttonbtn=null;
- HandlermHandler=newHandler(){
- @Override
- publicvoidhandleMessage(Messagemsg){
- if(msg.what==1){
- tv.setText("updateUIissuccess!");
- btn.setText("updateUIissuccess!");
- }
- super.handleMessage(msg);
- }
- };
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- tv=(TextView)findViewById(R.id.text);
- btn=(Button)findViewById(R.id.btn);
- btn.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- Threadthread=newThread(newRunnable(){
- @Override
- publicvoidrun(){
- System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
- Messagemsg=mHandler.obtainMessage();
- msg.what=1;
- msg.sendToTarget();
- }});
- thread.start();
- }
- });
- }
方法invalidate主要用在主线程中(即UI线程中), 不可以用于子线程。如果在子线程中需要使用postInvalidate方法。 sdk的api有说明: view plain print ?
- publicvoidinvalidate()
- Since:APILevel1
- Invalidatethewholeview.Iftheviewisvisible,onDraw(Canvas)willbecalledatsomepointinthefuture.ThismustbecalledfromaUIthread.Tocallfromanon-UIthread,callpostInvalidate().
- publicvoidinvalidate(){
- if(ViewDebug.TRACE_HIERARCHY){
- ViewDebug.trace(this,ViewDebug.HierarchyTraceType.INVALIDATE);
- }
- if((mPrivateFlags&(DRAWN|HAS_BOUNDS))==(DRAWN|HAS_BOUNDS)){
- mPrivateFlags&=~DRAWN&~DRAWING_CACHE_VALID;
- finalViewParentp=mParent;
- finalAttachInfoai=mAttachInfo;
- if(p!=null&&ai!=null){
- finalRectr=ai.mTmpInvalRect;
- r.set(0,0,mRight-mLeft,mBottom-mTop);
- //Don'tcallinvalidate--wedon'twanttointernallyscroll
- //ourownbounds
- p.invalidateChild(this,r);
- }
- }
- }
Android在onDraw事件处理绘图,
而invalidate()函数可以再一次触发onDraw事件,然后再一次进行绘图动作。
view plain print ?- publicclassMasterActivityextendsActivity{
- staticinttimes=1;
- /**Calledwhentheactivityisfirstcreated.*/
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(newView(null){
- PaintvPaint=newPaint();//绘制样式物件
- privateinti=0;//弧形角度
- @Override
- protectedvoidonDraw(Canvascanvas){
- super.onDraw(canvas);
- System.out.println("thisrun"+(times++)+"times!");
- //设定绘图样式
- vPaint.setColor(0xff00ffff);//画笔颜色
- vPaint.setAntiAlias(true);//反锯齿
- vPaint.setStyle(Paint.Style.STROKE);
- //绘制一个弧形
- canvas.drawArc(newRectF(60,120,260,320),0,i,true,vPaint);
- //弧形角度
- if((i+=10)>360)
- i=0;
- //重绘,再一次执行onDraw程序
- invalidate();
- }
- });
- }
- }
经过测试,发现times一直在++,说明onDraw被多次调用,并且一致在画图!
sdk的api有时候让人很郁闷,无语.....关于invalidate的使用,还待探索。革命尚未成功,同志仍需努力!
更多相关文章
- android设置屏幕禁止休眠的方法
- Android中使用Streams的两种方法
- 我的Android进阶之旅------>Ubuntu下不能识别Android设备的解决
- 动态修改Android参数信息的方法绕过改机检测
- android 横竖屏限制的配置方法
- Android file.createNewFile方法问题总结