Android多屏幕适配-手机

1、常用单位及其关系

 px:像素

inch:英寸

pt:1/72 英寸

dpi:一英寸长的直线上的像素点的数量,即像素密度。不同的设备,dpi值不同,显示效果不同,dpi的值跟设备硬件有关。标准值是160dp。

dp(dip):独立像素密度。即在标准屏幕下,1个像素点的长度,标准屏幕是160dpi,可以理解为1英寸长度上有160个像素。标准屏幕中1dp=1px。

px = dp*(dpi/160);//当dpi=160时,1px=1dp

分辨率:屏幕上长宽方向上像素点的数量,即一个屏幕上像素的数量。

例如:720*1280 = 屏幕x轴上有720个像素,屏幕y轴上有1280个像素

分辨率单位:dpi(点每英寸)、lpi(线每英寸)、ppi(像素每英寸)

屏幕的物理尺寸:屏幕对角线的长度,单位是inch

sp:专用于设定文字大小,受dpi影响和用户的字体偏好设定影响。

各单位和px的换算关系见TypedValue.applyDimension方法

  1. public static float applyDimension(int unit, float value,  
  2.                                        DisplayMetrics metrics)  
  3.     {  
  4.         switch (unit) {  
  5.         case COMPLEX_UNIT_PX:  
  6.             return value;  
  7.         case COMPLEX_UNIT_DIP:  
  8.             return value * metrics.density;  
  9.         case COMPLEX_UNIT_SP:  
  10.             return value * metrics.scaledDensity;  
  11.         case COMPLEX_UNIT_PT:  
  12.             return value * metrics.xdpi * (1.0f/72);  
  13.         case COMPLEX_UNIT_IN:  
  14.             return value * metrics.xdpi;  
  15.         case COMPLEX_UNIT_MM:  
  16.             return value * metrics.xdpi * (1.0f/25.4f);  
  17.         }  
  18.         return 0;  
  19.     }  
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;
    }

  1. //显示器  
  2. DisplayMetrics d = getResources().getDisplayMetrics();//可以获得scaledDensity,densityDpi,heightPixels,widthPixels等信息。  
  3. Configuration configuration = getResources().getConfiguration();//获取设备的配置信息  
  4. //configuration.screenHeightDp  当前屏幕可用空间的高度,单位是dp  
  5. //configuration.screenWidthDp   当前屏幕可用空间的宽度,单位是dp  
  6. //configuration.densityDpi      当前设备的dpi信息  
//显示器
DisplayMetrics d = getResources().getDisplayMetrics();//可以获得scaledDensity,densityDpi,heightPixels,widthPixels等信息。
Configuration configuration = getResources().getConfiguration();//获取设备的配置信息
//configuration.screenHeightDp  当前屏幕可用空间的高度,单位是dp
//configuration.screenWidthDp   当前屏幕可用空间的宽度,单位是dp
//configuration.densityDpi      当前设备的dpi信息

例子:

已知设备1080*1920,使用DisplayMetrics获取的实际信息是1080*1776,y轴方向上的像素有误差是因为软键盘,实际屏幕要小。

使用Configuration获取的设备的dpi=480dpi,根据公式px=dp*(dpi/160)。

现在px=1080,dpi=480,则dp=360.

使用configuration.screenWidthDp得到的数值为360,和上面用公式算出的一致。

但是y轴方向上用公式计算出来的应该是1776/3=592.但是用scrrenHeightDp获取的只有567。

 

通过源码验证上述是否成立:

applyDimension方法就是通过输入的任何值转换成px,也就是说,该方法是任何单位和px的换算关系。

COMPLEX_UNIT_DIP 就是dip单位,就是平时说的dp。和px的换算关系是

value * metrics.density;

metrics.density是密度,默认值是

SystemProperties.getInt(“qemu.sf.lcd_density”,SystemProperties.getInt(“ro.sf.lcd_density”, DENSITY_DEFAULT)) / 160

160是中密度屏幕的标准dpi

所以屏幕密度density=设备dpi/160

DENSITY_DEFAULT=160

densityDpi=SystemProperties.getInt(“qemu.sf.lcd_density”,SystemProperties.getInt(“ro.sf.lcd_density”, DENSITY_DEFAULT))/ DENSITY_DEFAULT

density= densityDpi/ DENSITY_DEFAULT

scaledDensity=density

xdpi= densityDpi

ydpi= densityDpi

但是实际上得到的xdpi!=ydpi的

xdpi:x轴屏幕每英寸的实际物理像素


2、换算方程式

  1. px=dp*(dpi/160)  
  2. px=sp* metrics.scaledDensity   px=sp*(dpi/160)  
  3. px=pt*xdpi*(1.0f/72)   pt*(1/72)=inch  
  4. px=inch*xdpi  
  5. px=mm*xdpi*(1.0f*25.4f)  mm*(1.0f/25.4f)=inch  
px=dp*(dpi/160)
px=sp* metrics.scaledDensity   px=sp*(dpi/160)
px=pt*xdpi*(1.0f/72)   pt*(1/72)=inch
px=inch*xdpi
px=mm*xdpi*(1.0f*25.4f)  mm*(1.0f/25.4f)=inch

易乱点:

分辨率1080*1920单位是像素,有的认为单位是dpi,但是通过源码验证,单位就是px

物理尺寸单位是inch

dpi跟设备有关系

只有在dpi=160时,1px=1dp


3、资源查找原则

app运行时,系统会根据属性选择适配的资源进行展示。如果有符合的资源则使用,反之,当符合的资源不存在时,系统会去寻找最相近的可用资源来代替。但是,查找的属性不同,查找的顺序会有所有差异。

 

res文件夹下的资源文件以优先级从高到低排列,命名规则:资源名-属性1-属性2-…..

v15修饰的资源仅用于4.0及以上的设备

w640dp修饰的适配640dp宽度的设备

h720dp修饰的资源适配720dp高度的设备

屏幕密度属性ldpi、mdpi、hdpi、xhdpi、xxhdpi、nodpi、tvdpi

ldpi=120

mdpi=160

hdpi=240

xhdpi=320

xxhdpi=480

xxxhdpi=640

 

其中nodpi用于开发者不希望系统对图片进行缩放的情况

tvdpi介于hdpi和mdpi之间。tvdpi一般在213左右,多用于Android系统的只能电视,大部分app很少用到

 

对dpi属性来说,查找的顺序为,高dpi的资源优先。例如,没能找到hdpi的图片资源,则系统的搜索顺序是

drawable-xhdpi->drawable-xxhdpi->drawable-mdip->drawable->drawable->drawable-ldpi。

这里drawable被认为比drawable-ldpi更接近hdpi。

 

有时候,我们的图片资源不一定是从drawable文件夹中读取的,还有可能是从sd卡上读取的,或者从网络上下载的。这个时候,我们需要注意,默认情况下,通过BitmapFactory.decodeFile()函数生成的图片被认为是MDPI的,如果想让图片也获得与drawable文件夹相似的缩放能力,则需要通过BitmapFactory.Option.inDensity属性设置(例如如果图片是为hdpi准备的,则设置为240)。

 

对于screan size,查找的顺序则是小尺寸优先,大尺寸放弃。例如,在Galaxy note 2上执行apk时,如果未能找到layout-large资源,则查找顺序为:layout-normal->layout->layout-small,不会查找layout-xlarge。


4、屏幕适配方案一

步骤:

1) 假设设计图的基准是720*1280,那么在设计图中1px对于1920*1080分辨率的手机应该是1.5px




在布局文件中就可以直接使用像素标识了,其实本质上是百分比

布局文件中的使用,比如说现在做一个670*80px的按钮,在density=1的时候,1dp=1px,在density=2.0的手机,1px=2.0dp,在density=1.5的手机中1px=1.5dp

如果使用dp设置这个button的大小,以2.0为基准,设置大小335*40dp,

在density=1.5的手机上,实际占的大小是502.5*60px。如果手机的分辨率是480*800px的就会出现button显示不全的情况。

 

但是,使用上面指定分辨率属性资源的,根据不同的手机分辨率应用自动寻找对应的资源文件。dimen_670_dip*dimen_80_dip在分辨率720*1280手机中是670*80px

在1080*1920分辨率的手机中是1005*120px。不需要计算就可以得到相应的尺寸。(不需要人为去计算,根据资源查找原则,系统会自动选择最适合屏幕的资源)

  1. <Button   
  2.            android:layout_width=”@dimen/dimen_670_dip”  
  3.            android:layout_height=”@dimen/dimen_80_dip”  
  4.            android:text=”登陆”  
  5. />  
<Button 
           android:layout_width="@dimen/dimen_670_dip"
           android:layout_height="@dimen/dimen_80_dip"
           android:text="登陆"
/>

因为大小按照不同的手机不同的分辨率进行了适配,但是字体也要进行适配。不然大小变小了,字体没有适配就会出现太小或太大的问题。

pt和sp的换算关系还跟用户的设置有关系,所以最好的方法就是,字体大小也使用px单位的换算。


5、屏幕适配方案二——-谷歌百分比布局库

android-support-percent.jar

方案一的适配已经运用在多款互联网应用,但是方案一的适配有一个缺点,就是会增加apk的大小。

使用谷歌的百分比布局库就可以解决这个问题

百分比布局库就是讲RelativeLayout换成PercentRelativeLayout,

将FrameLayout换成PercentFrameLayout

支持宽高设置和margin设置

 

Eclipse中直接将jar复制进libs文件夹,并依赖库percent

F:\Android\SDK\extras\android\support\percent

sdk路径/extras/android/support/percent

AndroidStudio在build.gradle中添加compile ‘com.android.support:percent:22.2.0’

使用示例

  1. <android.support.percent.PercentRelativeLayout  
  2.    xmlns:app=”http://schemas.android.com/apk/res-auto”  
  3.         android:layout_width=”match_parent”  
  4.         android:layout_height=”match_parent” >  
  5.         <TextView  
  6.             android:layout_width=”0dp”  
  7.             android:layout_height=”0dp”  
  8.             app:layout_heightPercent=”20%”  
  9.             app:layout_widthPercent=”30%”  
  10.             android:text=”w:30%,h:20%” />  
  11.     </android.support.percent.PercentRelativeLayout>  
<android.support.percent.PercentRelativeLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_heightPercent="20%"
            app:layout_widthPercent="30%"
            android:text="w:30%,h:20%" />
    </android.support.percent.PercentRelativeLayout>

6、常用方法

  1. /** 
  2.      * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 
  3.      */  
  4.     public static int dip2px(Context context, float dpValue) {  
  5.         final float scale = context.getResources().getDisplayMetrics().density;  
  6.         return (int) (dpValue * scale + 0.5f);  
  7.     }  
  8.   
  9.     /** 
  10.      * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 
  11.      */  
  12.     public static int px2dip(Context context, float pxValue) {  
  13.         final float scale = context.getResources().getDisplayMetrics().density;  
  14.         return (int) (pxValue / scale + 0.5f);  
  15.     }  
  16.   
  17.   
  18.     public static int getScreenWidth(Activity activity){  
  19.         DisplayMetrics dm = new DisplayMetrics();  
  20.         activity.getWindowManager().getDefaultDisplay().getMetrics(dm);  
  21.         int screenWidth = dm.widthPixels;  
  22.         return screenWidth;  
  23.     }  
  24.   
  25.     public static int getScreenHeigth(Activity activity){  
  26.         DisplayMetrics dm = new DisplayMetrics();  
  27.         activity.getWindowManager().getDefaultDisplay().getMetrics(dm);  
  28.         int screenHeigh = dm.heightPixels;  
  29.         return screenHeigh;  
  30.     }  
  31.   
  32.   
  33.     public static int measureHeight(View view){  
  34.   
  35.         int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);  
  36.         int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);  
  37.         view.measure(w, h);  
  38.         int height =view.getMeasuredHeight();  
  39.         return height;  
  40.     }  
  41.   
  42.     public static int measureWidth(View view){  
  43.         int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);  
  44.         int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);  
  45.         view.measure(w, h);  
  46.         int width =view.getMeasuredWidth();  
  47.         return width;  
  48.     }  
/**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }


    public static int getScreenWidth(Activity activity){
        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenWidth = dm.widthPixels;
        return screenWidth;
    }

    public static int getScreenHeigth(Activity activity){
        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenHeigh = dm.heightPixels;
        return screenHeigh;
    }


    public static int measureHeight(View view){

        int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
        view.measure(w, h);
        int height =view.getMeasuredHeight();
        return height;
    }

    public static int measureWidth(View view){
        int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(w, h);
        int width =view.getMeasuredWidth();
        return width;
    }


注意:

该适配方法只适用于手机。平板不适用该方法的适配。


参考资料:

谷歌官方资料:http://developer.android.com/guide/topics/resources/providing-resources.html

Android屏幕适配工具包(像素最大值是599px,sp最大值是49sp):http://download.csdn.net/detail/zmobs/8597341

屏幕分辨率信息查询:http://screensiz.es/phone

百分比布局库使用示例:https://github.com/JulienGenoud/android-percent-support-lib-sample

Android屏幕适配工具包2(像素最大值是2000px,sp最大值是200)http://download.csdn.NET/detail/qq_27570955/9653790

开源,原创,实用Android 屏幕适配方案分享:http://blog.csdn.net/i7788/article/details/44937277

Android多屏幕适配学习笔记

开源,原创,实用Android 屏幕适配方案分享
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值