本文讨论的是下图的这种数据展示方式

通过本文可以学到的内容

===View绘制的工作流程measure和Layout,即测量和布局;

===动态创建和添加子View,以及设置点击事件的一种思路

1、主要使用的是一个继承自ViewGroup的自定义类NewLineLayout

代码如下:

  1. /**
  2. * 可以换行的布局
  3. */
  4.  
  5. import android.content.Context;
  6. import android.util.AttributeSet;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9.  
  10. import com.orhanobut.logger.Logger;
  11.  
  12. public class NewLineLayout extends ViewGroup {
  13.  
  14. private int mScreenWidth;//屏幕宽度
  15. private int horizontalSpace, verticalSpace;//子View间距
  16.  
  17. public NewLineLayout(Context context) {
  18. this(context, null);
  19. }
  20.  
  21. public NewLineLayout(Context context, AttributeSet attrs) {
  22. super(context, attrs);
  23. mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
  24. }
  25.  
  26. @Override
  27. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  28. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  29. //确定此容器的宽高
  30. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  31. int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  32. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  33. int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  34.  
  35. //摆放子view
  36. View child;
  37. //子view摆放的起始位置
  38. int left = getPaddingLeft();
  39. //存储一行view最大的高度,用于子view进行换行时高度的计算
  40. int maxHeightInLine = 0;
  41. //存储所有行的高度相加,用于确定此容器的高度
  42. int allHeight = 0;
  43. //遍历子view
  44. int childCount = getChildCount();
  45. for (int i = 0; i < childCount; i++) {
  46. child = getChildAt(i);
  47. //测量子View宽高
  48. measureChild(child, widthMeasureSpec, heightMeasureSpec);
  49. //两两对比,取得一行中最大的高度
  50. int childHeight = child.getMeasuredHeight() +
  51. child.getPaddingTop() + child.getPaddingBottom();
  52. if (childHeight > maxHeightInLine) {
  53. maxHeightInLine = childHeight;
  54. }
  55. left += child.getMeasuredWidth() + horizontalSpace +
  56. child.getPaddingLeft() + child.getPaddingRight();
  57. //这一行所有子view相加的宽度大于容器的宽度,需要换行
  58. if (left > widthSize - getPaddingRight()) {
  59. //换行的首个子view,起始left为容器的paddingLeft
  60. left = getPaddingLeft();
  61. //累积行的总高度
  62. allHeight += maxHeightInLine + verticalSpace;
  63. //重置最大高度
  64. maxHeightInLine = 0;
  65. } else {
  66. allHeight += maxHeightInLine;
  67. }
  68. }
  69.  
  70. if (widthMode != MeasureSpec.EXACTLY) {
  71. widthSize = mScreenWidth;//如果没有指定宽,则默认为屏幕宽
  72. }
  73.  
  74. if (heightMode != MeasureSpec.EXACTLY) {//如果没有指定高度
  75. heightSize = allHeight + getPaddingBottom() + getPaddingTop();
  76. }
  77.  
  78. setMeasuredDimension(widthSize, heightSize);
  79. }
  80.  
  81. @Override
  82. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  83. if (changed) {
  84. //摆放子view
  85. View child;
  86. //子view摆放的起始位置
  87. int left = getPaddingLeft();
  88. int top = getPaddingTop();
  89. //存储一行view最大的高度,用于子view进行换行时高度的计算
  90. int maxHeightInLine = 0;
  91. for (int i = 0, len = getChildCount(); i < len; i++) {
  92. child = getChildAt(i);
  93. //从第二个子view开始算起
  94. //第一个子view默认从头开始摆放
  95. if (i > 0) {
  96. //两两对比,取得一行中最大的高度
  97. if (getChildAt(i - 1).getMeasuredHeight() > maxHeightInLine) {
  98. maxHeightInLine = getChildAt(i - 1).getMeasuredHeight();
  99. }
  100. //当前子view的起始left为 上一个子view的宽度+水平间距
  101. left += getChildAt(i - 1).getMeasuredWidth() + horizontalSpace;
  102. //这一行所有子view相加的宽度大于容器的宽度,需要换行
  103. if (left + child.getMeasuredWidth() > getWidth() - getPaddingRight()) {
  104. //换行的首个子view,起始left为容器的paddingLeft
  105. left = getPaddingLeft();
  106. //top的位置为上一行中拥有最大高度的某个View的高度+垂直间距
  107. top += maxHeightInLine + verticalSpace;
  108. //将上一行View的最大高度置0
  109. maxHeightInLine = 0;
  110. }
  111. }
  112. //摆放子view
  113. child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
  114. }
  115. }
  116. }
  117.  
  118. /**
  119. * 设置子view间的水平间距
  120. *
  121. * @param horizontalSpace
  122. */
  123. public void setHorizontalSpace(int horizontalSpace) {
  124. this.horizontalSpace = horizontalSpace;
  125. }
  126.  
  127. /**
  128. * 设置子view间的垂直间距
  129. *
  130. * @param verticalSpace
  131. */
  132. public void setVerticalSpace(int verticalSpace) {
  133. this.verticalSpace = verticalSpace;
  134. }
  135. }

注释比较清楚,耐心点绝对能理解

2、动态创建TextView的方法

  1. /**
  2. * 创建自定义View
  3. *
  4. * @param text 文本内容
  5. */
  6. private TextView newCustomTextView(String text) {
  7. TextView textView = new TextView(activity);
  8. textView.setText(text);
  9. //指定文字大小
  10. textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, activity.getResources().getDimension(R.dimen.font_text));
  11. //设置最小宽高
  12. textView.setMinHeight((int) activity.getResources().getDimension(R.dimen.text_height));
  13. textView.setMinWidth((int) activity.getResources().getDimension(R.dimen.text_width));
  14. //设置内边距
  15. int padding = (int) activity.getResources().getDimension(R.dimen.space_tiny);
  16. textView.setPadding(padding, 0, padding, 0);
  17. textView.setGravity(Gravity.CENTER);
  18. return textView;
  19. }

3、添加子View,利用tag属性关联View点击事件

  1. private String lineString = "_";//自定义分割字符
  2.  
  3. private void initView() {
  4. final TextView[] textViewArray = new TextView[dataList.length];
  5. View.OnClickListener onClickListener = new View.OnClickListener() {
  6. @Override
  7. public void onClick(View view) {
  8. String tag = (String) view.getTag();
  9. if (!StringUtil.isEmpty(tag)) {
  10. int lineIndex = tag.indexOf(lineString);
  11. int index = StringUtil.toInt(tag.substring(0, lineIndex), 0);
  12. //处理按钮组单选的情况
  13. changeButtonState(textViewArray, index);
  14. ToastUtil.showShortToast(activity, dataList[index]);
  15. }
  16. }
  17. };
  18. for (int i = 0; i < dataList.length; i++) {
  19. textViewArray[i] = newCustomTextView(dataList[i]);
  20. //组合tag处理单击事件
  21. String tag = i + lineString + dataList[i];
  22. textViewArray[i].setTag(tag);
  23. textViewArray[i].setOnClickListener(onClickListener);
  24. //添加自定义View
  25. layContent.addView(textViewArray[i]);
  26. }
  27. }

4、按钮组的单选实现方法

  1. /**
  2. * 单选按钮组
  3. *
  4. * @param index 根据序号改变按钮组的状态
  5. */
  6. public void changeButtonState(TextView[] textView, int index) {
  7. int size = textView.length;
  8. for (int i = 0; i < size; i++) {
  9. textView[i].setBackgroundResource(text_normal);
  10. textView[i].setTextColor(txt_normal);
  11. }
  12. textView[index].setBackgroundResource(text_select);
  13. textView[index].setTextColor(txt_select);
  14. }

===代码地址===

Android可以换行的布局的更多相关文章

  1. Android经常使用的布局类整理(一)

    Android经常使用的布局类整理 近期又回头做了一下android的项目,发觉越来越不从心,非常多东西都忘了,简单的页面布局也非常多写不出来,首先还是先整理一下一些会混淆的概念先 layout_wi ...

  2. Android UI组件:布局管理器

    为了更好的管理Android应用的用户界面中的组件,Android提供了布局管理器.通过使用布局管理器,Android应用的图形用户界面具有良好的平台无关性.通常,推荐使用布局管理器来管理组件的分布. ...

  3. Android中的LinearLayout布局

    LinearLayout : 线性布局 在一般情况下,当有很多控件需要在一个界面列出来时,我们就可以使用线性布局(LinearLayout)了,  线性布局是按照垂直方向(vertical)或水平方向 ...

  4. Android开发-之五大布局

    在html中大家都知道布局是什么意思了,简单来说就是将页面划分模块,比如html中的div.table等.那么Android中也是这样的.Android五大布局让界面更加美化,开发起来也更加方便.当然 ...

  5. 简单研究Android View绘制三 布局过程

    2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: /* ...

  6. Android性能优化之布局优化

    最新最准确内容建议直接访问原文:Android性能优化之布局优化 本文为Android性能优化的第二篇——布局优化,主要介绍使用抽象布局标签(include, viewstub, merge).去除不 ...

  7. Android中的五大布局

    Android中的五大布局 1.了解布局 一个丰富的界面总是要由很多个控件组成的,那我们如何才能让各个控件都有条不紊地 摆放在界面上,而不是乱糟糟的呢?这就需要借助布局来实现了.布局是一种可用于放置很 ...

  8. 关于android LinearLayout的比例布局(转载)

    关于android LinearLayout的比例布局,主要有以下三个属性需要设置: 1,android:layout_width,android:layout_height,android:layo ...

  9. 【转】Android性能优化之布局优化篇

     转自:http://blog.csdn.net/feiduclear_up/article/details/46670433 Android性能优化之布局优化篇 分类: andorid 开发2015 ...

随机推荐

  1. 概念 : 过程 : 前台login

    为了解决自动登入.访问权限机制.登入权限而诞生了这过程,过程需要概念来维护记忆. 通过singlePageAutoLoginLocalStorage, UIStateLocalStorage, Use ...

  2. 视频直播SDK-ios版

    IOS视频直播接入说明 一.名词解释 分辨率:用于计算机视频处理的图像,以水平和垂直方向上所能显示的像素数来表示分辨率.常见视频分辨率的有1080P即1920x1080,720P即1080x720,6 ...

  3. java之重定向与转发

    昨天搞了一个问题,关于手机返回按钮的(Android机,ios没有返回键) 在每一步操作都要进过鉴权,如果鉴权不通过就需要跳转到指定jsp页面,再进行link:到app进行登录操作: 然后问题出现了, ...

  4. apicloud下拉刷新

    //下拉 apiready = function () { var param = {}; toDoRequest(); param.loadingImgae = 'widget://image/re ...

  5. Linux进程管理详解

    何谓进程?进程,就是正在执行的一个程序或命令,每一个进程都是一个运行实体,有自己的地址空间,并占用一定的系统资源.简而言之,进程就是运行中的程序.在Linux中,诸如ls等命令都是进程,只不过某些命令 ...

  6. 微信小程序之提高应用速度小技巧

    作者:vicyao, 腾讯web前端开发 高级工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/294.htm ...

  7. SVNManager配置

    1.svn与apache的安装 yum install -y subversion httpd   httpd.conf添加如下内容: LoadModule dav_svn_module module ...

  8. 九度oj1163题

    题目描述: 输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1. 输入: 输入有多组数据. 每组一行,输入n. ...

  9. python 机器学习 K-近邻算法

    本人想边写文章,边学习,用的是 网上最火的<机器学习实战>machine learning in action 来做一次实践. 希望在过程中理顺思路之余,也有分享自己的一些理解,学习.加油 ...

  10. Android Fragment 开发(一)

    最近在学习Fragment 的使用,想弄一个在子窗体中调用父的方法,一直报错,终于找到解决方法啦 父窗体名称:MainActivity 父中有一个public的方法show() 子窗体调用: Main ...