Android可以换行的布局
本文讨论的是下图的这种数据展示方式

通过本文可以学到的内容
===View绘制的工作流程measure和Layout,即测量和布局;
===动态创建和添加子View,以及设置点击事件的一种思路
1、主要使用的是一个继承自ViewGroup的自定义类NewLineLayout
代码如下:
/**
* 可以换行的布局
*/
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.orhanobut.logger.Logger;
public class NewLineLayout extends ViewGroup {
private int mScreenWidth;//屏幕宽度
private int horizontalSpace, verticalSpace;//子View间距
public NewLineLayout(Context context) {
this(context, null);
}
public NewLineLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//确定此容器的宽高
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//摆放子view
View child;
//子view摆放的起始位置
int left = getPaddingLeft();
//存储一行view最大的高度,用于子view进行换行时高度的计算
int maxHeightInLine = 0;
//存储所有行的高度相加,用于确定此容器的高度
int allHeight = 0;
//遍历子view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
child = getChildAt(i);
//测量子View宽高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//两两对比,取得一行中最大的高度
int childHeight = child.getMeasuredHeight() +
child.getPaddingTop() + child.getPaddingBottom();
if (childHeight > maxHeightInLine) {
maxHeightInLine = childHeight;
}
left += child.getMeasuredWidth() + horizontalSpace +
child.getPaddingLeft() + child.getPaddingRight();
//这一行所有子view相加的宽度大于容器的宽度,需要换行
if (left > widthSize - getPaddingRight()) {
//换行的首个子view,起始left为容器的paddingLeft
left = getPaddingLeft();
//累积行的总高度
allHeight += maxHeightInLine + verticalSpace;
//重置最大高度
maxHeightInLine = 0;
} else {
allHeight += maxHeightInLine;
}
}
if (widthMode != MeasureSpec.EXACTLY) {
widthSize = mScreenWidth;//如果没有指定宽,则默认为屏幕宽
}
if (heightMode != MeasureSpec.EXACTLY) {//如果没有指定高度
heightSize = allHeight + getPaddingBottom() + getPaddingTop();
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
//摆放子view
View child;
//子view摆放的起始位置
int left = getPaddingLeft();
int top = getPaddingTop();
//存储一行view最大的高度,用于子view进行换行时高度的计算
int maxHeightInLine = 0;
for (int i = 0, len = getChildCount(); i < len; i++) {
child = getChildAt(i);
//从第二个子view开始算起
//第一个子view默认从头开始摆放
if (i > 0) {
//两两对比,取得一行中最大的高度
if (getChildAt(i - 1).getMeasuredHeight() > maxHeightInLine) {
maxHeightInLine = getChildAt(i - 1).getMeasuredHeight();
}
//当前子view的起始left为 上一个子view的宽度+水平间距
left += getChildAt(i - 1).getMeasuredWidth() + horizontalSpace;
//这一行所有子view相加的宽度大于容器的宽度,需要换行
if (left + child.getMeasuredWidth() > getWidth() - getPaddingRight()) {
//换行的首个子view,起始left为容器的paddingLeft
left = getPaddingLeft();
//top的位置为上一行中拥有最大高度的某个View的高度+垂直间距
top += maxHeightInLine + verticalSpace;
//将上一行View的最大高度置0
maxHeightInLine = 0;
}
}
//摆放子view
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
}
}
/**
* 设置子view间的水平间距
*
* @param horizontalSpace
*/
public void setHorizontalSpace(int horizontalSpace) {
this.horizontalSpace = horizontalSpace;
}
/**
* 设置子view间的垂直间距
*
* @param verticalSpace
*/
public void setVerticalSpace(int verticalSpace) {
this.verticalSpace = verticalSpace;
}
}
注释比较清楚,耐心点绝对能理解
2、动态创建TextView的方法
/**
* 创建自定义View
*
* @param text 文本内容
*/
private TextView newCustomTextView(String text) {
TextView textView = new TextView(activity);
textView.setText(text);
//指定文字大小
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, activity.getResources().getDimension(R.dimen.font_text));
//设置最小宽高
textView.setMinHeight((int) activity.getResources().getDimension(R.dimen.text_height));
textView.setMinWidth((int) activity.getResources().getDimension(R.dimen.text_width));
//设置内边距
int padding = (int) activity.getResources().getDimension(R.dimen.space_tiny);
textView.setPadding(padding, 0, padding, 0);
textView.setGravity(Gravity.CENTER);
return textView;
}
3、添加子View,利用tag属性关联View点击事件
private String lineString = "_";//自定义分割字符
private void initView() {
final TextView[] textViewArray = new TextView[dataList.length];
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
String tag = (String) view.getTag();
if (!StringUtil.isEmpty(tag)) {
int lineIndex = tag.indexOf(lineString);
int index = StringUtil.toInt(tag.substring(0, lineIndex), 0);
//处理按钮组单选的情况
changeButtonState(textViewArray, index);
ToastUtil.showShortToast(activity, dataList[index]);
}
}
};
for (int i = 0; i < dataList.length; i++) {
textViewArray[i] = newCustomTextView(dataList[i]);
//组合tag处理单击事件
String tag = i + lineString + dataList[i];
textViewArray[i].setTag(tag);
textViewArray[i].setOnClickListener(onClickListener);
//添加自定义View
layContent.addView(textViewArray[i]);
}
}
4、按钮组的单选实现方法
/**
* 单选按钮组
*
* @param index 根据序号改变按钮组的状态
*/
public void changeButtonState(TextView[] textView, int index) {
int size = textView.length;
for (int i = 0; i < size; i++) {
textView[i].setBackgroundResource(text_normal);
textView[i].setTextColor(txt_normal);
}
textView[index].setBackgroundResource(text_select);
textView[index].setTextColor(txt_select);
}
===代码地址===
Android可以换行的布局的更多相关文章
- Android经常使用的布局类整理(一)
Android经常使用的布局类整理 近期又回头做了一下android的项目,发觉越来越不从心,非常多东西都忘了,简单的页面布局也非常多写不出来,首先还是先整理一下一些会混淆的概念先 layout_wi ...
- Android UI组件:布局管理器
为了更好的管理Android应用的用户界面中的组件,Android提供了布局管理器.通过使用布局管理器,Android应用的图形用户界面具有良好的平台无关性.通常,推荐使用布局管理器来管理组件的分布. ...
- Android中的LinearLayout布局
LinearLayout : 线性布局 在一般情况下,当有很多控件需要在一个界面列出来时,我们就可以使用线性布局(LinearLayout)了, 线性布局是按照垂直方向(vertical)或水平方向 ...
- Android开发-之五大布局
在html中大家都知道布局是什么意思了,简单来说就是将页面划分模块,比如html中的div.table等.那么Android中也是这样的.Android五大布局让界面更加美化,开发起来也更加方便.当然 ...
- 简单研究Android View绘制三 布局过程
2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: /* ...
- Android性能优化之布局优化
最新最准确内容建议直接访问原文:Android性能优化之布局优化 本文为Android性能优化的第二篇——布局优化,主要介绍使用抽象布局标签(include, viewstub, merge).去除不 ...
- Android中的五大布局
Android中的五大布局 1.了解布局 一个丰富的界面总是要由很多个控件组成的,那我们如何才能让各个控件都有条不紊地 摆放在界面上,而不是乱糟糟的呢?这就需要借助布局来实现了.布局是一种可用于放置很 ...
- 关于android LinearLayout的比例布局(转载)
关于android LinearLayout的比例布局,主要有以下三个属性需要设置: 1,android:layout_width,android:layout_height,android:layo ...
- 【转】Android性能优化之布局优化篇
转自:http://blog.csdn.net/feiduclear_up/article/details/46670433 Android性能优化之布局优化篇 分类: andorid 开发2015 ...
随机推荐
- 规范 : 过程 : login cookies sessionTimeOut
规范 用户登入网站将得到一个cookies,如果用户有2个account,各别登入admin page 和 client page,是会得到2个cookies. 在reload page时,后台会得到 ...
- 第24篇 js小知识和“坑”
前面说了说了js的相关知识,基本上除了语法外,把项目常用的知识做了一个梳理,现在说下js的其它方面的知识,这些知识不成体系,属于不理解对于一般开发没什么太多影响,但如果理解清楚,可以更好去开发. js ...
- jQuery后续...
jQuery 选择器 1.jQuery选择器的简介 (1). Jquery中的选择器完全继承了CSS的风格,利用Jquery选择器,可以非常便捷和快速的找出特定的Dom元素,然后为他们添加相应的行为, ...
- DOM操作和样式操作库的封装
一.DOM常用方法和属性复习 以下粗略的罗列一下DOM的常用方法和属性,由于不是介绍DOM的基础内容,所以就不一一详细说明各个方法和属性了(学习DOM的封装的,一般都对基础DOM比较熟悉了). 1.1 ...
- Objective-C日记-之NSPredicate
谓语:NSPredicate 1,概述: 用于设置指定的过滤器的条件,通俗的理解就是设置选择的条件,再对指定的对象应用这个条件 2,用法: a,首先设置谓语. NSPredicate *predica ...
- 在.NET项目中使用PostSharp,使用CacheManager实现多种缓存框架的处理
在前面几篇随笔中,介绍了PostSharp的使用,以及整合MemoryCache,<在.NET项目中使用PostSharp,实现AOP面向切面编程处理>.<在.NET项目中使用Pos ...
- NSPredicate 查询/搜索
IOS NSPredicate 查询.搜索 简述:Cocoa框架中的NSPredicate用于查询,原理和用法都类似于SQL中的where,作用相当于数据库的过滤取. 最常用到的函数 + (NSP ...
- swift -- 代理delegate
1.声明协议 protocol SecondDelagate { func sendValue(text : String!) -> Void } 2.声明代理属性 var delegate : ...
- Unity获取安卓手机运营商,电量,wifi信号强度,本地Toast,获取已安装apk,调用第三方应用,强制自动重启本应用
一个完整的游戏项目上线需要不断的完善优化,但是到了后期的开发不再仅仅是游戏了,它的复杂度远远大于纯粹的应用开发.首先必须要考虑的就是集成第三方SDK,支付这块渠道商已经帮你我们做好了,只需要按照文档对 ...
- (28)初识struts框架
案例剖析中介绍了一个自己写的mystruts框架,这里开始简单的认识struts框架. 1.先看一个简单的struts开发的例子来熟悉一下流程 Struts就是基于mvc模式的框架! (struts其 ...