适配方式
方式一、生成对应px文件,百分比布局
方式二、动态改变px比例,百分比布局
方式三、生成对应dp文件,百分比布局
方式四、改变DisplayMetrics属性值,今日头条方案
获取设备信息
private void getMessage() {
// 获取屏幕密度
DisplayMetrics dm = new DisplayMetrics();
dm = getResources().getDisplayMetrics();
float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0)
int densityDPI = dm.densityDpi; // 屏幕密度(每寸像素:120/160/240/320)
float xdpi = dm.xdpi;
float ydpi = dm.ydpi;
Log.e(" DisplayMetrics", "xdpi=" + xdpi + "; ydpi=" + ydpi);
Log.e( " DisplayMetrics", "density=" + density + "; densityDPI=" + densityDPI);
Log.e( " DisplayMetrics", "screenWidth=" + dm.widthPixels + "; screenHeight=" + dm.heightPixels);
}
生对应px文件,百分比布局
该适配方案适配主流分辨率,对于特定并且没有适配的需要一个默认文件设置大小。
对于已经适配分辨率的手机支持不错,使用没有什么需要特别注意的。
如果UI设计采用的是px单位,建议使用这种适配。需要注意的是,该手机真实分辨率。
GenerateValueFiles.class生成px文件
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class GenerateValueFiles {
private int baseW;
private int baseH;
private String dirStr = "F:/res";
private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";
/**
* {0}-HEIGHT
*/
private final static String VALUE_TEMPLATE = "values-{0}x{1}";
private static final String SUPPORT_DIMESION = "1440,2392;320,480;480,800;480,854;540,960;600,1024;720,1184;720,1196;720,1280;768,1024;800,1280;1080,1812;1080,1920;1440,2560;";
private String supportStr = SUPPORT_DIMESION;
public GenerateValueFiles(int baseX, int baseY, String supportStr) {
this.baseW = baseX;
this.baseH = baseY;
if (!this.supportStr.contains(baseX + "," + baseY)) {
this.supportStr += baseX + "," + baseY + ";";
}
this.supportStr += validateInput(supportStr);
System.out.println(supportStr);
File dir = new File(dirStr);
if (!dir.exists()) {
dir.mkdir();
}
System.out.println(dir.getAbsoluteFile());
}
/**
* @param supportStr
* w,h_...w,h;
* @return
*/
private String validateInput(String supportStr) {
StringBuffer sb = new StringBuffer();
String[] vals = supportStr.split("_");
int w = -1;
int h = -1;
String[] wh;
for (String val : vals) {
try {
if (val == null || val.trim().length() == 0)
continue;
wh = val.split(",");
w = Integer.parseInt(wh[0]);
h = Integer.parseInt(wh[1]);
} catch (Exception e) {
System.out.println("skip invalidate params : w,h = " + val);
continue;
}
sb.append(w + "," + h + ";");
}
return sb.toString();
}
public void generate() {
String[] vals = supportStr.split(";");
for (String val : vals) {
String[] wh = val.split(",");
generateXmlFile(Integer.parseInt(wh[0]), Integer.parseInt(wh[1]));
}
}
private void generateXmlFile(int w, int h) {
StringBuffer sbForWidth = new StringBuffer();
sbForWidth.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sbForWidth.append("<resources>");
float cellw = w * 1.0f / baseW;
System.out.println("width : " + w + "," + baseW + "," + cellw);
for (int i = 1; i < baseW; i++) {
sbForWidth.append(WTemplate.replace("{0}", i + "").replace("{1}",
change(cellw * i) + ""));
}
sbForWidth.append(WTemplate.replace("{0}", baseW + "").replace("{1}",
w + ""));
sbForWidth.append("</resources>");
StringBuffer sbForHeight = new StringBuffer();
sbForHeight.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sbForHeight.append("<resources>");
float cellh = h *1.0f/ baseH;
System.out.println("height : "+ h + "," + baseH + "," + cellh);
for (int i = 1; i < baseH; i++) {
sbForHeight.append(HTemplate.replace("{0}", i + "").replace("{1}",
change(cellh * i) + ""));
}
sbForHeight.append(HTemplate.replace("{0}", baseH + "").replace("{1}",
h + ""));
sbForHeight.append("</resources>");
File fileDir = new File(dirStr + File.separator
+ VALUE_TEMPLATE.replace("{0}", h + "")//
.replace("{1}", w + ""));
fileDir.mkdir();
File layxFile = new File(fileDir.getAbsolutePath(), "lay_x.xml");
File layyFile = new File(fileDir.getAbsolutePath(), "lay_y.xml");
try {
PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
pw.print(sbForWidth.toString());
pw.close();
pw = new PrintWriter(new FileOutputStream(layyFile));
pw.print(sbForHeight.toString());
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static float change(float a) {
int temp = (int) (a * 100);
return temp / 100f;
}
public static void main(String[] args) {
int baseW = 1080;
int baseH = 1920;
String addition = "";
try {
if (args.length >= 3) {
baseW = Integer.parseInt(args[0]);
baseH = Integer.parseInt(args[1]);
addition = args[2];
} else if (args.length >= 2) {
baseW = Integer.parseInt(args[0]);
baseH = Integer.parseInt(args[1]);
} else if (args.length >= 1) {
addition = args[0];
}
} catch (NumberFormatException e) {
System.err
.println("right input params : java -jar xxx.jar width height w,h_w,h_..._w,h;");
e.printStackTrace();
System.exit(-1);
}
new GenerateValueFiles(baseW, baseH, addition).generate();
}
}
使用方式,导入res中
在布局使用
注意在生成文件是使用1920X1080为基准;所以x轴方向x540表示占屏幕一半。
动态改变px比例,百分比布局
public class PxAutoUtils {
public static int displayWidth;
public static int displayHeight;
private static int designWidth;
private static int designHeight;
private static double textPixelsRate;
public static void setSize(Activity act, boolean hasStatusBar, int designWidth, int designHeight) {
if (act == null || designWidth < 1 || designHeight < 1)
return;
Display display = act.getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
if (hasStatusBar) {
height -= getStatusBarHeight(act);
}
PxAutoUtils.displayWidth = width;
PxAutoUtils.displayHeight = height;
PxAutoUtils.designWidth = designWidth;
PxAutoUtils.designHeight = designHeight;
double displayDiagonal = Math.sqrt(Math.pow(PxAutoUtils.displayWidth, 2) + Math.pow(PxAutoUtils.displayHeight, 2));
double designDiagonal = Math.sqrt(Math.pow(PxAutoUtils.designWidth, 2) + Math.pow(PxAutoUtils.designHeight, 2));
PxAutoUtils.textPixelsRate = displayDiagonal / designDiagonal;
}
public static int getStatusBarHeight(Activity context) {
int result = 0;
try {
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
return result;
}
public static void auto(Activity act) {
if (act == null || displayWidth < 1 || displayHeight < 1)
return;
View view = act.getWindow().getDecorView();
auto(view);
}
public static void auto(View view) {
if (view == null || displayWidth < 1 || displayHeight < 1)
return;
PxAutoUtils.autoTextSize(view);
PxAutoUtils.autoSize(view);
PxAutoUtils.autoPadding(view);
PxAutoUtils.autoMargin(view);
if (view instanceof ViewGroup) {
auto((ViewGroup) view);
}
}
private static void auto(ViewGroup viewGroup) {
int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++) {
View child = viewGroup.getChildAt(i);
if (child != null) {
auto(child);
}
}
}
public static void autoMargin(View view) {
if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
return;
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (lp == null)
return;
lp.leftMargin = getDisplayWidthValue(lp.leftMargin);
lp.topMargin = getDisplayHeightValue(lp.topMargin);
lp.rightMargin = getDisplayWidthValue(lp.rightMargin);
lp.bottomMargin = getDisplayHeightValue(lp.bottomMargin);
}
public static void autoPadding(View view) {
int l = view.getPaddingLeft();
int t = view.getPaddingTop();
int r = view.getPaddingRight();
int b = view.getPaddingBottom();
l = getDisplayWidthValue(l);
t = getDisplayHeightValue(t);
r = getDisplayWidthValue(r);
b = getDisplayHeightValue(b);
view.setPadding(l, t, r, b);
}
public static void autoSize(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null)
return;
boolean isSquare = false;
if (lp.width == lp.height) {
isSquare = true;
}
if (lp.width > 0) {
lp.width = getDisplayWidthValue(lp.width);
}
if (lp.height > 0) {
lp.height = getDisplayHeightValue(lp.height);
}
if (isSquare) {
if (lp.width > lp.height) {
lp.width = lp.height;
} else {
lp.height = lp.width;
}
}
}
public static void autoTextSize(View view) {
if (view instanceof TextView) {
double designPixels = ((TextView) view).getTextSize();
double displayPixels = textPixelsRate * designPixels;
((TextView) view).setIncludeFontPadding(false);
((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, (float) displayPixels);
}
}
public static int getDisplayWidthValue(int designWidthValue) {
if (Math.abs(designWidthValue) < 2) {
return designWidthValue;
}
return designWidthValue * displayWidth / designWidth;
}
public static int getDisplayHeightValue(int designHeightValue) {
if (Math.abs(designHeightValue) < 2) {
return designHeightValue;
}
return designHeightValue * displayHeight / designHeight;
}
public static float getDisplayTextSize(float designTextSize){
return (float) (PxAutoUtils.textPixelsRate*designTextSize);
}
/**
* 适配所有Activity
* @param application
* @param width
* @param height
*/
private static Boolean KEY_ISFIRST = true;
public static void setAuto(Application application, final int width, final int height){
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks(){
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
if(KEY_ISFIRST){
KEY_ISFIRST=false;
setSize(activity,false,width,height);
}
}
@Override
public void onActivityStarted(Activity activity) {
auto(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 bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
在application注册即可适配activity页面
PxAutoUtils.setAuto(this,1080,1920);
对于加载自定义View,fragment,listview,recycleview时使用
PxAutoUtils.auto(view);
虽然使用简单,但是并不是非常建议使用,对于一切都是以px为单位;同样是以1080X1920为基准。假设某个页面或者布局被加载设置PxAutoUtils.auto(view)两次或者多次,就会改变正确数据;而对于某些特定系统页面也不能做到适配view.
生成对应dp文件,百分比布局
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class TestDp {
public static void main(String[] args) {
String baseUrl="F://dpres//value";
int baseDp=360;
String[] defaultDPArr = new String[]{"240","267","320","360","384", "392", "400", "410", "411", "432","440","480", "533", "560","592", "600", "640", "662", "720", "768", "800", "811", "820", "960", "961", "1024", "1280", "1365"};
for(int i=0;i<defaultDPArr.length;i++){
Integer value=Integer.valueOf(defaultDPArr[i]);
getFile(baseDp,value,baseUrl);
}
}
private static void getFile(float baseDp,int Dp, String pathFile ) {
float size=Dp/baseDp;
StringBuilder sb=new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resources>");
sb.append("\n");
for(float i=0;i<1;i=i+0.1f){
float temp=i*size;
String value=String.valueOf(temp);
if(value.length()>=3){
value=value.substring(0,3);
}
String value0=String.valueOf(i);
if(i==0){
value0="0";
}
if(value0.length()>=3){
value0=value0.substring(0,3);
}
sb.append("<dimen name=\"").append("dp").append(value0).append("\">").append(value).append("dp").append("</dimen>").append("\n");
}
for(int i=1;i<=baseDp;i=i+1){
float temp=i*size;
String value=String.valueOf(temp);
sb.append("<dimen name=\"").append("dp").append(i).append("\">").append(value).append("dp").append("</dimen>").append("\n");
}
for(int i=1;i<=50;i=i+1){
float temp=i*size;
String value=String.valueOf(temp);
sb.append("<dimen name=\"").append("sp").append(i).append("\">").append(value).append("sp").append("</dimen>").append("\n");
}
sb.append("</resources>\n");
//System.out.println(sb.toString());
String dir=pathFile+"//values-sw"+Dp+"dp";
try {
if(!new File(dir).exists()){
new File(dir).mkdirs();
}
String fileNme=dir+"//dimens.xml";
System.out.println(fileNme);
if(new File(fileNme).exists()){
new File(fileNme).delete();
}
new File(fileNme).createNewFile();
FileOutputStream outSTr = new FileOutputStream(new File(fileNme));
BufferedOutputStream Buff = new BufferedOutputStream(outSTr);
Buff.write(sb.toString().getBytes());
Buff.flush();
Buff.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
计算dp值
dp=宽度px/设备密度值(density);
比如720px density为2 dp为360;1080px density为3 dp为360;
dpi=160*density;
sw为small width,后面接的值表示,屏幕的最小宽度dp大于这个值的时候启用;会使用最接近文件适配。
使用
改变DisplayMetrics属性值,今日头条方案
public class DpAutoUtils {
private static float appDensity;
private static float appScaledDensity;
private static DisplayMetrics appDisplayMetrics;
/**
* 用来参照的的width
*/
private static float WIDTH;
public static void setDensity(@NonNull final Application application, float width) {
appDisplayMetrics = application.getResources().getDisplayMetrics();
WIDTH = width;
registerActivityLifecycleCallbacks(application);
if (appDensity == 0) {
//初始化的时候赋值
appDensity = appDisplayMetrics.density;
appScaledDensity = appDisplayMetrics.scaledDensity;
//添加字体变化的监听
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
//字体改变后,将appScaledDensity重新赋值
if (newConfig != null && newConfig.fontScale > 0) {
appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
}
private static void setDefault(Activity activity) {
setAppOrientation(activity);
}
private static void setAppOrientation(@Nullable Activity activity) {
float targetDensity = 0;
try {
targetDensity = appDisplayMetrics.widthPixels / WIDTH;
} catch (NumberFormatException e) {
e.printStackTrace();
}
float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
int targetDensityDpi = (int) (160 * targetDensity);
/**
*
* 最后在这里将修改过后的值赋给系统参数
*
* 只修改Activity的density值
*/
DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
private static void registerActivityLifecycleCallbacks(Application application) {
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
setDefault(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) {
}
});
}
}
使用在application注册
DpAutoUtils.setDensity(this,360);
360根据设计图宽度改变;最简单直接一种适配,全局不用改变,该dp用dp,该sp用sp;但是不能使用px。
总结
综上所述;如果设计图是dp为单位,用方案四今日头条比较好,毕竟大厂有一定保障性,其次是方案三dp文件,使用这种适配也不少;如果设计图以px为单位,对适配范围不那么严谨的话使用方案一px文件比较好,适配主流屏幕不错;最次就是方案二动态px适配,虽然考虑到所有屏幕,但是程序app中可能出现异常界面情况,而且适配view不太友好,有很大侵入性,所以不太推荐。
其他基本适配就是使用xml中特有属性适配,比如权重,wrap_content,match_parent等