• 布局渲染笔记


    CPU  处理逻辑计算和内存管理,显示操作。

    GPU  CPU无法显示复杂的图形,GPU用于显示复杂图形,分担CPU的任务

    xml布局到屏幕的显示流程:xml 通过 LayoutInflater 加载到内存中,然后经过CPU计算处理为多维图形,在通过 OpenGL 调用GPU,GPU对图形进行栅格化显示到屏幕上,此时如果上面流程在16毫秒内完成,则直接显示到显示器,如果没能完成,则垂直同步等待下一帧完成。

    由于人类的眼睛看到画面之帧率高于每秒约10-12帧的时候,就会认为是连贯的;

    对于有声电影的拍摄和播放帧率均为每秒24帧,对人是可以接受的,但是早期的高动态电子游戏帧率如果少于每秒30帧的话就会不连贯,因为没有动态模糊使流畅度降低,在于手机交互的过程中,如果触摸反馈60帧以下人是可以感觉出来的,60帧以上不能察觉变化,当手机上帧率低于60fps的时候会感觉画面的卡顿和迟滞现象;

    Android系统每隔16ms发出信号,触发对UI进行渲染,如果每次渲染都成功,就能达到流程的画面所需要的60fps,为了能够实现60fps,这意味着计算渲染的太多数操作都必须在16ms内完成。

    当一帧画面渲染时间超过16ms的时候,垂直同步机制(每隔16ms刷新帧率)会让显示器硬件等待GPU完成栅格化渲染操作,这样会让一帧画面多停留16ms甚至更多,这样就造成了用户看起来画面停顿。

    优化:

      CPU 减少xml转换成对象的时间,比如减少层级

      GPU 减少重复绘制

    GPU的绘制过程根据CPU传递的指令来绘制,16ms绘制一次,指令来了就绘制,哪怕是同样的绘制指令,如果层次太深,用户看不到的区域也会被绘制,以及自定义控件中,onDraw方法做了过多的绘制。

    Android手机开发者选项中,打开GPU过度绘制调试,蓝色 表示绘制了一次,淡绿色表示两次,淡红色表示三次,深红色表示四次。

    注意:Android的Theme-主题中,是自带背景的,可以设置windowBackground为null

     下面是一个过度绘制的例子,注释部分为过度绘制,打开部分为优化后的绘制,打开GPU调试后运行查看过度绘制区域,作比较

    import android.content.Context;
    import android.content.res.Resources;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import androidx.annotation.Nullable;
    
    public class DroidCardsView extends View {
    
        private int mCardSpacing = 150;//图片间隔
        private int mCardLeft = 50;//图片左侧距离
        private List<DroidCard> mDroidCards = new ArrayList<>();
        private Paint paint = new Paint();
    
        public DroidCardsView(Context context) {
            super(context);
            initCards();
        }
    
        public DroidCardsView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initCards();
        }
    
        private void initCards(){
            Resources res = getResources();
            mDroidCards.add(new DroidCard(res,R.mipmap.testpic1,mCardLeft));
            mCardLeft += mCardSpacing;
            mDroidCards.add(new DroidCard(res,R.mipmap.testpic2,mCardLeft));
            mCardLeft += mCardSpacing;
            mDroidCards.add(new DroidCard(res,R.mipmap.testpic1,mCardLeft));
            mCardLeft += mCardSpacing;
            mDroidCards.add(new DroidCard(res,R.mipmap.testpic2,mCardLeft));
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    //        for (DroidCard card : mDroidCards) {
    //            drawDroidCard(canvas,card);
    //        }
    //        invalidate();
    
            for (int i = 0;i < mDroidCards.size() -1;i++) {//最后一张不裁剪
                drawDroidCard(canvas,mDroidCards,i);
            }
            drawLeftDroidCard(canvas,mDroidCards.get(mDroidCards.size() -1));
            invalidate();
        }
    
        private void drawDroidCard(Canvas canvas,DroidCard card){
            canvas.drawBitmap(card.bitmap,card.left,0f,paint);
        }
    
        private void drawLeftDroidCard(Canvas canvas,DroidCard card){
            canvas.drawBitmap(card.bitmap,card.left,0f,paint);
        }
    
        private void drawDroidCard(Canvas canvas,List<DroidCard> list,int i){
            DroidCard card = list.get(i);
            /*Canvas中当前路径不属于保存状态,状态包含线条,平移,颜色,渐变,阴影等*/
            canvas.save();//保存画布状态,第二次绘制在起点,因为这里每一个的x坐标是以左侧为起点累加的
            //获取当前图片的 left 值,裁剪出当前图片显示的区域,并且绘制
            canvas.clipRect(card.left,0f,list.get(i+1).left,card.height);
            canvas.drawBitmap(card.bitmap,card.left,0f,paint);
            canvas.restore();
        }
    
    }
    import android.content.res.Resources;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    
    public class DroidCard {
    
        public Bitmap bitmap;
        public int left,width,height;
    
    
        public DroidCard(Resources resources,int resId,int left){
            this.bitmap = BitmapFactory.decodeResource(resources,resId);
            this.left = left;
            this.width = bitmap.getWidth();
            this.height = bitmap.getHeight();
        }
    
    }
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.example.testdemo.DroidCardsView
            android:layout_width="match_parent"
            android:layout_height="150dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </RelativeLayout>
  • 相关阅读:
    通过GUID生成可持久化的PID
    使用redisTemplate存储数据,出现xACxEDx00x05tx00
    二分查找法c语言实现
    请求路径@PathVariable注释中有点.英文句号的问题(忽略英文句号后面的后缀)
    windows下根据tcp端口查询对应的进程id(端口被占用)
    解决gradle项目每次编译都下载gradle-x.x-all.zip gradle-x.x-bin.zip
    HideTcpip.c
    ANSI C遍历二维数组指针地址
    sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    centos7安装VuePress
  • 原文地址:https://www.cnblogs.com/LiuZhen/p/10890472.html
Copyright © 2020-2023  润新知