您的位置:首页 > 移动开发 > Android开发

Android 自定义View过度绘制性能优化<7>

2016-01-11 17:16 615 查看
在自定义View 里面如果又有很多个子View相互重叠,比如,开发者在一张canvas上面画了N张图片,而N张图片存在重叠,那么很显然就会存在过度绘制的现象.专家分析的意见如下:
<pre class="html" name="code">前面有提到过,对不可见的UI组件进行绘制更新会导致Overdraw。例如Nav Drawer从前置可见的Activity滑出之后,如果还继续绘制那些在Nav Drawer里面不可见的UI组件,这就导致了Overdraw。为了解决这个问题,Android系统会通过避免绘制那些完全不可见的组件来尽量减少Overdraw。那些Nav Drawer里面不可见的View就不会被执行浪费资源。
但是不幸的是,对于那些过于复杂的自定义的View(通常重写了onDraw方法),Android系统无法检测在onDraw里面具体会执行什么操作,系统无法监控并自动优化,也就无法避免Overdraw了。但是我们可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。
其余要解决这样的问题原理还是比较简单,但是实现起来就很繁琐,因为它需要通过canvas.clipRect()方法将被覆盖的部分cut掉,比如说一张图片800*800的,但是能够显示出来的只有100*100,那么其他的700*700就要全部通过clipRect()方法调整绘制范围,只能够显示出100*100的部分,剩下的700*700全部被cut掉了.这样剩下的700*700的地方在绘制其他图片的时候,就不会出现重叠,如果不出现重叠,就不会出现过度绘制了(这话似乎有点绝对).可以做一个android 工程测试一次:<1> : 新建android 工程如下:<2> : 所有的程序代码如下:
DurianMainActivity.java
package org.durian.durianviewoverdraw;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;

public class DurianMainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.durian_main);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_durian_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}
DurianView.java
package org.durian.durianviewoverdraw.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.View;import org.durian.durianviewoverdraw.R;/*** Project name : DurianViewOverDraw* Created by zhibao.liu on 2016/1/11.* Time : 14:33* Email warden_sprite@foxmail.com* Action : durian*/public class DurianView extends View {private final static String TAG="DurianView";private Paint mpaint;private Bitmap bitmap;private Context mContext;private boolean isReject=false;public DurianView(Context context, AttributeSet attrs) {super(context, attrs);mContext=context;initView();}private void initView(){mpaint=new Paint();mpaint.setColor(0x00ff00);bitmap= BitmapFactory.decodeResource(mContext.getResources(), R.drawable.back);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for(int i=0;i<2;i++) {canvas.save();//            canvas.clipRect(10+i*180,10+i*180,500*(i+1),500*(i+1));canvas.drawBitmap(bitmap, 10+i*180, 10+i*180, null);/*if(i==1) {isReject = canvas.quickReject(10 + i * 180, 10 + i * 180, 10 + i * 180+50, 10 + i * 180+50, Canvas.EdgeType.BW);Log.i(TAG,"isReject : "+isReject);isReject = canvas.quickReject(10 , 10 , 10 +180, 10 +180, Canvas.EdgeType.BW);Log.i(TAG,"*** isReject : "+isReject);}*/canvas.restore();}}}
布局文件:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><org.durian.durianviewoverdraw.view.DurianViewandroid:id="@+id/durianview"android:layout_width="fill_parent"android:layout_height="fill_parent" /></LinearLayout>图片资源就自行放一张图片进去吧,稍微大一点的.<3> : 运行结果:截取部分,因为我是那nexus平板测试的:颜色就不需要解释了,蓝色这边是1X,绿色这边是2X,这个绿色是因为打开GPU overdraw显示出来的,并不是程序显示出来的.<4> : 在DurianView代码中,我注释掉了几行,现在打开,看一看:看以看出,裁剪掉的有绿色变成蓝色了,但是还是有一部分显示绿色,那是因为程序没有去裁剪(计算太繁琐了,所以没去弄了),这是因为画第一张图片,这样似乎不怎么好理解,我重新把DurianView程序调整一下:package org.durian.durianviewoverdraw.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.View;import org.durian.durianviewoverdraw.R;/*** Project name : DurianViewOverDraw* Created by zhibao.liu on 2016/1/11.* Time : 14:33* Email warden_sprite@foxmail.com* Action : durian*/public class DurianView extends View {private final static String TAG="DurianView";private Paint mpaint;private Bitmap bitmap;private Context mContext;private boolean isReject=false;public DurianView(Context context, AttributeSet attrs) {super(context, attrs);mContext=context;initView();}private void initView(){mpaint=new Paint();mpaint.setColor(0x00ff00);bitmap= BitmapFactory.decodeResource(mContext.getResources(), R.drawable.back);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for(int i=0;i<2;i++) {canvas.save();canvas.clipRect(10+i*250,100,10+250*(i+1),500*2);canvas.drawBitmap(bitmap, 10+i*250, 100, null);/*if(i==1) {isReject = canvas.quickReject(10 + i * 180, 10 + i * 180, 10 + i * 180+50, 10 + i * 180+50, Canvas.EdgeType.BW);Log.i(TAG,"isReject : "+isReject);isReject = canvas.quickReject(10 , 10 , 10 +180, 10 +180, Canvas.EdgeType.BW);Log.i(TAG,"*** isReject : "+isReject);}*/canvas.restore();}}}运行结果:如果不加裁剪:一看就出现2X重绘了.如果将程序改一下,划出4张来,把for循环里面的i<2,改为i<4即可,同时把裁剪那一行代码注释掉:就很容看到1X,2X,3X,4X了.然后将裁剪那一行代码释放出来:canvas.clipRect(10+i*250,100,10+250*(i+1),500*2);就不会存在了,全部是1X了,从而消除了过度绘制.专家还提到canvas.quickReject()方法
if(i==1) {isReject = canvas.quickReject(10 + i * 180, 10 + i * 180, 10 + i * 180+50, 10 + i * 180+50, Canvas.EdgeType.BW);Log.i(TAG,"isReject : "+isReject);isReject = canvas.quickReject(10 , 10 , 10 +180, 10 +180, Canvas.EdgeType.BW);Log.i(TAG,"*** isReject : "+isReject);}
在上面的基础上,注释去掉.也就是说如果图形方框没有存在重叠,返回的为true.下面把裁剪去掉:可以通过这个判断某个Rect区域是否存在重叠,或者所谓的子图形有边界冲突.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: