学习笔记:屏幕适配方案(像素适配)

264 阅读3分钟

思路:通过实际的屏幕大小比上设计图的屏幕大小,得到一个缩放值,然后自定义一个ViewGroup,在该ViewGroup的OnMeasure中重新计算每个子View的大小。

比如现在有个控件,宽高各是屏幕的一半,假设我们的屏幕是720p(720*1280)的屏幕,那这个控件的宽和高就应该是 360 * 640,如下图所示:

那么,当我们的屏幕大小变成1080 * 1920的时候,我还想要这个控件宽和高各占屏幕的一半的时候,这时候控件的宽和高就变成了540 * 960,才能实现占一半的效果:

现在需求明确了:无论屏幕大小怎么变,我要这个控件在屏幕中所显示的比例始终是一样的;

然后开始找规律:

//view的宽度等于  屏幕宽度/基准宽度*基准大小
540 = 1080/720*360

960 = 1920/1280*640

所以我们只要找到这个比例,再对设计图上的尺寸做对应的缩放就能实现屏幕布局的适配了。

撸码

1.目录结构

2.需要一个util类来帮助获取屏幕的相关信息

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;

public class ScreenAdapterUtils {

    /**
     * 基准宽高
     */
    private float standardWidth = 720;
    private float standardHeight = 1280;

    /**
     * 当前屏幕宽高
     */
    private float width;
    private float height;

    public static ScreenAdapterUtils instance;

    private ScreenAdapterUtils(Context context) {
        //如果没有获取过屏幕信息
        if (width == 0 || height == 0) {
            //通过Context获取WindowManager
            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            //实例化一个DisplayMetrics
            DisplayMetrics displayMetrics = new DisplayMetrics();
            //通过wm获取屏幕信息,并保存在displayMetrics中
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);

            if (displayMetrics.widthPixels > displayMetrics.heightPixels) {
                // 横屏模式
                width = displayMetrics.heightPixels;
                height = displayMetrics.widthPixels - getStateBar(context);

            } else {
                //竖屏模式
                width = displayMetrics.widthPixels;
                height = displayMetrics.heightPixels - getStateBar(context);
            }
        }
    }

    /**
     * 获取状态栏高度
     * @param context
     * @return
     */
    private float getStateBar(Context context) {
        float result = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = context.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

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

    //获取宽度缩放值
    public float getScaleX() {
        return width / standardWidth;
    }

    //获取高度缩放值
    public float getScaleY() {
        return height / standardHeight;
    }
}

3.自定义相关ViewGroup

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import com.ruiheng.antqueen.screenadapter.ScreenAdapterUtils;
import androidx.annotation.Nullable;

public class LinearLayout extends android.widget.LinearLayout {

    /**
     * measure方法会执行多次,只对view做一次改变
     * measure方法会执行多次的原因:
     * https://www.jianshu.com/p/733c7e9fb284
     */
    private boolean flag;

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!flag) {
            // 获取缩放比例
            float scaleX = ScreenAdapterUtils.getInstance(getContext()).getScaleX();
            float scaleY = ScreenAdapterUtils.getInstance(getContext()).getScaleY();

            int count = getChildCount();

            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
                // 重新对宽高赋值
                layoutParams.width = (int) (layoutParams.width * scaleX);
                layoutParams.height = (int) (layoutParams.height * scaleY);

                // 重新对X方向margin赋值
                layoutParams.rightMargin = (int) (layoutParams.rightMargin * scaleX);
                layoutParams.leftMargin = (int) (layoutParams.leftMargin * scaleX);

                // 重新对Y方向margin赋值
                layoutParams.topMargin = (int) (layoutParams.topMargin * scaleY);
                layoutParams.bottomMargin = (int) (layoutParams.bottomMargin * scaleY);
                // 重设属性
                child.setLayoutParams(layoutParams);
            }
            flag = true;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

4.如何使用

在xml布局文件中使用自己定义的ViewGroup,上面是定义了一个LinearLayout,可以根据需要重写FrameLayout,RelativeLayout。。等等需的任何layout,重写的时候只需要把上面的LinearLayout的onMeasure cv过去就行了。

在使用的时候,宽高设置都按照基准大小来设置,需要注意的是单位必须是px,因为在对view进行缩放的时候都是使用的px

效果:

棒子手机自带切换屏幕分辨率,不管怎么切换,出来的效果都是宽高各占一半效果

5.over