这里就以荣耀V9为例 物理分辨率为1440*2560,5.7英寸
dpi: 这个是系统指定的为640
scaleDensity: scaleDensity=dpi/160由于dpi是系统指定所以scaleDensity=640/160=4
ppi: 每英寸所包含的像素点个数
逻辑分辨率 = (物理分辨率) / 缩放因子=360*640
渲染的分辨率又为 逻辑分辨率*缩放因子=1440*2560
在我们Android里面1dp = 1px*scaleDensity = 1px*dpi/160
所以荣耀V9手机: 1dp = 1px*640/160=4px; 假如我们在布局里面输出360dp的话那么渲染到屏幕上就是4*360个像素点=1440屏幕的宽度.
在很多公司的切图都是以IOS的750作为切图标准,而且只切一套图,这个时候就可以根据修改上面的dpi的 density来达到适配
density = 1440/750=1.92
dpi =density*160=307.2
这样逻辑分辨率就为(1440*2560)/1.92=750*1333
那么当输入390dp的时候会显示多少像素呢?
1dp = 1px*scaleDensity这个公式不变
1dp = 1.92px; 390dp = 748.8px这只是逻辑宽度 实际渲染的宽度就等于748.8*1.92=1437.696 相当于一个屏幕宽度
1:图片资源适配:.9图和SVG图片进行缩放
2:布局适配
3:像素适配
4:限定符适配:比如分辨率限定符 xhdpi xxhdpi xxxhdpi 尺寸限定符:layout_small, layout_large...
5:最小屏幕限定符:values-sw360dp,values-sw384dp,... 屏幕方向限定符:layout_land, layout_port
6:百分比适配
7:改变DisplayMetrics参数适配
百分比适配:
PercentLayout extends RelativeLayout 并自定义几个百分比属性,比如width,height,margin等重写其onMeasure方法,获取到所有子类,并获取子类的百分比属性,根据控件的宽高和百分比系数转换成真正的宽高最后达到最终适配.
像素适配 ScreenLayout extends RelativeLayout
像素适配就是直接输入Px的适配也叫跟据屏幕比来适配,
原理:我们要跟据UI妹妹给的切图的尺寸来当做一个标准,比如很多都用IOS的750 ,然后我们通过代码获取到Android手机屏幕宽高,这二个数字对比得到一个比例,然后在onMeasure里面对子View的宽高等参数进行重新计算
改变DisplayMetrics参数适配
DisplayMetrics里面有几个重要参数
density:屏幕密度->以每一寸有160个像素点为基础 density=1;,如果每一寸有320个像素点的话,那么density=2
scaleDensity:字体缩放比例,默认情况下density = scaleDensity;
densityDpi:屏幕上每一英寸像素点有多不个,就是160或者320... Android最终都是以px来显示的
因为Android里面的大小都是通过px来显示的,可以从
TypeValue里面的public static float applyDimension(...)这个方法可以看出 public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0; }所以我们改变里面几个参数就可以达到完美适配
获取DisplayMetrics方法,Resources里面的getDisplayMetrics()
然后获取
appDensity = displayMetrics.density; appScaleDensity = displayMetrics.scaledDensity;我们这可以跟据我们自己设定的一个设置稿的宽高,比如
public static final float WIDTH = 384;这样我们就可以自己计算出自己想要的density; targetDensity =displayMetrics.widthPixels / WIDTH
自己想要的scaleDensity字体缩放比例
targetDensity * (appScaleDensity / appDensity)计算出自己的dpi: targetDensityDpi = (int) (targetDensity * 160);
最后就是将计算出来的进行替换
//替换系统的值 DisplayMetrics dm = activity.getResources().getDisplayMetrics(); dm.density = targetDensity; dm.densityDpi = targetDensityDpi; dm.scaledDensity = targetScaleDensity;因为系统字体可放大放小,可以还要监听字体改变
//添加字体变换监听 application.registerComponentCallbacks(new ComponentCallbacks() { @Override public void onConfigurationChanged(Configuration newConfig) { if (newConfig != null && newConfig.fontScale > 0) { //字体发生更改,重新对appScaleDensity赋值 // appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity; } } @Override public void onLowMemory() { } });最终代码如下:
package com.ancely.desy; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; /* * @项目名: px * @包名: com.ancely.desy * @文件名: PercentLayout * @描述: 百分比布局的实现 */ public class PercentLayout extends RelativeLayout { public PercentLayout(Context context) { super(context); } public PercentLayout(Context context, AttributeSet attrs) { super(context, attrs); } public PercentLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public static class LayoutParams extends RelativeLayout.LayoutParams { private float widthPresent; private float heightPresent; private float leftMarginPresent; private float rightMarginPresent; private float topMarginPresent; private float bottomMarginPresent; LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); //解析自定义属性 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout); widthPresent = a.getFloat(R.styleable.PercentLayout_Layout_widthPresent, 0); heightPresent = a.getFloat(R.styleable.PercentLayout_Layout_heightPresent, 0); leftMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintLeftPresent, 0); rightMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintRightPresent, 0); topMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintTopPresent, 0); bottomMarginPresent = a.getFloat(R.styleable.PercentLayout_Layout_margintBottomPresent, 0); a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); ViewGroup.LayoutParams params = child.getLayoutParams(); if (checkLayoutParams(params)) { LayoutParams lp = (LayoutParams) params; float widthPresent = lp.widthPresent; float heightPresent = lp.heightPresent; float leftMarginPresent = lp.leftMarginPresent; float rightMarginPresent = lp.rightMarginPresent; float topMarginPresent = lp.topMarginPresent; float bottomMarginPresent = lp.bottomMarginPresent; if (widthPresent > 0) { lp.width = (int) (widthSize * widthPresent); } if (heightPresent > 0) { lp.height = (int) (heightSize * heightPresent); } if (leftMarginPresent > 0) { lp.leftMargin = (int) (widthSize * leftMarginPresent); } if (widthPresent > 0) { lp.rightMargin = (int) (widthSize * rightMarginPresent); } if (widthPresent > 0) { lp.topMargin = (int) (heightSize * topMarginPresent); } if (widthPresent > 0) { lp.bottomMargin = (int) (heightSize * bottomMarginPresent); } } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } }