android 默认dimens_GitHub - AnJiaoDe/ScreenAdaptation: Andoid屏幕适配终极手段(小编用过最得劲的dp适配),1.工具类解析values目录下的d...

本文介绍了Android屏幕适配的两种方法:PX适配和DP适配。重点讨论了DP适配的原理,通过解析values目录下的dimens文件生成不同分辨率的dimens文件,以360dp为基准进行适配。文章提供了一个DP适配工具类DPGenerator,简化适配过程,并分析了DP适配的优缺点。
摘要由CSDN通过智能技术生成

微信公众号

[toc]

小编尝试过2种屏幕适配方法:

1.PX适配

使用PXGenerator代码生成各种分辨率的文件夹以及文件,

以某分辨率比如480x800为基准,1px=1px,

按比例生成其他各种分辨率的dimen文件,会有1px=4px,1px=3px之类的情况,

以次达到屏幕适配的目的

1bd6a33d621e82d8f8ecf5d1424028ae.png

14.7px

16.0px

17.3px

18.7px

20.0px

21.3px

22.7px

24.0px

25.3px

26.7px

28.0px

29.3px

30.7px

32.0px

33.3px

34.7px

36.0px

37.3px

38.7px

public class PXGenerator {

private static final String HEAD="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";//头部

private static final String START_TAG="\n";//开始标签

private static final String END_TAG="\n";//结束标签

private static final String ROOT="F:\\AndroidStudioWorkSpace\\ScreenAdaptation\\app\\src\\main\\res\\values-2560x1800\\";//文件夹

private static final String FILE_NAME="dimen_py2560.xml";//文件名

private static final String path=ROOT+FILE_NAME;//文件路径

private static final float TIMES=1920*1.0f/2560;

private static final int DIMENSION=1920;//以次为基准

public static void main(String[] args) {

generateXMl();

}

private static void generateXMl()

{

try

{

File diectoryFile = new File(ROOT);

if(!diectoryFile.exists()){

diectoryFile.mkdirs();

}

File file = new File(path);

if(file.exists()){

file.delete();

}

FileWriter fileWriter = new FileWriter(file);

fileWriter.write(HEAD);

fileWriter.write(START_TAG);

for(int i=0;i<=DIMENSION;i++){

String output="\t"+roundString(i)+"px\n";

fileWriter.write(output);

}

fileWriter.write(END_TAG);

fileWriter.flush();

fileWriter.close();

System.out.println("写入成功");

} catch (IOException e) {

e.printStackTrace();

System.out.println("写入失败");

}

}

private static String roundString(int data)

{

String result="";

float floatResult=data/TIMES;

DecimalFormat df = new DecimalFormat("0.00");

result = df.format(floatResult);

return result;

}

}

缺点:需要生成的相关分辨率的dimen实在太多,增大APK包体积

2.DP适配由来

先熟知如下图所示的各种参数

17831dc0bf8caab94504ceb25ea0fe07.png

36a48dc4d4f89fc86da7e5a0b824372c.png

b1e0994c74268bf69e5d18dbb9824a40.png

private String getScreenParams() {

DisplayMetrics dm = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(dm);

int heightPixels = dm.heightPixels;//高的像素

int widthPixels = dm.widthPixels;//宽的像素

int densityDpi = dm.densityDpi;//dpi

float xdpi = dm.xdpi;//xdpi

float ydpi = dm.ydpi;//ydpi

float density = dm.density;//density=dpi/160,密度比

float scaledDensity = dm.scaledDensity;//scaledDensity=dpi/160 字体缩放密度比

float heightDP = heightPixels / density;//高度的dp

float widthDP = widthPixels / density;//宽度的dp

String str = "heightPixels: " + heightPixels + "px";

str += "\nwidthPixels: " + widthPixels + "px";

str += "\ndensityDpi: " + densityDpi + "dpi";

str += "\nxdpi: " + xdpi + "dpi";

str += "\nydpi: " + ydpi + "dpi";

str += "\ndensity: " + density;

str += "\nscaledDensity: " + scaledDensity;

str += "\nheightDP: " + heightDP + "dp";

str += "\nwidthDP: " + widthDP + "dp";

return str;

}

1.px:

屏幕分辨率:在橫纵向上的像素点数。单位:px即1px=1个像素点。

一般以纵向像素x横向像素表示,如1920x1080

2.dpi:

屏幕像素密度,指每英寸上的像素点数,dot per inch的缩写,与屏幕尺寸和屏幕分辨率有关。以Nexus5为例,官方参数为1920*1080,dpi=445,4.95 inch 那么,这个445的dpi是怎么算出来的呢?由上面介绍可知,屏幕尺寸4.95是屏幕对角线的长度,而dpi是指每英寸上的像素点数,所以应该由对角线所占的像素值除以4.95,如下:

378feade6953708eb1e166930a40e5b3.png

3.dp/dip:

dp和dip是一样的,密度无关像素,Density Independent Pixels的缩写,以160dpi为基准。在160dpi设备 上1dp=1px,在240dpi设备上1dp=1.5px,以此类推

4.density:

density=dpi/160,密度比

5.scaledDensity :

scaledDensity=dpi/160 字体缩放密度比

6.heightDP:

heightDP = heightPixels / density;//高度的dp

7.widthDP:

heightDP = widthPixels / density;//宽度的dp

a89dfba5267d740447b7fffa0eca64d7.png

先看上图sw< N >dp:

smallestWidth,最小宽度,

c45e64685f26416ba4e84aa30b662a74.png

官方提供了多种尺寸限制符,sw指的是最小宽度,和竖屏横屏无关:

如800DPx480DP(最小宽度是480dp,比如部分800x480手机),

592DPx360DP(最小宽度是360dp,比如部分1920x1080手机),

604DPx360DP(最小宽度是360dp,比如部分1920x1080手机),

680DPx360DP(最小宽度是360dp.比如部分2160x1080手机(2018年左右开始流行长屏幕手机、刘海屏手机)),

1232DPx900DP(最小宽度是900dp,比如部分2560x1800手机(平板))

这里发现多种分辨率手机最小宽度都是360DP,尤其是1920x1080,是2018年左右最流行的屏幕分辨率,因此考虑以宽度360dp为基准做适配(这也是为何使用DP适配,而不使用PX适配的原因,因为DP适配需要的dimens文件会少很多)

sw< N >dp会向上兼容:

比如valuse-sw481dp文件夹,当且仅当手机最小宽度dp>=481dp才会去该目录寻找数值,800DPx480DP最小宽度480dp,是无法进入valuse-sw481dp寻找数值的,如果values目录下没有对应的数值,只有一个结果GG=APP崩溃。所以任何时候都记得一定要在values目录下创建对应的数值

3f0efb489bdbea5bc0769f235140e2b4.png

800DPx480DP手机,由于没有valuse-sw480dp文件夹,只能向下寻找,找到valuse-sw100dp文件夹,结果界面当然很丑,(适配具体操作 容后再谈)

c1f6425571a1f5c5cb8e92f1294f4f8f.png

上文说到:多种分辨率手机最小宽度都是360DP,尤其是1920x1080,是2018年左右最流行的屏幕分辨率,因此考虑以宽度360dp为基准做适配

3.DP适配原理以及具体操作

常见的swdp如下:

// private static final int[] dps = {360, 384, 392, 400, 410, 411, 480, 533, 592,

// 600, 640, 662, 720, 768, 800, 811, 820,900, 960, 961, 1024, 1280};//常见dp列表

为了适配更多手机,dp应尽可能包含更多,而且连续性强,间隔小

dp适配原理:

和px适配原理类似(按比例计算),以sw360dp为基准,

48dp

18sp

12dp

360dp

10sp

30dp

10dp

14sp

40dp

70dp

100dp

80dp

12sp

60dp

-10dp

4.5dp

-5.5dp

那么sw720dp如下:

全是2倍值

96dp

36sp

24dp

720dp

20sp

60dp

20dp

28sp

80dp

140dp

200dp

160dp

24sp

120dp

-20dp

9dp

-11dp

具体操作:

1)

以sw360dp为基准,按照1920x1080的UI设计图,可以让设计师用“标你妹”等软件标注dp和sp,也可以上传到蓝湖,蓝湖上的标注更爽,在values下的dimens文件中定义dp值对应的dimen标签。

比如,UI图上标注的一个按钮高度是40dp,那么在dimens文件中创建

40dp

name名字可以随便取

2)

将如下代码类放到当前android项目的app module下,记得更改root目录为自己的android项目的目录,

91415995c5b36d5ba0cb79bc2d82b399.png

public class DPGeneratorLittle {

private static final String HEAD = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";//头部

private static final String START_TAG = "\n";//开始标签

private static final String END_TAG = "\n";//结束标签

private static final float DP_BASE = 360;//360dp为基准

private static final int DP_MAX = 720;//所有dimens文件dp从0生成到这个值

private static final int SP_MAX = 48;//SP最大

private static final int[] dps = {360, 384, 392, 400, 410, 411, 480, 533, 592,

600, 640, 662, 720, 768, 800, 811, 820,900, 960, 961, 1024, 1280};//常见dp列表

// private static final int[] dps = {100,481,510,720,900};//常见dp列表

private static final String root="F:\\AndroidStudioWorkSpace\\ScreenAdaptation\\app\\src\\main\\res\\";//生成文件的主目录

private static ExecutorService fixedThreadPool;//线程池,用于生成XML文件

private static int size_thread = 5;//线程池大小

private static DocumentBuilderFactory dbFactory;

private static DocumentBuilder db;

private static Document document;

public static void main(String[] args) {

try {

dbFactory = DocumentBuilderFactory.newInstance();

db = dbFactory.newDocumentBuilder();

//将给定 URI 的内容解析为一个 XML 文档,并返回Document对象

//记得改成自己当前项目的路径

document = db.parse(root+"values\\dimens.xml");

//按文档顺序返回包含在文档中且具有给定标记名称的所有 Element 的 NodeList

NodeList dimenList = document.getElementsByTagName("dimen");

if (dimenList.getLength()==0)return;

List list = new ArrayList<>();

for (int i = 0; i < dimenList.getLength(); i++) {

//获取第i个book结点

Node node = dimenList.item(i);

//获取第i个dimen的所有属性

NamedNodeMap namedNodeMap = node.getAttributes();

//获取已知名为name的属性值

String atrName = namedNodeMap.getNamedItem("name").getTextContent();

String value = node.getTextContent();

System.out.println("+++atrName++++++++++++++++++++" + atrName);

System.out.println("+++++++++++++value++++++++++" + value);

list.add(new Dimen(atrName, value));

}

fixedThreadPool = Executors.newFixedThreadPool(size_thread);

for (int i = 0; i < dps.length; i++) {

XMLThread xmlThread = new XMLThread(i, list);

fixedThreadPool.execute(xmlThread);//线程启动执行

}

} catch (SAXException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} catch (ParserConfigurationException e) {

e.printStackTrace();

}

}

private static class XMLThread implements Runnable {

private int index = 0;

private List list;

public XMLThread(int index, List list) {

this.index = index;

this.list = list;

}

@Override

public void run() {

//记得改成自己当前项目的路径

generateXMl(list, index, root+"values-sw" + dps[index] + "dp\\", "dimens.xml");

}

}

private static void generateXMl(List list, int index, String pathDir, String fileName) {

try {

File diectoryFile = new File(pathDir);

if (!diectoryFile.exists()) {

diectoryFile.mkdirs();

}

File file = new File(pathDir + fileName);

if (file.exists()) {

file.delete();

}

FileWriter fileWriter = new FileWriter(file);

fileWriter.write(HEAD);

fileWriter.write(START_TAG);

//?????????????????????????????????????????????

int size = list.size();

String atrName;

String value;

for (int i = 0; i < size; i++) {

atrName = list.get(i).getAtrName();

value = list.get(i).getValue();

String output = "\t" +

roundString(Float.valueOf(value.substring(0, value.length() - 2)), index) +

value.substring(value.length()-2)+"\n";

fileWriter.write(output);

}

fileWriter.write(END_TAG);

fileWriter.flush();

fileWriter.close();

System.out.println("写入成功");

} catch (IOException e) {

e.printStackTrace();

System.out.println("写入失败");

}

}

//精确到小数点后2位,并且四舍五入(因为有SW1280dp,基准是160dp,1dp=1px,

// 如果精确到小数点后一位,四舍五入会有0.5dp误差,在sw1280dp中会有4PX误差,精确到小数点后2位,四舍五入,误差控制在1PX之内)

private static String roundString(float data, int index) {

String result = "";

float floatResult = data * dps[index] / DP_BASE;

DecimalFormat df = new DecimalFormat("0.00");

result = df.format(floatResult);

return result;

}

private static class Dimen {

private String atrName;

private String value;

public Dimen(String atrName, String value) {

this.atrName = atrName;

this.value = value;

}

public String getAtrName() {

return atrName;

}

public void setAtrName(String atrName) {

this.atrName = atrName;

}

public String getValue() {

return value;

}

public void setValue(String value) {

this.value = value;

}

}

}

如图执行工具类的main方法,即可生成各种dimens文件

a40d4fd6e53732c17ceedd43dc61abf9.gif

3)使用的相关原理及技巧

1.工具类解析values目录下的dimens文件,根据其中所有的dimen标签,name和values,生成其他各种dp对应的dimens文件

2.DP_BASE可修改为自己定的基准(你可以根据实际情况定为其他dp,一般情况下,定义为360dp,因为大部分手机sw都是360dp)

3.root对应的是当前android项目的主目录路径,千万不能出错

4.size_thread,线程池大小默认5,你可以设置其他数值

5.你还可以根据自己强迫症的各种习惯和喜好,各种更改成自己喜欢的风格,代码很精简,修改很容易

6.在每次打包(如果需要调试sw360dp之外的手机,或者上线APP)之前,记得执行DPGeneratorLittle的main方法,以确保所有的dimen文件都是最新的

7.负数的dp,小数的dp,甚至小数的sp(一般没这情况)都可以生成,放心大胆使用

8.如果发现某台手机适配效果不是很好,那么真机调试,得到其swdp,然后在用DPGeneratorLittle生成对应的swdp,dimen文件

9.执行DPGeneratorLittle报错的时候,记得删除所有生成的valuse目录,clean项目,重新执行

4.欣赏DP适配案例

800PXx480PX的手机

1a7b296ea8f42527d0131e623430ca37.png

1920PXx1080PX的手机

f5f1f71eec5e365de780d35f670f3de7.png

2560PXx1800PX的手机

988bb66e0a72766f50aaff050f9d4a3d.png

可以看到适配效果很好,几乎没有无法接受的地方

代码如下:

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/activity_main"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:layout_width="match_parent"

android:layout_height="@dimen/height_title"

android:background="#1d953f"

android:gravity="center_vertical">

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_toRightOf="@+id/iv_back"

android:gravity="center_vertical"

android:text="屏幕适配"

android:textColor="#ffffff"

android:textSize="@dimen/sp18" />

android:id="@+id/iv_back"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout_centerVertical="true"

android:padding="@dimen/padding_icon"

android:scaleType="centerInside"

android:src="@drawable/back" />

android:layout_width="match_parent"

android:layout_height="@dimen/dp360">

android:id="@+id/imageView"

android:layout_width="@dimen/dp360"

android:layout_height="@dimen/dp360"

android:layout_gravity="center_horizontal"

android:scaleType="centerCrop"

android:src="@drawable/rec" />

android:id="@+id/tv"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center"

android:textColor="#fff"

android:textSize="@dimen/sp18" />

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="horizontal">

android:layout_width="@dimen/dp40"

android:layout_height="@dimen/dp40"

android:layout_marginLeft="@dimen/_dp5_5"

android:background="#436876" />

android:id="@+id/view_bottom"

android:layout_width="@dimen/dp60"

android:layout_height="@dimen/dp60"

android:background="#00f"

android:gravity="center"

android:textColor="#fff"

android:textSize="@dimen/sp12" />

android:layout_width="0dp"

android:layout_height="0dp"

android:layout_weight="1" />

android:layout_width="@dimen/dp4_5"

android:layout_height="@dimen/dp40"

android:layout_marginRight="@dimen/dp10"

android:background="#00f" />

android:layout_width="@dimen/dp40"

android:layout_height="@dimen/dp40"

android:layout_marginRight="@dimen/_dp10"

android:background="#f00" />

public class MainActivity extends AppCompatActivity {

private TextView tv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.tv);

tv.setText(getScreenParams());

}

private String getScreenParams() {

DisplayMetrics dm = new DisplayMetrics();

getWindowManager().getDefaultDisplay().getMetrics(dm);

int heightPixels = dm.heightPixels;//高的像素

int widthPixels = dm.widthPixels;//宽的像素

int densityDpi = dm.densityDpi;//dpi

float xdpi = dm.xdpi;//xdpi

float ydpi = dm.ydpi;//ydpi

float density = dm.density;//density=dpi/160,密度比

float scaledDensity = dm.scaledDensity;//scaledDensity=dpi/160 字体缩放密度比

float heightDP = heightPixels / density;//高度的dp

float widthDP = widthPixels / density;//宽度的dp

String str = "heightPixels: " + heightPixels + "px";

str += "\nwidthPixels: " + widthPixels + "px";

str += "\ndensityDpi: " + densityDpi + "dpi";

str += "\nxdpi: " + xdpi + "dpi";

str += "\nydpi: " + ydpi + "dpi";

str += "\ndensity: " + density;

str += "\nscaledDensity: " + scaledDensity;

str += "\nheightDP: " + heightDP + "dp";

str += "\nwidthDP: " + widthDP + "dp";

return str;

}

}

**dp适配优点:**dimens文件很少,不影响apk包的体积,

缺点:在每次打包(如果需要调试sw360dp之外的手机,或者上线APP)之前,都需要执行DPGeneratorLittle的main方法,以确保所有的dimen文件都是最新的,有点烦躁

想不烦躁可以,你可以用github_DPScreenAdaptation的DPGenerator类

直接生成所有可能用到的dp,sp,但是这样,可能会增加好几百KB文件

各位老铁有问题欢迎及时联系、指正、批评、撕逼

微信公众号

QQ群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值