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

在自定义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);

    }

    @Override
    protected 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.DurianView
        android: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);

    }

    @Override
    protected 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区域是否存在重叠,或者所谓的子图形有边界冲突.














已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页