屏幕适配的策略总结

1.基于像素的屏幕适配

比如美工的切图只有px 

那么 可以先拿到设计图片的宽高

写个utils类 通过

WindowManager,
manager.getDefaultDisplay().getMetrics(displayMetrics);

获取屏幕宽高, 这时候还要判断屏幕的横竖屏 通过宽和高的值比较

然后计算宽高的缩放比 ,

手机实际宽度/设计稿宽度 =宽度的缩放比 

手机实际高度/设计稿高度 =高度的缩放比 

自定义一个ScreenAdapterLayout,继承 RelativeLayout

在onMeasure中遍历child    拿到View   再拿到LayoutParam  然后  让这其中的 margin  左上右下  和宽 高都乘上  对应的宽度缩放比和高度缩放比, 赋值到原来的LayoutParam中,这个过程只能赋值一次,如果多次绘制 就会出问题 所以还要加个flag 

然后 就可以用自定义的   ScreenAdapterLayout替代RelativeLayout

这样设计的一个问题是 布局中的所有空间都必须用px来指定宽高

优点是,只要拿到设计图的宽高,在任何手机上都能拿到和设计图一样的效果

具体代码如下

public class ScreenAdapterLayout extends RelativeLayout {

    private boolean flag;

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!flag){
            float scaleX = Utils.getInstance(getContext()).getHorizontalScale();
            float scaleY = Utils.getInstance(getContext()).getVerticalScale();

            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                params.width = (int) (params.width * scaleX);
                params.height = (int) (params.height * scaleY);
                params.leftMargin = (int)(params.leftMargin * scaleX);
                params.rightMargin = (int)(params.rightMargin * scaleX);
                params.topMargin = (int)(params.topMargin * scaleY);
                params.bottomMargin = (int)(params.bottomMargin * scaleY);
            }
            flag = true;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
public class Utils {

    private static Utils utils;

    //这里是设计稿参考宽高
    private static final float STANDARD_WIDTH = 1080;
    private static final float STANDARD_HEIGHT = 1920;

    //这里是屏幕显示宽高
    private int mDisplayWidth;
    private int mDisplayHeight;

    private Utils(Context context){
        //获取屏幕的宽高
        if(mDisplayWidth == 0 || mDisplayHeight == 0){
            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (manager != null){
                DisplayMetrics displayMetrics = new DisplayMetrics();
                manager.getDefaultDisplay().getMetrics(displayMetrics);
                if (displayMetrics.widthPixels > displayMetrics.heightPixels){
                    //横屏
                    mDisplayWidth = displayMetrics.heightPixels;
                    mDisplayHeight = displayMetrics.widthPixels;
                }else{
                    mDisplayWidth = displayMetrics.widthPixels;
                    mDisplayHeight = displayMetrics.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 static Utils getInstance(Context context){
        if (utils == null){
            utils = new Utils(context.getApplicationContext());
        }
        return utils;
    }

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

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

}
<?xml version="1.0" encoding="utf-8"?>
<com.netease.screenadapter.pixel.ScreenAdapterLayout 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">

    <TextView
        android:layout_width="540px"
        android:layout_height="540px"
        android:layout_marginLeft="10px"
        android:text="Hello World!"
        android:background="@color/colorAccent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</com.netease.screenadapter.pixel.ScreenAdapterLayout>

2.百分比布局的使用

使用依赖:

implementation "androidx.percentlayout:percentlayout:1.0.0"

代码实现:

<androidx.percentlayout.widget.PercentRelativeLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:layout_width="match_parent"
          android:layout_height="match_parent">
      <ImageView
          app:layout_widthPercent="50%"
          app:layout_heightPercent="50%"
          app:layout_marginTopPercent="25%"
          app:layout_marginLeftPercent="25%"/>
  </androidx.percentlayout.widget.PercentRelativeLayout>

 这样做的好处是  布局会随着屏幕改变而改变

坏处就是,美工不可能每次都给你百分比的宽高,所以,这些参数都必须自己计算

3.修改density适配(比较推荐的方法,目前缺点未知)

Dpi概念 手机屏幕每英寸所包含像素点的数量

通过设置参考的屏幕宽度 字体缩放比 根据设备实际宽度 修改屏幕密度 .

先拿到系统的 density 屏幕密度,scaleDensity字体缩放比.

而自定义的屏幕密度是 = 屏幕宽度/参考的屏幕宽度

自定义的字体缩放大小=自定义屏幕密度*屏幕原本的密度/原屏幕字体缩放比

自定义的DensityDpi= 自定义的屏幕密度*160  

而在android中 当每英寸像素为 160 ,每英寸dpi为160是 此时 1dp=1px, 160也是android中的一个参考值,基准值

设计这个类的时候还要考虑到用户系统设置字体大小的时候, 字体也跟着改变

所以要加registerComponentCallbacks的监听  ,在onConfigurationChanged 方法中重新赋值字体缩放比

这个工具类的使用:

1.可以写在BaseActivity里的oncreate方法里面 

2.也可以再MyApplication中注册一个activity生命周期的监听, registerActivityLifecycleCallbacks,只要有activity,onActivityCreated就会调用

可以再这个方法中配置Density.setDensity方法


        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                Density.setDensity(App.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) {

            }
        });
public class Density {

    private static final float  WIDTH = 320;//参考设备的宽,单位是dp 320 / 2 = 160

    private static float appDensity;//表示屏幕密度
    private static float appScaleDensity; //字体缩放比例,默认appDensity

    public static void setDensity(final Application application, Activity activity){
        //获取当前app的屏幕显示信息
        DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
        if (appDensity == 0){
            //初始化赋值操作
            appDensity = displayMetrics.density;
            appScaleDensity = displayMetrics.scaledDensity;

            //添加字体变化监听回调
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    //字体发生更改,重新对scaleDensity进行赋值
                    if (newConfig != null && newConfig.fontScale > 0){
                        appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }

        //计算目标值density, scaleDensity, densityDpi
        float targetDensity = displayMetrics.widthPixels / WIDTH; // 1080 / 360 = 3.0
        float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
        int targetDensityDpi = (int) (targetDensity * 160);

        //替换Activity的density, scaleDensity, densityDpi
        DisplayMetrics dm = activity.getResources().getDisplayMetrics();
        dm.density = targetDensity;
        dm.scaledDensity = targetScaleDensity;
        dm.densityDpi = targetDensityDpi;
    }

}

4.头条的屏幕适配(比较容易,用的人也很多,推荐)

使用方式
第一步: 在module的build.gradle文件下做如下依赖

   
    implementation 'me.jessyan:autosize:1.1.2'
第二步: 在 AndroidManifest 中填写全局设计图尺寸 (单位 dp),如果使用副单位,则可以直接填写像素尺寸,不需要再将像素转化为 dp

<manifest>
<application>            
    <meta-data
        android:name="design_width_in_dp"
        android:value="360"/>
    <meta-data
        android:name="design_height_in_dp"
        android:value="667"/>           
 </application>           
</manifest>
发布了137 篇原创文章 · 获赞 29 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/xiexiaotian11/article/details/103194598