封装一个适合自己的AutoLayout(基于鸿洋AutoLayout)

来源:互联网 发布:nginx指定ip访问服务器 编辑:程序博客网 时间:2024/05/23 13:57

简介

为了提升用户体验,经理要求移动端app适配竖屏版平板,我看了下资源包中的layout包,共计200+xml布局文件,不管是做适配文件,还是进行自动适配ViewGroupPrecentRelativelayoutConstraintlayout)进行适配,都是一个不小的工作量,更不用说中间还少不了各种测试......繁琐的操作流程并不适合我这种懒人,对我来说,追吼的就是找个能用的轮子改改,于是,看上了鸿洋的Autolayout

 

要解决的问题

在适配过称中发现一下几个问题:

1、平板适配图片畸形(拉宽)

2、Auto控件不稳定、已成型的布局替换不方便

3、dp在适配中的坑

解决方案

1、适配畸形(过宽、过长)

读了下AutoLayout中适配部分的源码,原理就是取出View中内外间距、宽高等基础距离数据,然后按照给定的参考机型数据进行计算(mf中定义的分辨率),计算出对应的数值。

虽然说这样通过设置pix可以同比放大尺寸和间距,但必然会导致一个问题,在类HeighAttrWidthAttr以及他们的父类AutoAttr中可以看出,高度和宽度按照的比例是高度对高度、宽度对宽度,如果1080p的手机对应1080p的平板这固然没问题,但是若是1080p的手机对应到小米那种(43)的2k平板上呢,或者更长的S8上呢,很显然宽高比不同步,必然会出问题,相较于1080p,2k的小米平板显得更宽,这也自然的会导致原本正方形、圆形等图片发生明显的形变。

     其实这个问题很好解决,按照固定比例计算就可以了,但是这里有个小问题,按照谁的固定比例更合适?理论上来讲,如果页面是静态的(不可滚动的),理应按照屏幕对应的宽高与参考宽高的最小比值进行计算,这样才会防止图片的遮盖、畸形,但是显示区域过窄并不适合用户观看,但是如果按照最大比例计算,则会出现试图的重叠、消失......这需要手工的在外层加上一层ScrollVeiw。


2、Auto控件不稳定、已成型的布局替换不方便

AutoLayout提供的实例中,有个ListView的案例,其中的子View并不是通过使用AutoLayout进行布局适配的,而是调用了AutoUtils中的auto

com.zhy.autolayout.utils;
,再进一步看,auto方法中还有其他四个方法的调用
public static void auto(View view)    {        if(view instanceof TabLayout)            return;        autoSize(view);        autoPadding(view);        autoMargin(view);        autoTextSize(view, AutoAttr.BASE_DEFAULT);    }
不读源码也可以通过名字看出,这四种方法针对的是View的尺寸、内间距、外间距、字体尺寸,一autoSize为例,继续往下看
public static void autoSize(View view)    {        auto(view, Attrs.WIDTH | Attrs.HEIGHT, AutoAttr.BASE_DEFAULT);    }
  public static void auto(View view, int attrs, int base)    {        AutoLayoutInfo autoLayoutInfo = AutoLayoutInfo.getAttrFromView(view, attrs, base);        if (autoLayoutInfo != null)            autoLayoutInfo.fillAttrs(view);    }
 public static AutoLayoutInfo getAttrFromView(View view, int attrs, int base)    {        ViewGroup.LayoutParams params = view.getLayoutParams();        if (params == null) return null;        AutoLayoutInfo autoLayoutInfo = new AutoLayoutInfo();        // width & height        if ((attrs & Attrs.WIDTH) != 0 && params.width > 0)        {            autoLayoutInfo.addAttr(WidthAttr.generate(params.width, base));        }        if ((attrs & Attrs.HEIGHT) != 0 && params.height > 0)        {            autoLayoutInfo.addAttr(HeightAttr.generate(params.height, base));        }                      ......        return autoLayoutInfo;    }
在以宽度为例进入
public static WidthAttr generate(int val, int baseFlag)    {        WidthAttr widthAttr = null;        switch (baseFlag)        {            case AutoAttr.BASE_WIDTH:                widthAttr = new WidthAttr(val, Attrs.WIDTH, 0);                break;            case AutoAttr.BASE_HEIGHT:                widthAttr = new WidthAttr(val, 0, Attrs.WIDTH);                break;            case AutoAttr.BASE_DEFAULT:                widthAttr = new WidthAttr(val, 0, 0);                break;        }        return widthAttr;    }

 private List<AutoAttr> autoAttrs = new ArrayList<>();    public void addAttr(AutoAttr autoAttr)    {        autoAttrs.add(autoAttr);    }
最终的属性加入到了一个list里边

回到方法auto中,查看autoInfo.fillattrs(view);

 public void fillAttrs(View view)    {        for (AutoAttr autoAttr : autoAttrs)        {            autoAttr.apply(view);        }    }
可以看出,这段其实就是将我们所便利的属性结果进行了实际运行,也就是说auto这个方案和AutoLayout的试试方案是类似的,只不过这个方案针对于已经成型的View,而不在需要进行布局替换。

明白了其中的道理问题就好解决了,在auto方案外层再次封装一个autoVp,让其遍历所持有的ViewGroup的属性,如果子View是ViewGroup,则继续向下便利:

public static boolean isVp(View view){        return view instanceof ViewGroup ?true: false;    }
public static void autoVP(View view){        if(isVp(view)){            for(int i=0;i<((ViewGroup)view).getChildCount();i++){                AutoUtils.auto(((ViewGroup)view).getChildAt(i));                autoVP(((ViewGroup)view).getChildAt(i));            }        }    }
这样的话,把从布局文件的中获得的View直接扔进autoVp里就可以自动适配了。


3、dp在适配中的坑

看下dp转px的公式,你会发现其实dp对应的应该是一个物理单位,也就是说理想情况下,dp并不会因为设备的大小与分辨率的变化而变化,但是这样的话在使用autoVp时就会出现很古怪的现象,比如在mf的meta中设置的是1080p,放在720p的手机上就会显的特别小,放在小米平板上也不会显示的大多少,而且还会变宽。所以,在使用autoVp时,就需要引入一个新的meta,这个meta设置为模型手机的尺寸(以nexus5为模型机,尺寸4.9)

<meta-data android:name="phone_size" android:value="4.9"></meta-data>
在AutoLayoutConfig中引入:
   private void getMetaData(Context context)    {        PackageManager packageManager = context.getPackageManager();        ApplicationInfo applicationInfo;        try        {            applicationInfo = packageManager.getApplicationInfo(context                    .getPackageName(), PackageManager.GET_META_DATA);            if (applicationInfo != null && applicationInfo.metaData != null)            {                mDesignWidth = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH);                mDesignHeight = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT);                mPhoneSize = (float) applicationInfo.metaData.get(KEY_PHONE_SIZE);            }        } catch (PackageManager.NameNotFoundException e)        {            throw new RuntimeException(                    "you must set " + KEY_DESIGN_WIDTH + " and " + KEY_DESIGN_HEIGHT + "  in your manifest file.", e);        }        L.e(" designWidth =" + mDesignWidth + " , designHeight = " + mDesignHeight);    }
AutoUtils中增加方法

  public static int getPercentPhoneSizeBigger(int val){        float designPhoneSize = (float) AutoLayoutConifg.getInstance().getPhoneSize();        float mPhoneSize = AutoLayoutConifg.getInstance().getScreenSize();        float res = val * mPhoneSize;        if (res % designPhoneSize == 0)        {            return (int)(res / designPhoneSize);        } else        {            return (int)(res / designPhoneSize + 1);        }    }
我这里直接在原有的基础上修改了宽高的计算方式,如果有原有参数使用的需求,大可独立出来一个方法:

    protected int getPercentWidthSize()    {//        return AutoUtils.getPercentWidthSizeBigger(pxVal);        return AutoUtils.getPercentPhoneSizeBigger(pxVal);    }    protected int getPercentHeightSize()    {//        return AutoUtils.getPercentHeightSizeBigger(pxVal);        return AutoUtils.getPercentPhoneSizeBigger(pxVal);    }
这样修改就完成了,对使用autoVp的View添加子View时,需要现对子View使用autoVP


参考文档 鸿洋http://blog.csdn.net/lmj623565791/article/details/49990941/