等比例适配Android

3 篇文章 0 订阅

一、背景

面对市场上五花八门的屏幕和分辨率,屏幕适配一直是android开发中让人头疼的问题。dp和res不能满足所有的适配需求,需要一种简单又方便的适配方案,可以弥补dp和res的不足。

二、方案

1、dp,看起来都一样大。

与px不同,dp是针对屏幕像素密度的一个单位,在小屏幕上1dp=1px,在大手机上1dp=5px,如果一个控件用dp单位来指定宽高的话,那么这个控件在不同手机上的大小都类似。

字不重要,看图:

以上是350dp的一个TextView在480*800,1080*1920上的效果图。这个TextView看起来是同样大小的,但是显示在屏幕上的效果完全不一样。对于要求所有手机屏幕显示效果一致的情况,显然不能满足需求。

2、res

res是通过设置不同的res,来达到适配效果。配合dp使用,可以适配大多数手机。但是缺点也十分明显,对于区分不同分辨率的粒度该怎么区分,并且使用起来也非常麻烦。

3、等比适配

通过阅读源码我们可以看到view是在onMeaure中通过LayoutParams属性来确定大小,在generateLayoutParams中获取LayoutParams对象。

源码调用过程:略。。。

所以,我们可以在这里截取到xml文件中的属性,根据我们针对屏幕计算出来的缩放比进行计算,把原来的LayoutParams换成我们自定义的LayoutParams。上代码:

//开始完成初始化工作(应该在app第一次安装的时候记录下来)
private  UIUtils(Context context) {
     this .context = context;
     @SuppressLint ( "ServiceCast" ) WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
     DisplayMetrics displayMetrics =  new  DisplayMetrics();
     if  (displayMetricsHeight ==  0 .0f || displayMetricsWidth ==  0 .0f) {
         windowManager.getDefaultDisplay().getMetrics(displayMetrics);
     }
     //获取状态栏的高度
     int  systemBarHeight = getSystemBarHeight(context);
     //为了适配平板和手机
     if  (displayMetrics.widthPixels > displayMetrics.heightPixels) {
         //平板
     else  {
         //手机
         this .displayMetricsWidth = displayMetrics.widthPixels;
         this .displayMetricsHeight = displayMetrics.heightPixels - systemBarHeight;
     }
 
}
//一般手机状态栏高度都是48dp,如果没有取到状态栏高度,就返回48
private  int  getSystemBarHeight(Context context) {
     return  getValue(context, DIMEN_CLASS,  "system_bar_height" 48 );
 
}
 
private  int  getValue(Context context, String attrCroupClass, String system,  int  i) {
     try  {
         Class e = Class.forName(attrCroupClass);
         Object object = e.newInstance();
         Field field = e.getField(system);
         int  x = Integer.parseInt(field.get(object).toString());
         return  ( int ) context.getResources().getDimensionPixelOffset(x);
     catch  (ClassNotFoundException e1) {
         e1.printStackTrace();
         return  i;
     catch  (IllegalAccessException e) {
         e.printStackTrace();
         return  i;
     catch  (InstantiationException e) {
         e.printStackTrace();
         return  i;
     catch  (NoSuchFieldException e) {
         e.printStackTrace();
         return  i;
     }

这段代码主要的作用就是获取当前手机与UI图上的缩放比。

下面是核心功能代码:

@Override
    protected  void  onMeasure( int  widthMeasureSpec,  int  heightMeasureSpec) {
        //先拿到View的个数
        int  count =  this .getChildCount();
        //获取缩放比
        float  scalcX = UIUtils.getUIUtils(getContext()).getHorizontalScaleValue();
        float  scalcY = UIUtils.getUIUtils(getContext()).getVeticalScaleValue();
        //获取到所有控件上的属性
        if  (flag) {
            flag =  false ;
            for  ( int  i =  0 ; i < count; i++) {
                View child =  this .getChildAt(i);
                //获取到布局参数
                LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
                //开始完成缩放
                layoutParams.width = ( int ) (layoutParams.width * scalcX);
                layoutParams.height = ( int ) (layoutParams.height * scalcY);
                layoutParams.leftMargin = ( int ) (layoutParams.leftMargin * scalcX);
                layoutParams.rightMargin = ( int ) (layoutParams.rightMargin * scalcX);
                layoutParams.topMargin = ( int ) (layoutParams.topMargin * scalcY);
                layoutParams.bottomMargin = ( int ) (layoutParams.bottomMargin * scalcY);
 
            }
        }
        super .onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    //帮助我们把自定义属性封装起来
 
    @Override
    public  RelativeLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return  new  LayoutParams(getContext(),attrs);
    }

上面的flag是针对Measure会针对宽高会执行两次onMeasure方法,避免重复计算,导致比例失调。

以上就是全部核心代码,只需要事先在app中设置好UI图对应的手机分辨率,然后在xml文件中采用MyLayout就可以了,view控件的宽高直接按UI图上给的px直接设置上去就好。

以下是宽高皆为350px的TextView在480*800和1080*1920的屏幕上的显示效果。

这样显示效果就一样了,不需要再对px→dp进行计算。

此外,这种方式还有一个附带的属性:我们不止可以截取源生属性换成我们自己的属性,还可以在xml文件中设置自己的属性,在这里截取换成源生属性,例如:我需要子控件执行一个动画效果,我们只需在对应的控件上加上自定义属性,然后在MyLayout中解析,翻译成对应的动画效果,这个有时间我会再做一个dome。

三、总结

以上三种适配方案各有各的优点,我们结合实际情况做相应的取舍。

总的来说

  • 如果需要在不同设备上显示的大小要几乎一样,就用dp(通常是内容显示类的View)。
  • 如果要求在不同设备上显示的比例要一样,就用自定义Layout的方式(通常是动画一类的View)。
  • 如果PM有特别的要求就要根据不同分辨率指定不同的res文件。

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值