`
Qaohao
  • 浏览: 260181 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

Android之view重绘

阅读更多
android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
以下是我在android文档中找到的说明,
引用

public void invalidate()
Invalidate the whole view. If the view is visible, onDraw(Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().

public void postInvalidate ()
Cause an invalidate to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.

google的文档的说明实在是简单,往往看了开发中都会遇到这两个问题:
1. 没有任何异常,view没能刷新。
2. android应用异常终止,打开logcat会看到这样的异常信息, Only the original thread that created a view hierarchy can touch its views。

最后,通过查文档,上网查询才知道,invalidate和postInvalidate方法需要使用android提供的handler,才能实现重绘,而在文档的说明中却只字不提,真是简单啊。具体是在需要重绘的地方调用handler的sendMessage方法发送消息,紧接着会os会触发handler中的handlerMessage方法,在handlerMessage方法中再调用view的invalidate或者postInvalidate方法就能实现重绘。

下面是我分别针对invalidate方法,给出view重绘代码,仅供参考:
class CustomizeView extends WhichView {

	public CustomizeView(Context context) {
		super(context);
		final Handler handler = new Handler();

		new Thread(new Runnable() {
			@Override
			public void run() {
				// delay some minutes you desire.
				/*try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
				}*/
				handler.post(new Runnable() {
					public void run() {
						concreteUpdateUI();
						invalidate();
					}
				});
			}
		}).start();
	}

	protected void concreteUpdateUI() {
		// Add concrete movement for UI updates.
		// ...
	}
}

或者这样实现也可以。
class CustomizeView extends TextView {

	public CustomizeView(Context context) {
		super(context);
		new Thread(new UIUpdateThread()).start();
	}

	class UIUpdateThread implements Runnable {
		final Handler mHandler = new Handler();

		final Runnable mUpdateResults = new Runnable() {
			public void run() {
				concreteUpdateUI();
				invalidate();
			}
		};

		public void run() {
			// delay some minutes you desire. 
			/*try {
				Thread.sleep(1000 * 5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}*/
			mHandler.post(mUpdateResults);
		}

	}

	protected void concreteUpdateUI() {
		// Add concrete movement for UI updates.  
		// ...  
	}

}


分享到:
评论
9 楼 kkmike999 2013-04-16  
用handler麻烦啊~~ 用AsyncTask更简单(这方法能运行,不过不知道view有没变化)


//AsyncTask三个参数,分别是输入、updateUI时传递的参数、返回的参数
class MyTask extends AsyncTask<Integer, Integer, Void> {

    @Override
    protected Void doInBackground(Integer... params) {
	
        publishProgress(params[0]);//之后会调用onProgressUpdate方法

	return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {

        //mView是View子类
        mView.invalidate();
    }
}

8 楼 kkmike999 2013-04-16  
用handler麻烦啊~~ 用AsyncTask更简单(这方法能运行,不过不知道view有没变化)


//AsyncTask三个参数,分别是输入、updateUI时传递的参数、返回的参数
class MyTask extends AsyncTask<Integer, Integer, Void> {

    @Override
    protected Void doInBackground(Integer... params) {
	
        publishProgress(params[0]);//之后会调用onProgressUpdate方法

	return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {

        //mView是View子类
        mView.invalidate();
    }
}

7 楼 bsxy 2012-08-27  
楼主给出的两段代码均有问题,
代码一:
class CustomizeView extends WhichView { 
 
    public CustomizeView(Context context) { 
        super(context); 
        final Handler handler = new Handler();  //这里的new操作已把handler和ui线程的消息队列绑定了.
 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                // delay some minutes you desire. 
                /*try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                }*/ 
                handler.post(new Runnable() { 
                    public void run() { 
//这里的代码其实是执行在handler所在的ui线程的
                        concreteUpdateUI(); 
                        invalidate(); 
                    } 
                }); 
            } 
        }).start(); 
    } 
 
    protected void concreteUpdateUI() { 
        // Add concrete movement for UI updates. 
        // ... 
    } 


代码二和线程完全没关系.所有代码都是执行在ui线程的

就这两份代码而言,如果concreteUpdateUI是绘制动作的话,那都是运行在主线程中.不需要开线程.
6 楼 Qaohao 2009-10-20  
回复:

你的思路我明白了,使用40个对象是因为,前20个对象是前一个游戏的残局,另外20个对象是新游戏的中牌。思路没问题,但是你这个使用的手机,因此这么考虑就不对了。我现在说说我的思路吧。

第一,不要完全将所有图片的信息读入到手机内存中,因为你得到自己的牌时也只能看到一张牌的五分之一,或者更少。因此你只需要将你要表示的部分存到一个数组里面就可以了,这里我建议你不要使用image对象。

第二,在你打出一张牌时,在真正读那张牌的内容。此外打出去的一张牌可以用一张图片来表示,一般游戏都是这么做的。

第三,适当压缩牌图片的bitmap信息,只要能清晰表示图片就行了。网上有些图片压缩算法,你可以找找。

最后,没有必要20张牌new40个对象,图像信息可以共享的。


以上是我的一些建议,希望对你有所帮助。
5 楼 android_learner 2009-10-16  
Qaohao 写道
android_learner 写道
呵呵! 谢谢了。

    你有没有重画的一些可以看到效果的代码,,简单的一个列子就行。。我想在虽然之前的问题解决了,但我重画区域的地方有点多,new的对象多了。vm抛出oom异常。。我不知道怎么解决!!  希望你给点意见。。谢谢啦!


我的例子是在linux下面,现在那个系统起不了,呵呵。你可以把你的异常信息发给我,我帮你看看。


好的。。。谢谢咯!不过我觉得我的方法有问题。。我现要的效果是像玩棋牌游戏,点击开始按钮时在该页面中显示玩家的牌。
我的思路:先在ondraw()方法里把bitmap都画出来,,当用户点击开始按钮后我再用实现重画的方法里new 一个新的bitmap把之前画的bitmap覆盖。。所以20张牌我就要new40个对象。。而且页面的bitmap远远不止这些。。所以导致内存泄露。。我想知道你有没有好的思路。。

10-16 13:58:44.461: ERROR/dalvikvm-heap(811): 617376-byte external allocation too large for this process.
10-16 13:58:44.461: ERROR/(811): VM won't let us allocate 617376 bytes
10-16 13:58:44.481: ERROR/AndroidRuntime(811): Uncaught handler: thread main exiting due to uncaught exception
10-16 13:58:44.511: ERROR/AndroidRuntime(811): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:439)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:322)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:344)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:364)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at com.hk.ddz.TableView.createBitmap(TableView.java:190)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at com.hk.ddz.TableView.onDraw(TableView.java:156)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.View.draw(View.java:6274)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.drawChild(ViewGroup.java:1526)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.drawChild(ViewGroup.java:1524)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.View.draw(View.java:6277)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.widget.FrameLayout.draw(FrameLayout.java:352)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.drawChild(ViewGroup.java:1526)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.drawChild(ViewGroup.java:1524)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.View.draw(View.java:6277)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.widget.FrameLayout.draw(FrameLayout.java:352)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1883)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewRoot.draw(ViewRoot.java:1332)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewRoot.performTraversals(ViewRoot.java:1097)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.os.Looper.loop(Looper.java:123)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at android.app.ActivityThread.main(ActivityThread.java:4203)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at java.lang.reflect.Method.invokeNative(Native Method)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at java.lang.reflect.Method.invoke(Method.java:521)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
10-16 13:58:44.511: ERROR/AndroidRuntime(811):     at dalvik.system.NativeStart.main(Native Method)

4 楼 Qaohao 2009-10-15  
android_learner 写道
呵呵! 谢谢了。

    你有没有重画的一些可以看到效果的代码,,简单的一个列子就行。。我想在虽然之前的问题解决了,但我重画区域的地方有点多,new的对象多了。vm抛出oom异常。。我不知道怎么解决!!  希望你给点意见。。谢谢啦!


我的例子是在linux下面,现在那个系统起不了,呵呵。你可以把你的异常信息发给我,我帮你看看。
3 楼 android_learner 2009-10-15  
呵呵! 谢谢了。

    你有没有重画的一些可以看到效果的代码,,简单的一个列子就行。。我想在虽然之前的问题解决了,但我重画区域的地方有点多,new的对象多了。vm抛出oom异常。。我不知道怎么解决!!  希望你给点意见。。谢谢啦!
2 楼 Qaohao 2009-10-12  
android_learner 写道
楼主:你好。。
请问concreteUpdateUI()里怎么实现。。我重画时concreteUpdateUI方法内实现根本没有重画成功??应该怎么实现呢??
protected void concreteUpdateUI() {
Toast.makeText(this.getContext(),"haha" + point.x+ " " + point.y, Toast.LENGTH_SHORT).show();

Paint p = new Paint();
p.setColor(Color.RED);
// Rect r = new Rect(50, 12, 73, 30);
// canvas.drawRect(tempRect, p);
// 桌子变色
Bitmap table = loadImage(R.drawable.table4_light, table1);
Canvas canvas = new Canvas();
canvas.drawBitmap(table, 0, 0, paint);
// 画人
Bitmap bitmap = Bitmap.createBitmap(topChair.right + 10,
topChair.bottom + 10, Config.ARGB_8888);
Resources r = this.getContext().getResources();
Drawable drawable = r.getDrawable(R.drawable.person);
drawable.setBounds(topChair);
drawable.draw(canvas);
canvas.drawBitmap(bitmap, 0, 0, p);

}


回复你好,我上面提供的代码,只是一个架子,这个架子里面只能实现两种情形:
1)其一就是android控件延时刷新,修改方法就是在UIUpdateThread类的run方法在呼出Handler的post方法之前增加一个延迟。
try {
         Thread.sleep(1000 * 5);
  } catch (InterruptedException e) {
         e.printStackTrace();
  }
    举个例子说明一下它在什么场合应用吧,比如启动游戏前的动画,是不是Imageview就可以通过这个延时刷新来实现启动动画的效果,刚开始加载一幅画,过会换一幅画。

2)其二就是android控件定时刷新,那就得在修改CustomizeView构造方法,将UIUpdateThread添加到Timer中,这样就可以实现定时刷新。


那么如果你需要不定时刷新的话,那我提供的这个架子就不适用了,你得自己去调用Handler的post方法。这种情况应该是最常见的。

呵呵!希望我的回复对你能有所帮助。


1 楼 android_learner 2009-10-11  
楼主:你好。。
请问concreteUpdateUI()里怎么实现。。我重画时concreteUpdateUI方法内实现根本没有重画成功??应该怎么实现呢??
protected void concreteUpdateUI() {
Toast.makeText(this.getContext(),"haha" + point.x+ " " + point.y, Toast.LENGTH_SHORT).show();

Paint p = new Paint();
p.setColor(Color.RED);
// Rect r = new Rect(50, 12, 73, 30);
// canvas.drawRect(tempRect, p);
// 桌子变色
Bitmap table = loadImage(R.drawable.table4_light, table1);
Canvas canvas = new Canvas();
canvas.drawBitmap(table, 0, 0, paint);
// 画人
Bitmap bitmap = Bitmap.createBitmap(topChair.right + 10,
topChair.bottom + 10, Config.ARGB_8888);
Resources r = this.getContext().getResources();
Drawable drawable = r.getDrawable(R.drawable.person);
drawable.setBounds(topChair);
drawable.draw(canvas);
canvas.drawBitmap(bitmap, 0, 0, p);

}

相关推荐

    android面试题

    View重绘和内存泄露的好像是面试经常问的问题1. View的刷新: 在需要刷新的地方,使用handle.sendmessage发送信息,然后在handle的getmessage里面执行invaliate或者postinvaliate. 2. GC内存泄露

    android贪食蛇源码(游戏是基于重绘view机制开发的)

    工作需要开发了一个贪食蛇apk,共10关。包含了游戏得分记录,升级地图切换,游戏音效,退出游戏可继续,游戏...游戏是基于重绘view机制开发的,没有使用surfaceview,喜欢的可以参考下改改。(新手参考,大神请绕道。)

    android自定义View之曲线图

    一个简单的曲线图来标识数据,开始以为很简单,android已经有那么多的开源图表库了,什么achartenginee,hellochart,mpandroidchart等等,下载Demo一找,都强大到有点不适合...于是只能自已去画了,继承自View去重绘。

    Android 自定义画布canvas 实现绘制和清空画布功能

    Android 自定义画布canvas 实现绘制和清空画布功能,内含源码、apk

    Android View 绘制机制的详解

    整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw),流程图...

    android View 绘制完成监听的实现方法

    //view重绘时回调 view.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { @Override public void onDraw() { // TODO Auto-generated method stub } }); //view加载完成时回调 view....

    Android代码-MediaUtils

    重绘了录制视频和录音时的两个自定义view,可在原本基础上进行二次开发 提供了视频截图方法,支持双击放大,支持自动对焦 视频录制暂时使用 SurfaceView Camera , 后续会升级为 TextureView Camera2 简单的加了

    Android代码-使用硬件加速提高安卓动画性能

     在绘制动画时,你的view需要重绘每一层,如果你使用视图图层代替重绘,视图渲染只会渲染一次并会被重用。使用硬件加速会让动画绘制的更快,因为硬件会把图层缓存在GPU上。  &gt;用法    用法很简单关键方法是View....

    Android中View绘制流程以及invalidate()等相关方法分析

    整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为根据之前设置的状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、...

    Android自定义View的实现方法实例详解

    一、自绘控件 下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了...通过 Android视图状态及重绘流程分析,带你一步步深入了解View(三) 这篇文章的学习我们都已经知道,调用

    Android自定义View实现水波纹效果

    实现思路: 先将最大圆半径与最小圆半径间距分成几等份,从内到外,Paint 透明度依次递减,绘制出同心圆,然后不断的改变这些同心圆的半径大小,延迟一定时间重绘,便达到了想外散开的动画效果了。 public class ...

    Android自定义View圆形百分比控件(一)

    只需要画一个圆、一个圆弧、一个百分比文本,添加一个点击事件,传入百分比重绘 1、在res/values文件夹下新建attrs.xml文件,编写自定义属性: &lt;?xml version=1.0 encoding=utf-8?&gt; &lt;attr name=

    实例讲解Android中的View类以及自定义View控件的方法

    主要介绍了Android中的View类以及自定义View控件的方法,讲解了如何继承View类并且展示了一个对View进行重绘的例子,需要的朋友可以参考下

    Android如何创建可拖动的图片控件

    本文实例为大家分享了Android创建可拖动图片控件的具体代码,供大家参考,具体内容如下 重载、自绘 ...刷新控件,导致控件重绘,重绘时移动绘制的左上角坐标即可。 刚开始时,只是收到了ACTION_DOWN

    Android 绘制动态图

    1、创建一个继承与View类自定义类 2、覆盖其中的onDraw()方法,并在此方法中绘图。 3、在MainActivity中使用invalidate()方法来调用onDraw()方法来进行图形的重绘.

    BlurView:适用于Android的基础视图的动态iOS类模糊

    当检测到视图层次结构中的更改时,BlurView将重绘其模糊的内容(调用draw())。 它尊重位置和大小的更改,包括视图动画和属性动画。 如何使用 &lt;!-- Any child View here, TabLayout for example. This View...

    Android编程基于自定义控件实现时钟功能的方法

    这里选择延迟一秒发送消息重绘view来实现的动画,对外提供了开启时钟,关闭时钟的方法,当activity执行onResume方法的时候,执行startClock()方法,当移除view或activity执行onStop方法的时候可以执行stopClock()...

    Android自定义控件RatingBar调整字体大小

    这是一个类似于RatingBar的控件,然而配置RatingBar的样式难以实现这样的效果,如选中的图案和上面的... 在onTouchEvent()方法中处理ACTION_DOWN和ACTION_MOVE事件,调用invalidate()方法引起View的重绘,以更新视图

    Android代码-ViewExplosion

    简介 最近在闲逛的时候,发现了一款粒子爆炸特效的控件,觉得比较有意思,效果也不错。...draw()方法是它最重要的方法,也就是使所有粒子重绘自身,从而实现动画效果。 &gt; &gt; ParticleFactory,是一个抽象类。用于产生

Global site tag (gtag.js) - Google Analytics