一、概述
最近学习使用Android AutoLayout解决适配问题,想要单纯使用这个库将适配很轻松的做好,还是有些问题需要注意的,个人就探索过程中遇到的问题进行记录,希望对后来者有所参考。
本库的地址:https://github.com/hongyangAndroid/AndroidAutoLayout
对于适配,我希望的效果是这样的:拿到设计图,meta信息中填入设计图的尺寸,然后不需要额外计算,布局直接抄设计图上的尺寸,不产生任何多余的资源文件,完成各种分辨率的适配。

二、直观的体验

通常带有标注的设计图如下:

Android tablayout 去掉长按 android autolayout_android

如果没有提供这样的设计图,可以自己去测量,测量的方法有很多,这里不赘述了。

对于 新增旅客 布局文件可以这么写

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="86px"
android:layout_marginTop="26px"
android:background="#ffffffff">

<ImageView
    android:id="@+id/id_tv_add"
    android:layout_width="34px"
    android:layout_height="34px"
    android:layout_gravity="center_vertical"
    android:layout_marginLeft="276px"
    android:layout_marginTop="26px"
    android:src="@mipmap/add"
    />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="26px"
    android:layout_toRightOf="@id/id_tv_add"
    android:text="新增旅客"
    android:textColor="#1fb6c4"
    android:textSize="32px"
    />
</RelativeLayout>

对于Item的布局文件,可以这么写:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="108px"
    android:layout_marginTop="26px"
    android:background="#ffffffff"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="22px"
        android:layout_marginTop="16px"
        android:text="王大炮 WANG.DAPAO"
        android:textColor="#333"
        android:textSize="28px"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="16px"
        android:layout_marginLeft="22px"
        android:text="护照:G50786449"
        android:textColor="#999"
        android:textSize="26px"
        />

</RelativeLayout>

说明:这里的px并不代表1像素,在内部会进行百分比化处理。按照百分比的规则,完美实现了适配,这样的好处是:
再也不用拿着设计稿去想这控件的宽高到底取多少dp
再也不用去为多个屏幕去写多个dimens
再也不用去计算百分比了(如果使用百分比控件完成适配)
再也不用去跟UI MM去解释什么是dp了

三、用法
(1)将AutoLayout引入

dependencies {
    compile project(':autolayout')
}

也可以直接

dependencies {
    compile 'com.zhy:autolayout:1.4.3'
}

(2)注册设计图尺寸
在项目的AndroidManifest中注明设计稿的尺寸。

<meta-data android:name="design_width" android:value="768"></meta-data>
<meta-data android:name="design_height" android:value="1280"></meta-data>

(3)开启适配
有两种方式开启适配
1)将Activity继承AutoLayoutActivity
2)手动替换Layout布局文件中的
LinearLayout -> AutoLinearLayout
RelativeLayout -> AutoRelativeLayout
FrameLayout -> AutoFrameLayout

四、注意事项

(1)如何开启PreView

写布局文件的时候,如果不能实时的预览效果,那么体验真的是非常的不好,也在很大程度上降低开发效率,所以下面说明如何用好,用对PreView(针对该库)。

首先,要记得设计稿的尺寸,比如 768 * 1280

然后,在PreView面板,选择分辨率一致的设备:

Android tablayout 去掉长按 android autolayout_android_02

然后就可以看到最为精确的预览了。

(2)TextView的高度问题
关于TextView这个控件,可能和设计稿上会有一些出入,并非是此库的原因,而是与生俱来的特性。
设计稿一般只会标识一个字体的大小,比如你设置textSize=”20px”,实际上TextView所占据的高度肯定大于20px,字的上下都会有一定的间隙,所以一定要灵活去写字体的高度,比如对于text上下的margin可以选择尽可能小一点。或者选择别的约束条件去定位(比如上例,选择了marginBottom)
比如:

<TextView
 textSize="32px"
 layout_height="wrap_contnt"
 />

运行后肯定不是32px的高度,文字的上下方都会有一定的空隙。如果将高度写死,也会发现文字显示不全。
恩,所以呢,灵活应对这个问题,对于存在字体标识很精确的值,你可以选择:对于TextView与其他控件的上下边距呢,尽可能的稍微写小一点。

其实我上面的例子,几乎都是TextView,所有我在编写Item里面的时候,也有意缩小了一下marginTop值等。不过,对于其他控件是不存在这样的问题的。

ps:因为TextView的上述问题:所以对于居中,虽然可以使用本库通过编写margin_left,margin_top等很轻松的完成居中。但是为了精确起见,还是建议使用gravity,centerInXXX等属性完成。

(3)指定设置的值参考宽度或者高度
由于该库的特点,布局文件中宽高上的1px是不相等的,于是如果需要宽高保持一致的情况,布局中使用属性:

app:layout_auto_basewidth="height",代表height上编写的像素值参考宽度。

app:layout_auto_baseheight="width",代表width上编写的像素值参考高度。

如果需要指定多个值参考宽度即:

app:layout_auto_basewidth="height|padding"

用|隔开,类似gravity的用法,取值为:

width,height
margin,marginLeft,marginTop,marginRight,marginBottom
padding,paddingLeft,paddingTop,paddingRight,paddingBottom
textSize.

(4)将状态栏区域作为内容区域

如果某个Activity需要将状态栏区域作为实际的内容区域时,那么可用高度会变大,你所要做的只有一件事:让这个Activity实现UseStatusBar接口(仅仅作为标识左右,不需要实现任何方法),当然你肯定要自己开启windowTranslucentStatus或者设置FLAG_TRANSLUCENT_STATUS。
注意:仅仅是改变状态栏颜色,并不需要实现此接口,因为并没有实际上增加可用高度。

五、其他

目前支持属性

layout_width
layout_height
layout_margin(left,top,right,bottom)
pading(left,top,right,bottom)
textSize
maxWidth, minWidth, maxHeight, minHeight
不会影响系统所有的其他属性,以及不会影响dp,sp的使用

性能的提升

通过本库的方式去编写代码,可以在很大程序上使用margin,也就是说,对于View的位置非常好控制,从而能够减少非常多的嵌套,甚至任何一个复杂的界面做到无嵌套。

以及,几乎不需要去使用RelativeLayout的规则了,比如rightOf,完全可以由marginLeft完成,其他的rule同理。

对于LinearLayout的weight,几乎也不需要使用了,比如屏幕宽度720px,想要四个控件横向均分,完全可以写layout_width=”180px”

相信通过上述的介绍,本库适配的做法可以说是目前见过的最方便的适配方案,最大化的减轻了适配的负担,甚至比不适配时编写UI都方便。目前本库,已经尝试用于项目中,尽可能去发现一些潜在的问题。

六、潜在的问题

(1)默认使用的高度是设备的可用高度,也就是不包括状态栏和底部的操作栏的,如果希望拿设备的物理高度进行百分比化,可以在Application的onCreate方法中进行设置:

public class UseDeviceSizeApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();
        AutoLayoutConifg.getInstance().useDeviceSize();
    }
}

(2)对于其他继承系统的FrameLayout、LinearLayout、RelativeLayout的控件,比如CardView,如果希望在其内部直接支持“px”百分比化,可以自己扩展,扩展方式为下面的代码,也可参考issue#21

package com.zhy.sample.view;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;

import com.zhy.autolayout.AutoFrameLayout;
import com.zhy.autolayout.utils.AutoLayoutHelper;

/**
 * Created by zhy on 15/12/8.
 */
public class AutoCardView extends CardView
{
    private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);

    public AutoCardView(Context context)
    {
        super(context);
    }

    public AutoCardView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public AutoCardView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public AutoFrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs)
    {
        return new AutoFrameLayout.LayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        if (!isInEditMode())
        {
            mHelper.adjustChildren();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


}

(3)ListView、RecyclerView类的Item的适配(recyclerview 加载 RadioButton)
对于ListView这类控件的item,默认根局部写“px”进行适配是无效的,以为外层非AutoXXXLayout,而是ListView。但是,一行代码就可以支持了:

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    ViewHolder holder = null;
    if (convertView == null)
    {
        holder = new ViewHolder();
        convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);
        convertView.setTag(holder);
        //对于listview,注意添加这一行,即可在item上使用高度
        AutoUtils.autoSize(convertView);
    } else
    {
        holder = (ViewHolder) convertView.getTag();
    }

    return convertView;
}

注意AutoUtils.autoSize(convertView);这行代码的位置即可。
同类方法还有:
AutoUtils.autoPadding(convertView); 等。

对于RecyclerView

public ViewHolder(View itemView)
{
      super(itemView);
      AutoUtils.autoSize(itemView);
}

//...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
     View convertView = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_item, parent, false);
     return new ViewHolder(convertView);
}

一定要记得LayoutInflater.from(mContext).inflate使用三个参数的方法!

(4)导入后出现org/gradle/api/publication/maven/internal/DefaultMavenFactory

最简单的方式,通过compile 'com.zhy:autolayout:x.x.x'进行依赖使用,如果一定要以module引用,参考该issue#74

(5)RadioGroup,Toolbar等控件中的子View无法完成适配
解决方法跟(2)一样,需要自己扩展。不过这个很多使用者贡献了他们的扩展类可以直接使用, 参考autolayout-widget, 如果没有发现你需要的容器类,那么你就真的需要自行扩展了,ps:需要用到哪个copy就好了,不要直接引用autolayout-widget,因为其引用了大量的库,可能很多 库你是用不到的。例如:RadioGroup 无法适配

(6)暂时没找到横屏适配的方法,从github上提问有人说是采用传统的方法,横屏拉出来单独适配。。。。

(7)java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

这个问题是因为默认AutoLayoutActivity会继承自AppCompatActivity,所以默认需要设置 Theme.AppCompat的theme;

如果你使用的依旧是FragmentActivity等,且不考虑使用AppCompatActivity, 你可以选择自己编写一个MyAutoLayoutActivity extends 目前你使用的Activity基类,例如 MyAutoLayoutActivity extends FragmentActivity,然后将该库中AutoLayoutActivity中的逻辑 拷贝进去即可,以后你就继承你的MyAutoLayoutActivity就好了。

ps:还是建议尽快更新SDK版本使用AppCompatActivity,截止文档编写日期,SDK最新版本为1.4.3。