UI绘制优化

CPU与GPU

CPU 的任务繁多,做逻辑计算外,还要做内存管理、显示操作,因此
在实际运算的时候性能会大打折扣,在没有 GPU 的时代,不能显示复
杂的图形,其运算速度远跟不上今天复杂三维游戏的要求。即使 CPU
的工作频率超过 2GHz 或更高,对它绘制图形提高也不大。这时 GPU
的设计就出来了, CPU 的控制器较为复杂,擅长各种复杂的逻辑运算,但不擅长数学尤其是浮点运算。
简单来说就是CPU将图形的计算公式传给GPU,也可理解为显卡,然后GPU拿到这个公式后就开始矢量图的绘制工作,Android 系统每隔 16ms 发出 VSYNC 信号 (1000ms/60=16.66ms) ,触发对 UI 进行渲染, 如果每次渲染都成功这样就能够达到流畅的画面所需要的 60fps ,为了能够实现 60fps ,这意味着计算渲染的大多数操作都必须在 16ms 内完成。如果很多帧都没有在这个时间内绘制完成,就会出来视觉上的卡顿现象。这也是60Hz 刷新频率由来。

过度绘制

概念:

GPU的绘制过程,类似于装修刷墙,一层一层的进行,并且16ms刷一次,每刷一遍就是一个图层,这样就会有图层覆盖的情况,下边的图层用户看不到,但其实GPU也已经进行了绘制。

过度绘制出现的原因

  1. 自定义控件中的 onDraw 方法做了多重绘制
  2. 布局层次太深,例如xml文件使用了迷之缩进,布局层叠太多,用户看不到的区域也进行了绘制,导致耗时增加

过渡绘制查看

开发者工具打开调试GPU过渡绘制
过度绘制查看

可以看到上边有很多颜色,颜色越浅,说明过度绘制越少,

  1. 蓝色部分,过渡绘制一次,就是无过渡绘制,只有一层
  2. 淡绿色 过渡绘制两次,说明有两层
  3. 淡红色 过度绘制三次
  4. 深红 过度绘制四次及以上

然后就各个击破进行优化就好了

过度绘制优化

  1. 减少背景重复
去掉单个activity的主题设置的属性
可以在setContentView之前getWindow().setBackgroundDrawable(null);

去掉所有activity主题设置中的属性
直接在styles.xml中设置<item name="android:windowBackground">@null</item>

  1. 使用裁减减少控件之间的重合部分
  2. 注意点:
    1.能在一个平面显示的内容,尽量只用一个容器
    2.尽可能把相同的容器合并merge
    3.能复用的代码,用include处理,可以减少GPU重复工作

hierarchy view 查看布局结构图

在你的AndroidSDK的安装目录找到 Android\sdk\tools\monitor.bat,双击运行(运行前先在模拟器中启动项目,真机好像不太好使),切换到Hierarchy View ,找到自己的要查看的页面

在这里插入图片描述

view层级

这样就可以看到你的当前页面每个布局下一共有几层view了,试图减少view的层级可以提高GPU的渲染效率

自定义view裁剪被遮挡布局

public class DroidCardsView extends View {

    //图片与图片之间的间距
    private int mCardSpacing = 150;
    //图片与左侧距离的记录
    private int mCardLeft = 10;

    private List<DroidCard> mDroidCards = new ArrayList<DroidCard>();

    private Paint paint = new Paint();

    public DroidCardsView(Context context) {
        super(context);
        initCards();
    }

    public DroidCardsView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initCards();
    }



    /**
     * 初始化卡片集合
     */
    protected void initCards(){
        Resources res = getResources();
        mDroidCards.add(new DroidCard(res, R.drawable.alex,mCardLeft));

        mCardLeft+=mCardSpacing;
        mDroidCards.add(new DroidCard(res, R.drawable.claire,mCardLeft));

        mCardLeft+=mCardSpacing;
        mDroidCards.add(new DroidCard(res, R.drawable.kathryn,mCardLeft));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (DroidCard c : mDroidCards) {
            drawDroidCard(canvas,c);
        }
        for (int i = 0; i < mDroidCards.size() - 1; i++){
            drawDroidCard(canvas, mDroidCards,i);
        }
        drawLastDroidCard(canvas,mDroidCards.get(mDroidCards.size()-1));

        invalidate();
    }

    private void drawDroidCard(Canvas canvas, DroidCard c) {
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
    }

    /**
     * 绘制最后一个DroidCard
     * @param canvas
     * @param c
     */
    private void drawLastDroidCard(Canvas canvas,DroidCard c) {
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
    }

    /**
     * 绘制DroidCard
     * @param canvas
     * @param mDroidCards
     * @param i
     */
    private void drawDroidCard(Canvas canvas,List<DroidCard> mDroidCards,int i) {
        DroidCard c = mDroidCards.get(i);
        canvas.save();//备份画布
        
        //裁剪画布
        canvas.clipRect((float)c.x,0f,(float)(mDroidCards.get(i+1).x),(float)c.height);
        canvas.drawBitmap(c.bitmap,c.x,0f,paint);
        
        canvas.restore();//释放画布
    }
}

include、ViewStub、merge

  1. include就是为了解决重复定义相同布局的问题
  2. ViewStub就是一个宽高都为0的一个View,它默认是不可见的。
    只有通过调用 setVisibility() 函数或者 Inflate() 函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果。在ViewStub布局可显示之前,系统不会消耗资源去实例化里面的布局,可以节省系统资源消耗。
<ViewStub
    android:id="@+id/view_stub"
    android:layout_width="fill_parent"
    android:layout_height="49dp"
    android:layout="@layout/my_layout"
    android:inflatedId="@+id/view_stub_inflated_id"/>
// 第一种使用方法:
//使用android:layout="@layout/my_layout"设置布局
ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);  
//设置setVisibility,使布局文件实例化
viewStub .setVisibility(View.VISIBLE);  
// 通过ViewStub的xml中的属性 inflatedId 来获取View
LinearLayout linearLayout =(LinearLayout) findViewById(R.id.view_stub_inflated_id);  
if ( viewStub.getVisibility() == View.VISIBLE ) {  
    // 加载成功
}
// 第二种使用方法:
ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);  
viewStub.setLayoutResource(R.layout.my_layout);
//使用 inflate,使布局文件实例化
LinearLayout linearLayout= (LinearLayout)viewStub.inflate();
if (linearLayout!= null){ 
    //加载成功
}
  1. merge它可以删减多余的层级,优化UI,例如你的主布局文件是垂直的LinearLayout,这时使用include将 my_layout.xml 引入进来。新布局也是垂直的LinearLayout,那么这个新的LinearLayout就没有任何意义了。使用的话反而增加反应时间。这时可以使用标签优化。
    merge 原理就是在解析xml时候,如果是 标签,那么直接将其中的子元素添加到merge 标签parent中,这样就保证了不会引入额外的层级
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值