美文网首页
Android屏幕适配

Android屏幕适配

作者: 时间不可逆 | 来源:发表于2019-04-11 17:51 被阅读0次
    1. px适配;
    2. 百分比适配;
    3. 修改dp适配;

    屏幕适配

    • 布局适配
      • 使用wrap_content,match_parent;
      • LinearLayout xxx:layout_weight="1"
      • RelativeLayout xxx:layout_centerParent="true"
      • ContraintLayout xxx:layout_constraintLeft_toLeftOf="parent"
      • Percent-support-lib xxx:layout_widthParcent="30%"
    • 图片资源适配
      • .9图或者SVG图实现缩放
      • 备用位图匹配不同的分辨率
    • 用户流程适配
      • 根据业务逻辑执行不同的逻辑跳转
      • 根据别名展示不同的页面
    • 限定符适配
      • 分辨率限定符 drawable-hdpi,drawable-xhdpi,...
      • 尺寸限定符 layout-small,layout-large,...
      • 最小宽度限定符 values-sw360dp,values-sw84dp,...
      • 屏幕方向限定符 layout-land,layout-port
    • 刘海屏适配
      • Android 9.0 官方适配
      • 华为,Oppo,Vivo

    自定义view中像素适配

    • 以一个特定宽度尺寸的设备为参考,在view的加载过程,根据当前设备的实际像素换算出目标像素,再作用在控件上。
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        if(!flag){
            float scaleX = Utils.getInstance(getContext()).getHorizontalScale();//获取横向的缩放比例
            float scaleY = Utils.getInstance(getContext()).getVerticalScale();//获取竖向的缩放比例
            for(int i = 0; i < getChildCount(); i++){
                View child = getChildAt(i);
                LayoutParams lp = (LayoutParams)child.getLayoutParams();
                lp.width = (int) (lp.width * scaleX);//换算宽度目标值
                lp.height = (int)(lp.height * scaleY);//换算高度的目标值
                lp.topMargin = (int)(lp.topMargin * scaleY);//换算四周间距的目标值
                ......
              }
            flag = true;
        }
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }
    

    缩放比例 是当前设备的像素值和参考值比值的结果;布局中是px来写布局;

    百分比布局适配

    • 以父容器尺寸作为参考,在view的加载过程,根据当前父容器实际尺寸换算出目标尺寸,再作用在view上。
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        //获取父容器的宽高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        ...
        for(int i = 0; i < getChildCount(); i++){
            View child = getChildAt(i);//重写设置子view的布局属性,再进行view的测量
            LayoutParams lp = (LayoutParams)child.getLayoutParams();
            float widthPercent = ((LayoutParams).lp).widthPercent;//自定义百分比属性
            if(widthPercent > 0){
                lp.width = (int) width * widthPercent;//设置当前view在父容器中的尺寸占比
            }
            ...
        }
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }
    

    注意:需要参考RelativeLayout中的静态类LayoutParams实现自定义容器布局;
    view的加载过程,view是通过容器view来进行加载;

    1. 创建自定义属性
    2. 继承 static LayoutParams,解析自定义属性;
    3. 必须重写generateLayoutParams(AttributeSet attrs)方法;
    4. 重写onMeasure,计算;

    像素密度适配

    • 修改density,scaleDensity,densityDpi值,直接更改系统内部对于目标尺寸而言的像素密度。

    px : 其实就是像素单位,比如我们通常说的手机分辨列表800*400都是px的单位
    sp : 同dp相似,还会根据用户的字体大小偏好来缩放
    dp : 虚拟像素,在不同的像素密度的设备上会自动适配
    dip: 同dp
    要理解dp,首先要先引入dpi这个概念,dpi全称是dots per inch,对角线每英寸的像素点的个数,
    而dp也叫dip,是device independent pixels。设备不依赖像素的一个单位。在不同的像素密度的设备上会自动适配,比如:
    在320x480分辨率,像素密度为160,1dp=1px
    在480x800分辨率,像素密度为240,1dp=1.5px
    计算公式:px = dp * (dpi/160)

    private static final float WIDTH = 360;//参考像素密度
    protected void setDensity(final Application application,Activity activity){
        DisplayMetics appDisplayMetrics = application.getResources().getDisplayMetrics();
        //获取目标density值
        ...
        float targetDensity = appDisplayMetrics.widthPixels / WIDTH;
        float targetScaleDensity = targetDensity * (appScaledDensity / appDensity);
        int targetDensityDpi = (int)(targetDensity * 160);
        //替换Activity的density,scaleDensity等值
        DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
        displayMetrics.density = targetDensity;
        displayMetrics.scaledDensity = targetScaleDensity;
        displayMetrics.densityDpi = targetDensityDpi;
    }
    
    //可运行代码
     private static final float WIDTH = 360;//参考屏幕的宽,单位是dp  设计稿的大小
     private static float appDensity;//表示屏幕的密度
     private static float appScaledDensity;//表示字体缩放比例 默认是density
    public static void setDensity(final Application application, Activity activity){
            final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
            if (appDensity == 0){
                appDensity = displayMetrics.density;
                appScaledDensity = displayMetrics.scaledDensity;
                //监听系统 字体变化
                application.registerComponentCallbacks(new ComponentCallbacks() {
                    @Override
                    public void onConfigurationChanged(Configuration newConfig) {
                        if (newConfig != null && newConfig.fontScale > 0){
                            appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                        }
                    }
                    @Override
                    public void onLowMemory() {
                    }
                });
            }
            //目标的值
            float targetDensity = displayMetrics.widthPixels / WIDTH;// 1080 / 360 = 3.0  --- 类比 160 /160 = 1
            float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
            int targetDensityDpi = (int) (targetDensity * 160);
            //需要修改的 值
            DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
            metrics.density = targetDensity;
            metrics.scaledDensity = targetScaledDensity;
            metrics.densityDpi = targetDensityDpi;
        }
    

    刘海屏适配

    • Android官方9.0刘海屏适配策略
      • 如果非全屏模式(有状态栏),则app不受刘海屏的影响,刘海屏的高度就是状态栏的高;
      • 如果全屏模式,app未适配刘海屏,则系统会对界面做特殊处理,竖屏向下移动,横屏向右移动
    • 全屏模式下刘海区黑边(内容区域下挫)问题。

      //1.全屏设置
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      window.setFlags(LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParamsWindowManager.FLAG_FULLSCREEN);
      //2.让内容区域延伸至刘海区,需要先判断是否有刘海
      WindowManager.LayoutParams params = window.getAttributes();
      params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
      window.setAttributes();
      //3.沉浸式
      int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
      int visibility = view.getDecorView().getSystemUiVisibility();
      visibility |= flags;
      window.getDecorView().setSystemUiVisibility(visibility);

    • 避开刘海区域

    //获取刘海高度,通常情况刘海的高度不会超过状态栏高度
    int height = getStatusBarHeight();
    //设置控件的margin
    //设置父容器的padding

    • 其他厂商的适配

    部分代码参考

     @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //1 设置全屏
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            Window window = getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
            if (hasCutout(window)) {
                //2 内容可以延伸进刘海
                WindowManager.LayoutParams attributes = window.getAttributes();
                /*
                 * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 默认
                 * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES  可以扩展
                 * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不允许
                 */
                attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
                //3.沉浸式设置
                int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;//虚拟导航栏
                int visibility = window.getDecorView().getSystemUiVisibility();//系统的显示类型
                visibility |= flags;
                window.getDecorView().setSystemUiVisibility(visibility);
            }
            setContentView(R.layout.activity_cutout);
            //1.判断手机厂商  2.判断是否有刘海  3.是否内容延伸进刘海  4.是否内容 避开刘海  5、获取刘海高
        }
    
        @TargetApi(Build.VERSION_CODES.P)
        boolean hasCutout(Window window) {
            View decorView = window.getDecorView();
            WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
            DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
                if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0) {
                    return true;
                }
            }
            return false;
        }
    
    private int getStatusBarHeight(Context context){
            int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resId > 0){
                return context.getResources().getDimensionPixelSize(resId);
            }
            return 0;
        }
    

    相关文章

      网友评论

          本文标题:Android屏幕适配

          本文链接:https://www.haomeiwen.com/subject/wwmtwqtx.html