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>