一、概述
最近学习使用Android AutoLayout解决适配问题,想要单纯使用这个库将适配很轻松的做好,还是有些问题需要注意的,个人就探索过程中遇到的问题进行记录,希望对后来者有所参考。
本库的地址:https://github.com/hongyangAndroid/AndroidAutoLayout
对于适配,我希望的效果是这样的:拿到设计图,meta信息中填入设计图的尺寸,然后不需要额外计算,布局直接抄设计图上的尺寸,不产生任何多余的资源文件,完成各种分辨率的适配。
二、直观的体验
通常带有标注的设计图如下:
如果没有提供这样的设计图,可以自己去测量,测量的方法有很多,这里不赘述了。
对于 新增旅客
布局文件可以这么写
<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面板,选择分辨率一致的设备:
然后就可以看到最为精确的
预览了。
(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。