自定义控件实践——流式布局
来源:互联网 发布:在家学英语软件 编辑:程序博客网 时间:2024/04/20 06:09
1 Preface
在这篇文章中,我们将实现一个自定义控件,类似水平方向的 LinearLayout,区别是:当水平方向上空间不足时,子 View 自动从下一行开始放置。
这种控件有个统称:流式布局(FlowLayout)。
2 Situation
先来看一个微信朋友圈详情页的照片墙效果:
我们通过 View Hierarchy 来看下这些头像的布局:
可以看到,每一行头像都是一个水平方向的 LinearLayout。外面也是一个 LinearLayout。
当然,这是最容易想到的实现方式。
好处是思路简单,代码容易维护,即使是新手也能维护。
坏处是出现层级嵌套,影响性能。
3 Target
从减少层级的角度,对这个布局做下优化。
这种布局的本质是:有一个 ViewGroup,对其添加子 View 的时候,从左至右水平摆放,当第 n 行的水平空间不足时从第 n+1 行最左侧开始摆放,即一个会自动换行的 ViewGroup。
这种方式的好处很明显:布局层级明显减少。
由于子 View 从上一行末尾换到下一行首时的轨迹像 Z,,我们姑且称之为 ZLayout。
4 Action
继承 ViewGroup,重写 onMeasure()、onLayout()方法。
5 Result
5.1 ZLayout
该控件已经开源到 Github 上,具体代码见链接。
5.2 使用场景
ZLayout 除了可以实现 Situation 中的照片墙,还有下面一种用法。
截取大众点评 app 个人空间的一个效果:
注意昵称后面 lv5 和 vip 图标
如果用户昵称后面除了 lv5 和 vip 之外,还有其他头衔,很可能因为空间不够而导致一行放不下,我们假设空间不足是不显示剩余的头衔图标。这种情况该怎么办呢?
在昵称后面放一个 LinearLayout 行吗?我们试验下。
用 200px x 100px 的飞机图来代表头衔,分别使用不同的 LayoutParams 参数,看能否达到上述目标。
ico_aereo.png
实验代码:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout container = new LinearLayout(getApplicationContext()); container.setOrientation(LinearLayout.VERTICAL); container.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // 第1行 LinearLayout line1 = new LinearLayout(getApplicationContext()); line1.setOrientation(LinearLayout.HORIZONTAL); LinearLayout.LayoutParams lpWrapContent = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); // 第2行 LinearLayout line2 = new LinearLayout(getApplicationContext()); line2.setOrientation(LinearLayout.HORIZONTAL); LinearLayout.LayoutParams lpExactDimension = new LinearLayout.LayoutParams(200, 100); for (int i = 0; i < 200; i++) { ImageView img1 = new ImageView(getApplicationContext()); img1.setImageDrawable(getResources().getDrawable(R.mipmap.ico_aereo)); line1.addView(img1, lpWrapContent); ImageView img2 = new ImageView(getApplicationContext()); img2.setImageDrawable(getResources().getDrawable(R.mipmap.ico_aereo)); line2.addView(img2, lpExactDimension); } container.addView(line1, lpWrapContent); container.addView(line2, lpWrapContent); setContentView(container); }}
运行结果如下:
从图可以看出,将 ImageView 的 LayoutParams 设置为 WRAP_CONTENT 和精确值,效果是不同的。具体来说,当剩余水平空间不足以放下一个原始尺寸的 ImageView 时,前者等比例缩小显示,后者则截断显示。
注意,上述结果仅仅是对于 ImageView 而言的,如果是其他类型,如 TextView,则不一定成立。换做 TextView 的实验结果如下:
第1行的 TextView 的 LayoutParams 全部是 WRAP_CONTENT;第2行的 TextView 的 width=127px,height=WRAP_CONTENT。
试验代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout container = new LinearLayout(getApplicationContext()); container.setOrientation(LinearLayout.VERTICAL); container.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // 第1行 LinearLayout line1 = new LinearLayout(getApplicationContext()); line1.setOrientation(LinearLayout.HORIZONTAL); LinearLayout.LayoutParams lpWrapContent = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); // 第2行 LinearLayout line2 = new LinearLayout(getApplicationContext()); line2.setOrientation(LinearLayout.HORIZONTAL); LinearLayout.LayoutParams lpExactDimension = new LinearLayout.LayoutParams(127, ViewGroup.LayoutParams.WRAP_CONTENT); for (int i = 0; i < 200; i++) { TextView view1 = new TextView(getApplicationContext()); view1.setText("love u..."); view1.setTextColor(getResources().getColor(R.color.colorAccent)); line1.addView(view1, lpWrapContent); TextView view2 = new TextView(getApplicationContext()); view2.setText("love u..."); view2.setTextColor(getResources().getColor(R.color.colorAccent)); line2.addView(view2, lpExactDimension); } container.addView(line1, lpWrapContent); container.addView(line2, lpWrapContent); setContentView(container); }
显然,LinearLayout 对 ImageView 的 layout 策略是无法满足我们的需求的。
ZLayout 可以做到。只需将设置其一个属性即可:
z:maxLines = "1"
或
z.setMaxLines(1);
即,ZLayout 最多只有1行子控件,其余的不做处理。
6 References
- ZLayout
- 安卓自定义控件原理及实践
- 自定义控件实践——流式布局
- 自定义流式布局控件
- 自定义控件:流式布局
- 流式布局(自定义控件)
- 自定义流式布局控件FlowLayout
- 流式布局 自定义控件 特别热
- android 自定义控件实现流式布局
- 自定义控件之-流式布局FlowLayout
- Android自定义控件--流式布局(FlowLayout)
- FLEX4实践—自定义控件皮肤
- android控件——创建自定义控件或布局
- 自定义控件之流式布局
- Android自定义控件--流式布局(FlowLayout)--自动适配
- Android流式标签布局,自定义标签控件tagView
- android 自定义控件--用viewGroup实现流式布局
- 自定义控件之——自动显示隐藏头布局
- 自定义控件实践——单选/多选文本标签流
- Android自定义控件之流式布局
- classloader详解
- 各种常用的正则表达式
- 烦恼的高考志愿(一)
- mac上c++11的编译问题
- jzoj 1521. 【普及模拟】好数 数位dp
- 自定义控件实践——流式布局
- LeetCode 5. Longest Palindromic Substring
- php数组,常用函数
- redis 移植开发
- 安装eclipse 遇到Java was started but returned exit code=13
- ubuntu对于mysql的卸载,重装
- Java泛型的约束和局限性
- python 遍历文件夹得到子一级文件夹列表
- player stage robot ---tcp client