Android屏幕适配

  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屏幕适配)