`
Qaohao
  • 浏览: 261423 次
  • 性别: 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-手绘地图

    当地图数据发生变化时,可以通过调用`invalidate()`或`postInvalidate()`方法请求重绘。`invalidate()`会立即触发`onDraw()`,而`postInvalidate()`则会在UI线程的下一次遍历时执行。 二、自定义ViewGroup 自定义...

    android view的旋转

    在Android开发中,View的旋转是一项常见的操作,用于实现各种动态效果或用户交互。本文将深入探讨Android View...在实际编程过程中,还需要注意性能优化,合理利用硬件加速以及避免不必要的重绘,以保证应用的流畅性。

    android 自定义view比较综合的例子

    1. 减少不必要的重绘:合理使用View的可见性属性,避免在不需要绘制的地方调用invalidate()。 2. 使用硬件加速:开启硬件加速可以提高View的绘制性能,但并非所有情况都适用,需谨慎使用。 3. 适当使用ViewStub:当...

    Android-AndroidDraw一个Android绘画View视图

    可以使用硬件加速(`setLayerType(View.LAYER_TYPE_HARDWARE, null)`),避免不必要的重绘(`setWillNotDraw(false)`或`setWillNotDraw(true)`),以及合理使用`invalidate()`来仅刷新需要更新的部分。 7. **绘图...

    Android 自定义View实现水平温度计

    可以通过`invalidate()`或`postInvalidate()`方法请求重绘View,以反映温度变化。 5. **布局与测量**: - 在`onMeasure()`方法中,我们需要指定自定义View的大小。可以根据需求设置固定尺寸或者基于内容计算尺寸。...

    Android中View绘制流程

    - **invalidate()**和`postInvalidate()`:这两个方法用于触发View的重绘。当View的状态改变需要更新界面时,调用它们通知系统View需要重新绘制。 - **Choreographer**:Android系统的动画和绘制调度器,负责协调...

    Android中View绘制流程以及invalidate()

    `invalidate()`方法是触发View重绘的关键。当调用`invalidate()`时,Android系统会把该View添加到待绘制队列中,稍后在UI线程的空闲时间进行重绘。这个过程会重新执行上述的测量、布局和绘制三个步骤。`invalidate()...

    Android自定义View—仿雷达扫描效果

    比如,如果扫描线只在一定范围内移动,那么我们只需要重绘这部分区域,而不是整个View。这可以通过`clipRect()`方法实现。 最后,作者可能分享了如何在布局文件中添加自定义View,并在运行时根据需要调整参数,如...

    android获取根View的方法

    在优化性能时,避免频繁操作根View,以减少不必要的布局重绘,提高应用效率。 在提供的“RootActivity”示例中,可能包含了一个展示如何获取和使用根View的Activity实例。通过阅读源代码,开发者可以更深入地理解...

    Android-Android自定义View有这些足够了

    例如,减少不必要的重绘,避免在主线程进行耗时操作等。 在`awesome-view-master`这个项目中,你可能会发现各种自定义View的示例,它们涵盖了上述提到的技术点,包括自定义图形绘制、触摸事件处理、动画实现等。...

    android 自定义view大全,非常好用

    避免不必要的重绘,使用硬件加速,合理使用Bitmap缓存,以及利用View的复用机制(例如,RecyclerView的ViewHolder模式)都是提高性能的关键。 6. **源码分析**:标签中的"android view 源码"暗示这个资源可能包含对...

    重绘CheckBox资料

    在Android开发中,我们使用自定义View来实现Checkbox的重绘。首先,我们需要创建一个新的类,继承自`AppCompatCheckBox`或`CompoundButton`。然后,我们覆盖`onDraw()`方法,这是绘制View的核心函数。在这个方法里,...

    android开发随声音大小变化的自定义view

    可以考虑使用`postInvalidate()`而不是`invalidate()`来避免不必要的重绘,或者使用`ObjectAnimator`来平滑地过渡动画效果。 6. 将自定义View添加到布局: - 在XML布局文件中,将自定义View包含进来,指定其宽度、...

    android自定义View之NotePad出鞘记

    可以利用硬件加速、减少不必要的重绘,以及合理使用`invalidate()`和`postInvalidate()`刷新视图。 8. **样式和主题**:自定义View也可以接受属性,这些属性可以设置在XML布局中,通过`attr.xml`定义,然后在`...

    android之view学习示例

    - 使用`Canvas.save()`和`Canvas.restore()`保存和恢复绘制状态,减少不必要的重绘。 - 使用硬件加速(`android:hardwareAccelerated="true"`),可以提高复杂的图形绘制性能。 10. **视图状态与样式** - View的...

    android 自定义view之进度条(解决刚开始矩形bug)

    通常,一个自定义进度条可能涉及到对View的重绘和动画处理。当进度条开始加载时,如果绘制逻辑出现问题,可能会导致在短时间内看到一个未填充的矩形形状,而不是预期的平滑过渡。这可能是由于初始状态的绘制不完整...

    android 自定义view 圆形进度条

    当进度值改变时,可以调用`invalidate()`方法触发视图重绘,这样`onDraw()`就会再次执行,更新进度条的显示。 此外,我们还可以添加触摸事件监听器,使用户可以通过滑动手指来改变进度。通过重写`onTouchEvent()`...

    android学习之自定义view(一)

    - 使用`ViewCompat.postInvalidateOnAnimation()`代替`invalidate()`,以在下一帧动画时更新视图,减少不必要的重绘。 5. **动画支持**: - 自定义View可以利用Android的动画框架实现平移、旋转、缩放等效果。...

    android汽车仪表view

    由于自定义View需要频繁重绘,特别是在动画运行时,性能优化是必要的。开发者可能使用`invalidate()`方法合理地请求重绘,避免不必要的计算。同时,通过使用硬件加速和避免在`onDraw()`中执行耗时操作,可以提高...

    Android-Android自定义View之LeavesLoading

    无效化和重绘是View更新的关键,只有当View被标记为无效时,系统才会调用`onDraw()`进行重绘。 文件名称列表中的"TestLeavesLoading-master"可能是一个项目源码仓库,包含了一个完整的LeavesLoading自定义View的...

Global site tag (gtag.js) - Google Analytics