为什么要屏幕适配
同一个界面需要在不同尺寸的屏幕上显示,即使屏幕的尺寸一样,密度也可能不一样。导致App的界面元素在不同屏幕尺寸上显示不一致。所以我们让布局,布局组件,资源,用户界面流程,匹配不同屏幕尺寸。
屏幕适配常见方式
布局适配
- 避免写死控件尺寸,使用wrap_content, match_parent
- LinearLayout xxx:layout_weight=“0.5” //线性布局使用权重来布局
- RelativeLayout xxx:layout_centerInParent=“true” //使用相对布局
- ContraintLayout xxx:layout_constraintLeft_toLeftOf=“parent”…//使用约束布局
- Percent-support-lib xxx:layout_widthPercent=“30%” …//使用百分比布局
图片资源适配
- .9图或者SVG图实现缩放
- 备用位图匹配不同分辨率
限定符适配
- 分辨率限定符 drawable-hdpi ,drawable-xdpi,…
- 尺寸限定符 layout-small,layout-large,…
- 最小宽度限定符 values-sw360dp,values-sw384dp, …
- 屏幕方向限定符 layout-land,layout-part
特殊屏幕适配
比如刘海屏,水滴屏幕等
自定义View屏幕适配的具体方案:
如下图:设计师给出来是720*1280尺寸的设计稿,我们需要适配不同屏幕上的尺寸,怎么适配,
如图:720 上显示350的宽,如果在1080上也显示350,我们会发现比例不对。
所以我们需要改成525,那么525是怎么计算出来。
width = 350/720 * 1080 或者是 width = 1080 / 720 * 350
但是我们不可能每次都去这么算。下面我们写一个Utils专门做这个事情,使用该Utils必须设置设计稿的参考宽高,如下代码
//这里设计稿参考宽高
private static final float STANDARD_WIDTH = 720;
private static final float STANDARD_HEIGHT = 1280;
public class Utils {
private static Utils utils;
//这里设计稿参考宽高
private static final float STANDARD_WIDTH = 720;
private static final float STANDARD_HEIGHT = 1280;
//这里是屏幕显示的宽高
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 getHorizaontalScale(){
return mDisplayWidth / STANDARD_WIDTH;
}
//获取垂直方向上的缩放比例
public float getVerticalScale(){
return mDIsplayHeight / STANDARD_HEIGHT;
}
}
然后我们自定义一个View继承RelativeLayout,来让所有的子View都自己进行适配。根据当前设备的实际像素换算出目标像素,在作用到控件上
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()).getHorizaontalScale();//获取横向缩放比例
float scaley = Utils.getInstance(getContext()).getVerticalScale();//获取竖向缩放比例
for (int i = 0; i < getChildCount(); i++){
View child = getChildAt(i);//重新设置子View的布局属性,再进行View的测量
LayoutParams lp = (LayoutParams)child.getLayoutParams();
lp.width = (int) (lp.width * scalex);//换算宽度目标值
lp.height = (int) (lp.height * scaley);//换算高度目标值
lp.topMargin = (int) (lp.topMargin * scaley);//换算四周间距的目标值
lp.bottomMargin = (int) (lp.bottomMargin * scaley);//换算四周间距的目标值
lp.leftMargin = (int) (lp.leftMargin * scalex);//换算四周间距的目标值
lp.rightMargin = (int) (lp.rightMargin * scalex);//换算四周间距的目标值
}
flag = true;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
使用我们自定义的View做为布局的根View就能实现屏幕适配。