当前位置:   article > 正文

Android常用屏幕适配方式_linearlayout布局适配

linearlayout布局适配

屏幕适配

原因:Android设备碎片化,导致APP的界面元素在不同的屏幕尺寸上显示不一致。
目的:让布局,布局组件,资源,用户界面流程,匹配不同的屏幕尺寸。

屏幕适配常见方式
  • 布局适配
    1、避免写死控件尺寸,使用match_parent,wrap_content。
    2、LinearLayout使用android:layout_weight=“1”,android:weightSum="4"等等。
    3、RelativeLayout的android:layout_centerInParent=“true”,
    android:layout_centerVertical=“true”,android:layout_centerHorizontal=“true”
    4、ConstraintLayout(RelativeLayout的加强版,更强大的功能)
    5、百分比库

  • 图片资源适配
    1、使用.9或者svg图实现缩放
    2、使用多套位图匹配不同的屏幕分辨率

  • 用户流程适配
    1、根据业务逻辑执行不同的跳转逻辑
    2、根据别名展示不同的界面

  • 限定符适配
    1、分辨率限定符mipmap-hdpi,drawable-hdpi等等
    2、尺寸限定符:layout-small,layout-large等
    3、最小宽度限定符:values-sw360dp,values-sw480dp等
    4、屏幕方向限定符:layout-land,layout-port等

  • 刘海屏适配
    Android 9.0官方适配
    华为、OPPO、VIVO,、小米等等

一、像素适配

自定义像素适配,自定义View来适配
以一个特定宽度尺寸的设备为参考,在View的加载过程中,根据当前设备的实际像素换算出目标像素,再作用在控件上。

  • 工具类,计算屏幕宽高,求出目标View和参考图的缩放比
public class ScreenAdapterUtils {
    //设计稿参考宽高
    private static final float STANDARD_WIDTH = 1080;
    private static final float STANDARD_HEIGHT = 1920;

    //屏幕显示宽高
    private int mScreenWidth;
    private int mScreenHeight;

    private static ScreenAdapterUtils mInstance;

    public static ScreenAdapterUtils getInstance(Context context) {
        if (mInstance == null) {
            synchronized (ScreenAdapterUtils.class) {
                if (mInstance == null) {
                    mInstance = new ScreenAdapterUtils(context.getApplicationContext());
                }
            }
        }
        return mInstance;
    }

    private ScreenAdapterUtils(Context context) {
        if (mScreenHeight == 0 || mScreenWidth == 0) {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (wm != null) {
                DisplayMetrics metrics = new DisplayMetrics();
                wm.getDefaultDisplay().getMetrics(metrics);
                if (metrics.widthPixels > metrics.heightPixels) {
                    // 横屏
                    mScreenWidth = metrics.heightPixels;
                    mScreenHeight = metrics.widthPixels;
                } else {
                    // 竖屏
                    mScreenWidth = metrics.widthPixels;
                    mScreenHeight = metrics.heightPixels - getStatusBarHeight(context);
                }
            }
        }
    }


    public int getStatusBarHeight(Context context) {
        int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resId > 0) {
            return context.getResources().getDimensionPixelSize(resId);
        }
        return 0;
    }

    //获取水平方向的缩放比例
    public float getHorizontalScale(){
        return mScreenWidth / STANDARD_WIDTH;
    }

    //获取垂直方向的缩放比例
    public float getVerticalScale(){
        return mScreenHeight / STANDARD_HEIGHT;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 适配的ViewGroup,在onMeasure中根据缩放比,重新去计算目标View的宽高
public class ScreenAdapterLayout extends LinearLayout {
    /**
     * 是否被测量
     */
    private boolean isMeasure;

    public ScreenAdapterLayout(Context context) {
        super(context);
    }

    public ScreenAdapterLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    public ScreenAdapterLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!isMeasure) {
            // 获取横向缩放比
            float horizontalScale = ScreenAdapterUtils.getInstance(getContext()).getHorizontalScale();
            // 获取纵向缩放比
            float verticalScale = ScreenAdapterUtils.getInstance(getContext()).getVerticalScale();

            // 重新计算所有子View的尺寸
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                // 获取子View重新进行测量
                View child = getChildAt(i);
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                // 根据宽高的缩放比,重新计算目标View的真实宽高
                params.width = (int) (params.width * horizontalScale);
                // 这样缩放,如果我们设置的宽高是一样的,你会发现在水平和数值方向上的缩放比不一样的时候
                // 控件会被拉伸,不能达到我们想要的正确的相同宽高
                // params.height = (int) (params.height * verticalScale);
                // 如果改成都使用水平的缩放比,就刚好正确
                params.height = (int) (params.height * horizontalScale);

                // 计算目标View四周的间距
                params.leftMargin = (int) (params.leftMargin * horizontalScale);
                params.rightMargin = (int) (params.rightMargin * horizontalScale);
                params.topMargin = (int) (params.topMargin * verticalScale);
                params.bottomMargin = (int) (params.bottomMargin * verticalScale);
            }
        }


        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

布局文件

<com.dh.summarize.view.ScreenAdapterLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:layout_width="540px"
            android:layout_height="540px"
            android:background="@color/colorAccent"/>

</com.dh.summarize.view.ScreenAdapterLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

自定义View像素适配,不能穿透适配,也就是当前容器是像素适配容器,它其中如果还有容器需要适配,也依然要使用像素适配容器。造成的结果就是如果需求更改,会改的很惨。O(∩_∩)O哈哈~。

二、百分比布局适配

自定义百分比适配,继承对应的ViewGroup,在使用百分比适配的情况下,不影响ViewGroup本身的属性的使用。

Google本身提供了百分比库,我们正常情况下需要使用直接导入Google的百分比库就好。

以下例子我们直接继承RelativeLayout,在

  • onMeasure()中去重新计算我们的宽高。
  • 继承RelativeLayout的LayoutParams,实现以下构造方法LayoutParams(Context c, AttributeSet attrs),在构造方法中去获取我们自定义属性,这个可以参考RelativeLayout本身的实现。
  • 实现generateLayoutParams,将我们自己的LayoutParams返回
public class PercentRelativeLayout extends RelativeLayout {
    public PercentRelativeLayout(Context context) {
        super(context);
    }

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

    public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 获取父容器的宽高
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
            // 判断LayoutParams是否是百分比属性
            if (layoutParams instanceof LayoutParams) {
                LayoutParams params = (LayoutParams) layoutParams;
                float widthPercent = params.widthPercent;
                float heightPercent = params.heightPercent;
                float marginLeftPercent = params.marginLeftPercent;
                float marginRightPercent = params.marginRightPercent;
                float marginTopPercent = params.marginTopPercent;
                float marginBottomPercent = params.marginBottomPercent;

                if (widthPercent > 0) {
                    params.width = (int) (parentWidth * widthPercent);
                }
                if (heightPercent > 0) {
                    params.height = (int) (parentHeight * heightPercent);
                }
                if (marginLeftPercent > 0) {
                    params.leftMargin = (int) (parentWidth * marginLeftPercent);
                }
                if (marginRightPercent > 0) {
                    params.rightMargin = (int) (parentWidth * marginRightPercent);
                }
                if (marginTopPercent > 0) {
                    params.topMargin = (int) (parentHeight * marginTopPercent);
                }
                if (marginBottomPercent > 0) {
                    params.bottomMargin = (int) (parentHeight * marginBottomPercent);
                }
            }
        }


        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    public static class LayoutParams extends RelativeLayout.LayoutParams {
        private float widthPercent;
        private float heightPercent;
        private float marginLeftPercent;
        private float marginRightPercent;
        private float marginTopPercent;
        private float marginBottomPercent;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            @SuppressLint("CustomViewStyleable")
            TypedArray typedArray = c.obtainStyledAttributes(attrs, R.styleable.PercentRelativeLayout);
            widthPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_widthPercent, 0);
            heightPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_heightPercent, 0);
            marginLeftPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_marginLeftPercent, 0);
            marginRightPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_marginRightPercent, 0);
            marginTopPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_marginTopPercent, 0);
            marginBottomPercent = typedArray.getFloat(R.styleable.PercentRelativeLayout_marginBottomPercent, 0);

            typedArray.recycle();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

具体使用

<com.dh.summarize.view.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            android:textSize="20sp"
            android:text="宽:33%,高:50%"
            app:widthPercent = "0.33"
            app:heightPercent = "0.5"
            android:gravity="center_vertical"/>

</com.dh.summarize.view.PercentRelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

我们设置的百分比都是相对于父容器的,相对于父容器的宽度百分比,高度百分比
在这里插入图片描述

三、修改像素密度

我最早知道是今日头条开放出来的,具体可以参考
字节跳动:一种极低成本的Android屏幕适配方式

修改density(屏幕密度),scaleDensity(一般用于字体,通常情况下与density相等),densityDpi(屏幕上每一英寸的像素点)的值,直接更改系统内部对于目标尺寸而言的像素密度。


android中的dp在渲染前会将dp转为px,计算公式:
  • px = density * dp;
  • density = dpi / 160;
  • px = dp * (dpi / 160);
    而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的。

具体工具类

就是根据设计稿去修改目标Activity的Density等值

public class DensityUtils {
    /**
     * 设计稿宽度(dp)
     */
    private static final float DEFAULT_WIDTH = 360;
    // 屏幕密度
    private static float appDensity;
    // 字体缩放比,默认就是appDensity
    private static float appScaleDensity;


    public static void setCustomDensity(Application application, Activity activity) {
        DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();

        if (appDensity == 0) {
            // 初始化值
            appDensity = displayMetrics.density;
            appScaleDensity = displayMetrics.scaledDensity;

            // 由于我们在系统设置中切换了文字大小,需要改变应用本身的文字大小
            application.registerComponentCallbacks(new ComponentCallbacks() {
                // 监听系统配置切换
                @Override
                public void onConfigurationChanged(@NonNull Configuration newConfig) {
                    if (newConfig != null && newConfig.fontScale > 0) {
                        // 重新获取scaleDensity
                        appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }

        // 计算所有的目标值
        float targetDensity = displayMetrics.widthPixels / DEFAULT_WIDTH;
        float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
        // density = dpi / 160
        int targetDensityDpi = (int) (targetDensity * 160);

        // 将Activity的Density,ScaleDensity,DensityDpi
        DisplayMetrics dm = activity.getResources().getDisplayMetrics();
        dm.density = targetDensity;
        dm.scaledDensity = targetScaleDensity;
        dm.densityDpi = targetDensityDpi;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

工具类具体调用在Activity的onCreate()的setContentView()方法之前。可以在每个Activity单独设置,也可以在BaseActivity中设置,或者在Application实现Activity的监听。

Application的onCreate()中监听,自己实现一个BaseApplication继承Application。

registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
	@Override
	public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
		DensityUtils.setCustomDensity(this, activity)
	}

	@Override
	public void onActivityStarted(Activity activity) {

	}

	@Override
	public void onActivityResumed(Activity activity) {

	}

	@Override
	public void onActivityPaused(Activity activity) {

	}

	@Override
	public void onActivityStopped(Activity activity) {

	}

	@Override
	public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

	}

	@Override
	public void onActivityDestroyed(Activity activity) {

	}
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

布局文件,我们适配稿给的360dp的宽度,所以我们分别对两个控件设置180dp,实际适配之后结果应该是平分屏幕宽度。

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:id="@+id/tvOne"
            android:layout_width="180dp"
            android:layout_height="100dp"
            android:background="@color/colorAccent"
            android:textSize="20sp"
            android:text="测试1"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:gravity="center"/>

    <TextView
            android:id="@+id/tvTwo"
            android:layout_width="180dp"
            android:layout_height="100dp"
            android:background="@color/colorAccent"
            android:textSize="20sp"
            android:text="测试2"
            app:layout_constraintTop_toBottomOf="@id/tvOne"
            app:layout_constraintStart_toEndOf="@id/tvOne"
            android:gravity="center"/>

</androidx.constraintlayout.widget.ConstraintLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

效果对比,适配前和适配后:
在这里插入图片描述
最后,各种适配方式没有什么是最好的,合适的才是最好的,有句话怎么说的(不适配就是最好的适配),哈哈。平时的适配根据具体项目具体选择采用什么样的适配方式。还有我们可以选择对整体界面适配也可以对单独的某一块进行适配。

四、刘海屏适配

Android 9.0官方适配方式

  • 如果非全屏模式(有状态栏),则APP不受刘海屏影响,刘海屏的高度就是状态栏的高度。
  • 如果是全屏模式,APP未适配刘海屏,系统会对界面做特殊处理,竖屏向下移动,横屏向右移动。

其他手机厂商的适配:

官方适配方案

日常适配刘海屏,我们需要对厂商的版本单独处理,很多厂商都有他们自己的适配方式。比如华为,小米,OPPO等。

1、取消标题栏,设置全屏
2、判断手机厂商
3、判断手机是否有刘海
4、设置是否让内容区域延伸进刘海
5、设置控件是否避开刘海区域
6、获取刘海的高度(一般就是状态栏的高度)

设置全屏,设置内容延伸至刘海等操作都是在setContentView()方法之前

class ShapedScreenActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 1.设置全屏
        // 取消标题栏
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        window?.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )

        // 手机厂商:华为,小米,OPPO等
        // 1.判断手机厂商,
        // 2,判断手机是否有刘海,
        // 3,设置是否让内容区域延伸进刘海
        // 4,设置控件是否避开刘海区域
        // 5,获取刘海的高度

        // 2.判断是否是刘海屏
        if (hasDisplayCutout(window)) {
            // 将内容区域延伸进刘海
            val attributes = window?.attributes
            /*
             * LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 全屏模式,内容下移,非全屏不受影响
             * LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 允许内容去延伸进刘海区
             * LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不允许内容延伸进刘海区
             */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                attributes?.layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
                window?.attributes = attributes
            }


            //3.设置成沉浸式
            val flags =
                View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            var systemUiVisibility = window?.decorView?.systemUiVisibility
            systemUiVisibility = systemUiVisibility?.or(flags)
            window?.decorView?.systemUiVisibility = systemUiVisibility ?: 0
        }

        setContentView(R.layout.activity_shaped_screen)

        // 4,设置控件是否避开刘海区域
        val layoutParams: ConstraintLayout.LayoutParams =
            tvShape.layoutParams as ConstraintLayout.LayoutParams
        layoutParams.topMargin = getStatusBarHeight()
        tvShape.layoutParams = layoutParams

    }

    /**
     * 判断是否是刘海屏
     */
    private fun hasDisplayCutout(window: Window?): Boolean {
        val displayCutout: DisplayCutout?
        val decorView = window?.decorView
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val rootWindowInsets = decorView?.rootWindowInsets
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && rootWindowInsets != null) {
                displayCutout = rootWindowInsets.displayCutout
                if (displayCutout != null) {
                    // 判断刘海屏区域数量,判断刘海区域是否高度大于0
                    if (displayCutout.boundingRects.size > 0 && displayCutout.safeInsetBottom > 0) {
                        return true
                    }
                }
            }
        }
        return false
    }

    /**
     * 获取状态栏高度(通常情况下,刘海的高度等于状态栏的高度)
     */
    private fun getStatusBarHeight(): Int {
        val resId = resources.getIdentifier("status_bar_height", "dimen", "android")
        if (resId > 0) {
            return resources.getDimensionPixelSize(resId)
        }
        return 0
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
五、单独适配

对需要适配的布局进行单独适配,相对来说更麻烦,但是效果也是最好的。

  1. 跟屏幕相关的缩放比,水平,竖直高度的工具类
public class UIUtils {
    private static UIUtils instance;

    // 设计稿尺寸,
    private static final float DEFAULT_WIDTH = 1080f;
    private static final float DEFAULT_HEIGHT = 1920;

    private float displayMetricsWidth;
    private float displayMetricsHeight;
    private float mStatusBarHeight;

    private UIUtils(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        if (wm == null) return;
        DisplayMetrics displayMetrics = new DisplayMetrics();
        if (displayMetricsWidth == 0 || displayMetricsHeight == 0) {
            // 忽略掉了导航栏高度
            wm.getDefaultDisplay().getMetrics(displayMetrics);
            // 获取真实的屏幕尺寸
            //wm.getDefaultDisplay().getRealMetrics(displayMetrics);
            mStatusBarHeight = getSystemBarHeight(context);
            // 判断横屏还是竖屏
            if (displayMetrics.widthPixels > displayMetrics.heightPixels) {
                // 横屏
                displayMetricsWidth = displayMetrics.heightPixels;
                displayMetricsHeight = displayMetrics.widthPixels;
                // 如果不是沉浸式,我们还需要减去状态栏的高度
                // displayMetricsWidth = displayMetrics.heightPixels - mStatusBarHeight;
            } else {
                displayMetricsWidth = displayMetrics.widthPixels;
                displayMetricsHeight = displayMetrics.heightPixels;
            }
        }
    }

    public static UIUtils getInstance(Context context) {
        if (instance == null) {
            synchronized (UIUtils.class) {
                if (instance == null) {
                    instance = new UIUtils(context.getApplicationContext());
                }
            }
        }
        return instance;
    }


    public static UIUtils getInstance() {
        if (instance == null) {
            throw new NullPointerException("UIUtils未被初始化");
        }
        return instance;
    }

    /**
     * 水平缩放比
     *
     * @return
     */
    public float getHorizontalScaleValue() {
        return displayMetricsWidth / DEFAULT_WIDTH;
    }

    /**
     * 竖直缩放比
     *
     * @return
     */
    public float getVerticalScaleValue() {
        return displayMetricsHeight / DEFAULT_HEIGHT;
    }

    /**
     * 获取宽度
     * @param width
     * @return
     */
    public int getWidth(int width) {
        return Math.round((float) width * displayMetricsWidth / DEFAULT_WIDTH);
    }

    /**
     * 获取高度
     * @param height
     * @return
     */
    public int getHeight(int height) {
        return Math.round((float) height * displayMetricsHeight / DEFAULT_HEIGHT);
    }

    /**
     * 用于得到状态框的高度
     */
    public int getSystemBarHeight(Context context) {
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        int height = context.getResources().getDimensionPixelSize(resourceId);
        if (height != -1) {
            return height;
        }
        return getValue(context, "com.android.internal.R$dimen", "system_bar_height", 48);
    }


    private int getValue(Context context, String dimeClass, String system_bar_height, int defaultValue) {
        try {
            Class<?> clazz = Class.forName(dimeClass);
            Object object = clazz.newInstance();
            Field field = clazz.getField(system_bar_height);
            Object obj = field.get(object);
            if (obj == null) return defaultValue;
            int id = Integer.parseInt(obj.toString());
            return context.getResources().getDimensionPixelSize(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return defaultValue;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  1. 对单独布局进行适配的工具类
public class ViewCalculateUtils {

    /**
     * 设置字体大小
     *
     * @param textView
     * @param size
     */
    public static void setTextSize(TextView textView, int size) {
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, UIUtils.getInstance().getHeight(size));
    }


    /**
     * @param view
     * @param width
     * @param height
     * @param lefMargin
     * @param topMargin
     * @param rightMargin
     * @param bottomMargin
     * @param asWidth      true 不改变宽高的缩放比
     */
    public static void setViewRelativeLayoutParam(View view, int width, int height, int lefMargin, int topMargin,
                                                  int rightMargin, int bottomMargin, @Nullable boolean asWidth) {
        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
        if (layoutParams == null) return;
        if (width != RelativeLayout.LayoutParams.MATCH_PARENT
                && width != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.width = UIUtils.getInstance().getWidth(width);
        } else {
            layoutParams.width = width;
        }

        if (height != RelativeLayout.LayoutParams.MATCH_PARENT
                && height != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.height = asWidth ? UIUtils.getInstance().getWidth(height) : UIUtils.getInstance().getHeight(height);
        } else {
            layoutParams.height = height;
        }
        layoutParams.topMargin = asWidth ? UIUtils.getInstance().getWidth(topMargin) : UIUtils.getInstance().getHeight(topMargin);
        layoutParams.bottomMargin = asWidth ? UIUtils.getInstance().getWidth(bottomMargin) : UIUtils.getInstance().getHeight(bottomMargin);
        layoutParams.leftMargin = UIUtils.getInstance().getWidth(lefMargin);
        layoutParams.rightMargin = UIUtils.getInstance().getWidth(rightMargin);
        view.setLayoutParams(layoutParams);
    }

    /**
     * 设置view的内边距
     *
     * @param view
     * @param topPadding
     * @param bottomPadding
     * @param leftPadding
     * @param rightPadding
     */
    public static void setViewPadding(View view, int topPadding, int bottomPadding, int leftPadding, int rightPadding) {
        view.setPadding(UIUtils.getInstance().getWidth(leftPadding),
                UIUtils.getInstance().getHeight(topPadding),
                UIUtils.getInstance().getWidth(rightPadding),
                UIUtils.getInstance().getHeight(bottomPadding));
    }

    /**
     * @param view
     * @param width
     * @param height
     * @param topMargin
     * @param bottomMargin
     * @param lefMargin
     * @param rightMargin
     */
    public static void setViewFrameLayoutParam(View view, int width, int height, int topMargin, int bottomMargin, int lefMargin,
                                               int rightMargin, @Nullable boolean asWidth) {

        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();
        if (width != RelativeLayout.LayoutParams.MATCH_PARENT
                && width != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.width = UIUtils.getInstance().getWidth(width);
        } else {
            layoutParams.width = width;
        }
        if (height != RelativeLayout.LayoutParams.MATCH_PARENT
                && height != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.height = asWidth ? UIUtils.getInstance().getWidth(height) : UIUtils.getInstance().getHeight(height);
        } else {
            layoutParams.height = height;
        }

        layoutParams.topMargin = asWidth ? UIUtils.getInstance().getWidth(topMargin) : UIUtils.getInstance().getHeight(topMargin);
        layoutParams.bottomMargin = asWidth ? UIUtils.getInstance().getWidth(bottomMargin) : UIUtils.getInstance().getHeight(bottomMargin);
        layoutParams.leftMargin = UIUtils.getInstance().getWidth(lefMargin);
        layoutParams.rightMargin = UIUtils.getInstance().getWidth(rightMargin);
        view.setLayoutParams(layoutParams);
    }


    /**
     * @param view
     * @param width
     * @param height
     * @param topMargin
     * @param bottomMargin
     * @param lefMargin
     * @param rightMargin
     * @param asWidth
     */
    public static void setViewLinearLayoutParam(View view, int width, int height, int topMargin, int bottomMargin, int lefMargin,
                                                int rightMargin, @Nullable boolean asWidth) {

        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
        if (width != RelativeLayout.LayoutParams.MATCH_PARENT && width != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.width = UIUtils.getInstance().getWidth(width);
        } else {
            layoutParams.width = width;
        }
        if (height != RelativeLayout.LayoutParams.MATCH_PARENT && height != RelativeLayout.LayoutParams.WRAP_CONTENT) {
            layoutParams.height = asWidth ? UIUtils.getInstance().getWidth(height) : UIUtils.getInstance().getHeight(height);
        } else {
            layoutParams.height = height;
        }

        layoutParams.topMargin = asWidth ? UIUtils.getInstance().getWidth(topMargin) : UIUtils.getInstance().getHeight(topMargin);
        layoutParams.bottomMargin = asWidth ? UIUtils.getInstance().getWidth(bottomMargin) : UIUtils.getInstance().getHeight(bottomMargin);
        layoutParams.leftMargin = UIUtils.getInstance().getWidth(lefMargin);
        layoutParams.rightMargin = UIUtils.getInstance().getWidth(rightMargin);
        view.setLayoutParams(layoutParams);
    }

    /**
     * @param view
     * @param width
     * @param height
     * @param asWidth
     */
    public static void setViewGroupLayoutParam(View view, int width, int height, @Nullable boolean asWidth) {

        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
        if (width != RelativeLayout.LayoutParams.MATCH_PARENT && width != RelativeLayout.LayoutParams.WRAP_CONTENT && width != RelativeLayout.LayoutParams.FILL_PARENT) {
            layoutParams.width = UIUtils.getInstance().getWidth(width);
        } else {
            layoutParams.width = width;
        }
        if (height != RelativeLayout.LayoutParams.MATCH_PARENT && height != RelativeLayout.LayoutParams.WRAP_CONTENT && height != RelativeLayout.LayoutParams.FILL_PARENT) {
            layoutParams.height = asWidth ? UIUtils.getInstance().getWidth(height) : UIUtils.getInstance().getHeight(height);
        } else {
            layoutParams.height = height;
        }
        view.setLayoutParams(layoutParams);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
加深理解的文章
  1. 骚年你的屏幕适配方式该升级了!-今日头条适配方案
  2. 骚年你的屏幕适配方式该升级了!-SmallestWidth 限定符适配方案
  3. 今日头条屏幕适配方案终极版正式发布!
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/311194
推荐阅读
相关标签
  

闽ICP备14008679号