`
guozhenqian
  • 浏览: 150829 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android实现局部重绘

 
阅读更多

转载自http://www.cnblogs.com/SkyD/archive/2010/11/08/1871423.html,对原作者表示感谢

首先我们来看一下本例需要用到的两个素材图片:

image

image

 

bj.jpg就是一个渐变图,用作背景。

question.png是一个半透明的图像,我们希望将它放在上面,围绕其圆心不断旋转。

实现代码如下package SkyD.SurfaceViewTest;

 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
 
public class Main extends Activity {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(new MySurfaceView(this));
    }
 
    // 自定义的SurfaceView子类
    class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
 
       // 背景图
       private Bitmap BackgroundImage;
       // 问号图
       private Bitmap QuestionImage;
 
       SurfaceHolder Holder;
 
       public MySurfaceView(Context context) {
           super(context);
           BackgroundImage = BitmapFactory.decodeResource(getResources(),
                  R.drawable.bg);
           QuestionImage = BitmapFactory.decodeResource(getResources(),
                  R.drawable.question);
 
           Holder = this.getHolder();// 获取holder
           Holder.addCallback(this);
       }
 
       @Override
       public void surfaceChanged(SurfaceHolder holder, int format, int width,
              int height) {
           // TODO Auto-generated method stub
 
       }
 
       @Override
       public void surfaceCreated(SurfaceHolder holder) {
           // 启动自定义线程
           new Thread(new MyThread()).start();
       }
 
       @Override
       public void surfaceDestroyed(SurfaceHolder holder) {
           // TODO Auto-generated method stub
 
       }
 
       // 自定义线程类
       class MyThread implements Runnable {
 
           @Override
           public void run() {
              Canvas canvas = null;
              int rotate = 0;// 旋转角度变量
              while (true) {
                  try {
                     canvas = Holder.lockCanvas();// 获取画布
                     Paint mPaint = new Paint();
                     // 绘制背景
                     canvas.drawBitmap(BackgroundImage, 0, 0, mPaint);
                     // 创建矩阵以控制图片旋转和平移
                     Matrix m = new Matrix();
                     // 设置旋转角度
                     m.postRotate((rotate += 48) % 360,
                            QuestionImage.getWidth() / 2,
                            QuestionImage.getHeight() / 2);
                     // 设置左边距和上边距
                     m.postTranslate(47, 47);
                     // 绘制问号图
                     canvas.drawBitmap(QuestionImage, m, mPaint);
                     // 休眠以控制最大帧频为每秒约30帧
                     Thread.sleep(33);
                  } catch (Exception e) {
                  } finally {
                     Holder.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
                  }
              }
           }
 
       }
 
    }
}

 模拟器中的运行效果:

image

(注:图中的问号图形是在不断旋转中的)

这看起来不错,但是有一个问题:我们在代码中设置的帧频最大值是每秒30帧,而实际运行时的帧频根据目测就能看出是到不了30帧的,这是因为程序在每一帧都要对整个画面进行重绘,过多的时间都被用作绘图处理,所以难以达到最大帧频。

 

脏矩形刷新

接下来我们将采取脏矩形刷新的方法来优化性能,所谓脏矩形刷新,意为仅刷新有新变化的部分所在的矩形区域,而其他没用的部分就不去刷新,以此来减少资源浪费。

我们可以通过在获取Canvas画布时,为其指派一个参数来声明我们需要画布哪个局部,这样就可以只获得这个部分的控制权:

 

image

在这里为了便于观察,我将矩形区域设定为问号图形的1/4区域,也就是说在整个画面中我们仅仅更新问号图形的1/4大小那么点区域,其执行效果为:

 

SNAGHTML342f602

可以看到,仅有那1/4区域在快速刷新,其他部分都是静止不动的了,现在的刷新帧频差不多已经能达到最大帧频了,我们的优化起作用了:)

不过别高兴的太早,实际上如果把刷新区域扩大到整个问号图形所在的矩形区域的话,你会发现优化作用变得微乎其微了,还是没法达到最大帧频的,因为更新区域增大了3倍,带来的资源消耗也就大幅增加。

 

覆盖刷新

这种情况下就应当考虑结合覆盖刷新方法再进一步优化了。

试想一下,我们每次刷新时最大的消耗在哪?

没错,在背景图绘制上,这个绘制区域非常大,会消耗我们很多资源,但实际上背景图在此例中是从不变化的,也就是说我们浪费了很多资源在无用的地方。

那么可不可以只绘制一次背景,以后每次都只绘制会动的问号图形呢?

完全可以,尝试修改一下代码,再前面加一个帧计数器,然后我们仅在第一帧的时候绘制背景:

image

这样很简单,但是改后直接运行的话你会发现一个奇怪的状况:

image

问号图案会变得有残影了。

啊哈,这正是我使用半透明图案做范例的目的,通过这个重影,我们就能看出,覆盖刷新其实就是将每次的新的图形绘制到上一帧去,所以如果图像是半透明的,就要考虑重复叠加导致的问题了,而如果是完全不透明的图形则不会有任何问题。

背景会在背景图和黑色背景之间来回闪。

这个问题其实是源于SurfaceView的双缓冲机制,我理解就是它会缓冲前两帧的图像交替传递给后面的帧用作覆盖,这样由于我们仅在第一帧绘制了背景,第二帧就是无背景状态了,且通过双缓冲机制一直保持下来,解决办法就是改为在前两帧都进行背景绘制:

image

现在就没有问题了(如果换成个不透明的图形的话就真没问题了):

image

现在虽然还是达不到最大帧频,但是也算不错啦,在真机上跑的会更快些,接近最大帧频了。

分享到:
评论

相关推荐

    Android-Android自定义控件之局部图片放大镜--BiggerView

    4. **重绘机制**:在Android中,`View`的重绘可以通过调用`invalidate()`或`postInvalidate()`来触发。当放大镜的位置或显示内容发生变化时,需要重新绘制视图以更新显示。 5. **性能优化**:由于涉及到实时的图像...

    android 图片局部放大效果(放大镜)

    这需要在 onDraw() 方法中进行,每次手势更新后都需要调用 invalidate() 以触发重绘。 5. **动画平滑**: 为了提供更好的用户体验,可以添加平滑过渡的动画。当放大区域移动时,可以通过 ValueAnimator 或 ...

    Android 画图工具源码.zip

    在画图工具中,`Bitmap`通常作为绘图的画布,可以使用`Canvas`在上面进行绘制,并通过`save`和`restore`方法来管理绘图状态,实现局部重绘或者保存当前绘图的状态。 此外,源码中可能还包括了触摸事件处理,例如`...

    Android实现折线图的demo

    因此,可以考虑使用局部刷新技术,如`invalidate()`方法来只重绘改变的部分,或者使用`ViewTreeObserver`监听视图的变化以优化绘制过程。 7. **动画效果**: 为了提升用户体验,Demo可能还实现了平滑的动画效果,...

    Android 自定义View实现体育赛事积分表效果

    7. 点击事件处理:为每个单元格添加点击事件,可以通过设置Rect对象并使用Canvas.save()和Canvas.restore()方法来实现局部重绘。 在实际项目中,为了提高代码的可维护性和复用性,可以将体育赛事积分表封装成一个...

    android 绘图带动画 裁剪 圆弧计分图 柱状图 线性图

    在Android开发中,图形绘制是实现复杂用户界面和动态...开发时要注意性能优化,避免不必要的重绘,并利用缓存策略提高效率。同时,理解Android的绘图原理和API,可以灵活地创造出各种定制化的视觉效果,提升用户体验。

    Android放大镜的实现

    在Android开发中,实现放大镜功能是一项常见的需求,它能够为用户提供更加清晰的局部细节查看体验,常见于图片查看器、地图应用等场景。本文将详细介绍如何在Android中实现一个自定义的放大镜效果。 首先,我们需要...

    局部刷新导致界面会闪烁的问题

    3. **DOM操作不当**:频繁地添加、删除或修改DOM元素可能导致浏览器重绘和回流,这些操作非常耗性能,尤其是在大规模的DOM树中。 4. **动画不平滑**:局部刷新可能会影响到正在进行的动画,如果刷新时间点与动画帧...

    Android中的全局变量与局部变量使用小结

    ViewHolder模式是为了减少ListView的滚动时的重绘,通过缓存已创建的视图组件来避免频繁查找。在这种情况下,商品数量的增减操作应该在处理单个Item的回调方法中完成,并且针对每个Item保持独立的局部变量,以确保每...

    Android程序研发源码Android 类似于放大镜源码.rar

    可以使用Bitmap的子类`BitmapShader`来对图片进行局部渲染,避免全屏重绘。此外,合理的内存管理和缓存策略也能提升应用的性能。 7. **布局管理**: 在布局文件中,你需要为放大镜View设定合适的尺寸和位置,使其...

    Android代码-原创自定义控件之-Canvas实时绘制音乐波形图

    5. **性能优化**:由于频繁的重绘可能会消耗大量资源,因此在实现时要考虑性能优化。例如,可以使用Bitmap的局部更新(如createBitmap()的inMutable参数),或者使用硬件加速(setLayerType(View.LAYER_TYPE_...

    Android消息气泡拖拽

    例如,可以通过`View.setLayerType()`开启硬件加速,或者使用`invalidate()`的局部刷新功能,只重绘需要更新的部分。 8. **手势识别**: 可以考虑使用` GestureDetector`或`ScaleGestureDetector`来识别更多的手势...

    rcv的局部数据刷新和布局刷新

    例如,如果你有一个新闻列表,当某条新闻的评论数量增加时,只需调用对应位置的数据项的`notifyItemChanged()`方法,RecycleView会自动识别并仅更新该位置的视图,而不会重绘整个列表。这种方法减少了不必要的计算和...

    Android飞机大战

    - 优化绘图操作,避免不必要的重绘,使用`Canvas`的`saveLayer`和`restore`方法进行局部刷新。 8. **测试与调试**: - 使用Android提供的模拟器或实际设备进行测试,确保游戏在各种屏幕尺寸和分辨率下都能正常...

    Android开发之ListView实现Item局部刷新

    通过这种方式,我们避免了无谓的View重绘,提高了应用性能,并减少了用户界面的闪烁,提供了更好的用户体验。同时,这也适用于有复杂视图(如图片)的情况,因为只有被更新的Item才会重新加载其资源。 总之,...

    Android 自定义WheelView

    当数据量较大时,为了提高性能,可以采用局部重绘策略,只更新实际需要改变的部分,而不是整个视图。此外,考虑使用`ViewStub`或延迟加载技术,减少初始加载时的资源消耗。 7. **交互设计**: 为了让用户有更好的...

    android放大镜功能

    为了保证流畅的用户体验,可能需要对图像处理进行优化,例如只对放大镜覆盖的区域进行重绘,而不是整个屏幕。 在提供的`ExtractWordView-master`项目中,很可能是实现这一功能的一个示例代码。通过分析这个项目的...

    android 仿iphone 的齿轮效果

    - 考虑到性能,尽量减少`onDraw()`的调用频率,可以使用`invalidate()`的局部刷新功能,只重绘需要改变的部分。 - 如果齿轮选项很多,可以考虑使用视差滚动,只加载可见部分,提高滚动流畅度。 6. **布局整合**:...

    Android仿QQ点赞动画

    在Android中,过度的UI更新会导致不必要的屏幕重绘,影响性能。因此,我们应该尽量减少不必要的绘制,例如,通过设置动画的duration和interpolator来控制动画的速度和节奏。同时,利用硬件加速可以显著提升动画的...

    Android之实现“摇一摇”动态更换皮肤.zip

    如果是全局主题的改变,Activity会自动重绘;如果是局部UI元素,可能需要调用`invalidate()`或`requestLayout()`方法。 4. **动画效果**:为了提升用户体验,可以加入过渡动画,让皮肤的切换更加平滑自然。 总的来...

Global site tag (gtag.js) - Google Analytics