自定义ViewGroup 流式布局
使用
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_flowlayout);FlowLayout flow_layout = (FlowLayout) findViewById(R.id.flow_layout);//一定要注意,我们自定义的FlowLayout中使用的是MarginLayoutParams,所以这里也只能用MarginLayoutParams,不然报ClassCastExceptionMarginLayoutParams marginLayoutParams = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);int margins = (int) (2 * getResources().getDisplayMetrics().density + 0.5f);marginLayoutParams.setMargins(margins, margins, margins, margins);TextView tv1 = new TextView(new ContextThemeWrapper(this, R.style.text_style3), null, 0);//这是代码中设置style的方法!TextView tv2 = new TextView(new ContextThemeWrapper(this, R.style.text_style2), null, 0);TextView tv3 = new TextView(new ContextThemeWrapper(this, R.style.text_style1), null, 0);TextView tv4 = new TextView(new ContextThemeWrapper(this, R.style.text_style2), null, 0);tv1.setText("代码中添加View");tv2.setText("并设置style");tv3.setText("并设置margins");tv4.setText("博客:http://www.cnblogs.com/baiqiantao/,如果TextView内容特别长会是这种效果");tv1.setLayoutParams(marginLayoutParams);tv2.setLayoutParams(marginLayoutParams);tv3.setLayoutParams(marginLayoutParams);tv4.setLayoutParams(marginLayoutParams);flow_layout.addView(tv1);flow_layout.addView(tv2);flow_layout.addView(tv3);flow_layout.addView(tv4);}}<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E1E6F6"android:orientation="vertical" ><com.bqt.myview.FlowLayoutandroid:id="@+id/flow_layout"android:layout_width="wrap_content"android:layout_height="wrap_content" ><TextViewstyle="@style/text_style1"android:text="包青天" /><TextViewstyle="@style/text_style1"android:text="流式布局" /><TextViewstyle="@style/text_style1"android:text="自定义ViewGroup" /><TextViewstyle="@style/text_style2"android:text="包青天" /><TextViewstyle="@style/text_style2"android:text="奋发图强" /><TextViewstyle="@style/text_style2"android:text="baiqiantao@sina.com" /><TextViewstyle="@style/text_style3"android:text="努力工作" /><TextViewstyle="@style/text_style3"android:text="月薪15K起" /><TextViewstyle="@style/text_style3"android:text="移动开发" /><TextViewstyle="@style/text_style3"android:text="android开发" /></com.bqt.myview.FlowLayout></LinearLayout>
分析
简单的分析
1、对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用MarginLayoutParams,我们需要重写ViewGroup 的几个相应的方法。
2、onMeasure中计算所有child的宽高,然后根据child的宽高,计算自己的宽和高。当然,如果不是wrap_content,直接使用父ViewGroup传入的计算值即可。3、onLayout中对所有的childView进行布局。onMeasure方法
- 首先得到其父容器传入的测量模式和宽高的计算值,然后遍历所有的childView,使用measureChild方法对所有的childView进行测量。
- 然后根据所有childView的测量得出的宽和高,得到该ViewGroup设置为wrap_content时的宽和高。
- 最后根据模式,如果是MeasureSpec.EXACTLY则直接使用父ViewGroup传入的宽和高,否则设置为自己计算的宽和高。
onLayout方法
- onLayout中完成对所有childView的位置以及大小的指定
- 遍历所有的childView,用于设置allViews的值,以及mLineHeight的值。
- 根据allViews的长度,遍历所有的行
- 遍历每一行的中所有的child,对child的left , top , right , bottom 进行计算和定位。
- 重置left和top,准备计算下一行的childView的位置。
View
public class FlowLayout extends ViewGroup {/**存储所有的View,按行记录*/private List<List<View>> mAllViews = new ArrayList<List<View>>();/**记录每一行的最大高度*/private List<Integer> mLineHeight = new ArrayList<Integer>();/**布局的宽高*/private int width = 0, height = 0;/**每一行的宽度,width不断取其中最大的宽度*/private int lineWidth = 0;/**每一行的高度,累加至height*/private int lineHeight = 0;/**布局中的子控件*/private View child;/**布局中子控件设置的LayoutParam*/private MarginLayoutParams layoutParams;public FlowLayout(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected LayoutParams generateLayoutParams(LayoutParams p) {return new MarginLayoutParams(p);}@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected LayoutParams generateDefaultLayoutParams() {return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//根据所有子控件设置自己的宽和高for (int i = 0; i < getChildCount(); i++) {child = getChildAt(i);// 让系统去测量当前child的宽高measureChild(child, widthMeasureSpec, heightMeasureSpec);// 获取当前child实际占据的宽高layoutParams = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;int childHeight = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;//如果加入当前child后超出最大允许宽度,则将目前最大宽度给width,累加height,然后开启新行if (lineWidth + childWidth > MeasureSpec.getSize(widthMeasureSpec)) {width = Math.max(lineWidth, childWidth);// 对比得到最大宽度// 开启新行,将当前行的宽高设为当前child的宽高lineWidth = childWidth;lineHeight = childHeight;// 累加行高height += lineHeight;} else {// 否则(不换行)累加行宽,lineHeight取最大高度lineWidth += childWidth;lineHeight = Math.max(lineHeight, childHeight);}// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较,并累加行高if (i == getChildCount() - 1) {width = Math.max(width, lineWidth);height += lineHeight;}}//如果是布局中设置的是wrap_content,设置为我们计算的值;否则,直接设置为父容器测量的值。width = (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) ? MeasureSpec.getSize(widthMeasureSpec) : width;height = (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) ? MeasureSpec.getSize(heightMeasureSpec) : height;setMeasuredDimension(width, height);}@SuppressLint("DrawAllocation")@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mAllViews.clear();mLineHeight.clear();lineWidth = 0;lineHeight = 0;// 存储每一行所有的childViewList<View> lineViews = new ArrayList<View>();for (int i = 0; i < getChildCount(); i++) {child = getChildAt(i);layoutParams = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth();int childHeight = child.getMeasuredHeight();// 如果需要换行if (childWidth + layoutParams.leftMargin + layoutParams.rightMargin + lineWidth > width) {// 记录这一行所有的View中的最大高度mLineHeight.add(lineHeight);// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childViewmAllViews.add(lineViews);lineWidth = 0;// 重置行宽lineViews = new ArrayList<View>();}//如果不需要换行,则累加lineWidth += childWidth + layoutParams.leftMargin + layoutParams.rightMargin;lineHeight = Math.max(lineHeight, childHeight + layoutParams.topMargin + layoutParams.bottomMargin);lineViews.add(child);}// 记录最后一行mLineHeight.add(lineHeight);mAllViews.add(lineViews);//记录当前child相对前一个child的坐标位置int left = 0;int top = 0;// 一行一行的遍历for (int i = 0; i < mAllViews.size(); i++) {// 遍历每一行lineViews = mAllViews.get(i);for (int j = 0; j < lineViews.size(); j++) {child = lineViews.get(j);if (child.getVisibility() == View.GONE) continue;layoutParams = (MarginLayoutParams) child.getLayoutParams();//计算child的坐标int leftPosition = left + layoutParams.leftMargin;int topPosition = top + layoutParams.topMargin;int rightPosition = leftPosition + child.getMeasuredWidth();int bottomPosition = topPosition + child.getMeasuredHeight();//对child进行布局child.layout(leftPosition, topPosition, rightPosition, bottomPosition);//相对位置右移left = rightPosition + layoutParams.rightMargin;}//相对位置从左侧重头开始,并下移left = 0;top += mLineHeight.get(i);}}}
样式、背景
样式<style name="text_style1"><item name="android:layout_width">wrap_content</item><item name="android:layout_height">wrap_content</item><item name="android:layout_margin">2dp</item><item name="android:background">@drawable/flowlayout_shape1</item><item name="android:textColor">#ffffff</item><item name="android:paddingLeft">5dp</item><item name="android:paddingRight">5dp</item><item name="android:textSize">11sp</item></style><style name="text_style2"><item name="android:layout_width">wrap_content</item><item name="android:layout_height">wrap_content</item><item name="android:layout_margin">2dp</item><item name="android:background">@drawable/flowlayout_shape2</item><item name="android:textColor">#880</item><item name="android:textSize">11sp</item></style><style name="text_style3"><item name="android:layout_width">wrap_content</item><item name="android:layout_height">wrap_content</item><item name="android:layout_margin">2dp</item><item name="android:background">@drawable/flowlayout_shape3</item><item name="android:textColor">#43BBE7</item><item name="android:textSize">11sp</item></style>背景图<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" ><solid android:color="#7690A5" /><corners android:radius="5dp" /><paddingandroid:bottom="2dp"android:left="2dp"android:right="2dp"android:top="2dp" /></shape><?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" ><solid android:color="#FFFFFF" /><corners android:radius="40dp"/><stroke android:color="#ff0000" android:width="2dp"/><paddingandroid:bottom="2dp"android:left="10dp"android:right="10dp"android:top="2dp" /></shape><?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" ><solid android:color="#C1FFC1" /><strokeandroid:width="1dp"android:color="#912CEE" /><corners android:radius="40dp" /><paddingandroid:bottom="2dp"android:left="10dp"android:right="10dp"android:top="2dp" /></shape>
自定义ViewGroup 流式布局的更多相关文章
- Android自定义之流式布局
流式布局,好处就是父类布局可以自动的判断子孩子是不是需要换行,什么时候需要换行,可以做到网页版的标签的效果.今天就是简单的做了自定义的流式布局. 具体效果: 原理: 其实很简单,Measure La ...
- 28 自定义View流式布局
流式布局每行的行高以本行中最高的元素作为高,如果一个元素放不下到一行时直接到第二行 FlowLayoutView package com.qf.sxy.customview05.widget; imp ...
- Android控件进阶-自定义流式布局和热门标签控件
技术:Android+java 概述 在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧,类 ...
- 自定义流式布局:ViewGroup的测量与布局
目录 1.View生命周期以及View层级 1.1.View生命周期 1.2.View层级 2.View测量与MeasureSpec类 2.1.MeasureSpec类 2.2.父View的限制 :测 ...
- 自定义View(三)--实现一个简单地流式布局
Android中的流式布局也就是常说的瀑布流很是常见,不仅在很多项目中都能见到,而且面试中也有很多面试官问道,那么什么是流式布局呢?简单来说就是如果当前行的剩余宽度不足以摆放下一个控件的时候,则自动将 ...
- Android 自定义View修炼-Android中常见的热门标签的流式布局的实现
一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...
- 【Android - 自定义View】之自定义可滚动的流式布局
首先来介绍一下这个自定义View: (1)这个自定义View的名称叫做 FlowLayout ,继承自ViewGroup类: (2)在这个自定义View中,用户可以放入所有继承自View类的视图,这个 ...
- webapp,liveapp: 流式布局和rem布局
liveapp场景应用,一般针对的是移动端,近来也是很火,颇有一些感受,拿来分享一下. 页面宽度范围: 一般移动端页面我们的像素范围是320px-640px,最大640px,最小320px,所以设计稿 ...
- Android流式布局实现
查看我的所有开源项目[开源实验室] 欢迎增加我的QQ群:[201055521],本博客client下载[请点击] 摘要 新项目用到了一种全新布局----Android标签流式布局的功能,正好一直说给大 ...
随机推荐
- HP SimpleXML
PHP SimpleXML PHP SimpleXML 处理最普通的 XML 任务,其余的任务则交由其它扩展处理. 什么是 PHP SimpleXML? SimpleXML 是 PHP 5 中的新特性 ...
- mysql 数据库字符串替换
UPDATE `table_name` SET `field_name` = replace (`field_name`,'from_str','to_str') WHERE `field_name` ...
- 武汉科技大学ACM:1004: 零起点学算法36——3n+1问题
Problem Description 任给一个正整数n,如果n为偶数,就将它变为n/2,如果为奇数,则将它乘3加1(即3n+1).不断重复这样的运算,经过有限步后,一定可以得到1 . Input 输 ...
- 武汉科技大学ACM :1001: 华科版C语言程序设计教程(第二版)课后习题3.12
Problem Description 输入n,输出对应的边长为n的空心正六边形. 为方便看图,样例中点 '.' 表示空格,打印图形时请打印空格而非小圆点. Input 边长n.(n<=20) ...
- 删除所有ecshop版权和logo
前面我们已经讲过如何删除ecshop的版权,但是还有很多人不会,今天就详细的讲下如何删除所有ecshop版权和logo 前台部分: 1:去掉头部TITLE部分的ECSHOP演示站 Powered by ...
- Vim模式
Vim是从vi发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.和Emacs并列成为类Unix系统用户最喜欢的编辑器. Vim的第一个版本由布莱姆 ...
- 解决animate动画连续播放bug
在animate动画中,如果几个div之间频繁切换,会导致鼠标移开后,动画仍在继续,解决方法有两个 一个,判断当前是否在运行动画: if(!$(".block").is(" ...
- web前端知识
4.表格与表单 4.1 动态添加行 <script language=”javascript”> window.onload=function(){ var oTr = document. ...
- Centos7 wget和普通下载有区别
今天下的禅道 wget和用win下载之后再ssh传过去,效果不一样 wget不能正常启动禅道.回来要探讨一下wget的不同之处,先记下来
- 更换centos源
我的虚拟机中在安装GoAccess的时候,说找不到GeoIP...尴尬. 这里暂时不说GeoIP的事情,我想更新一下我的yum源,因为我系统(Centos 6.5)使用的是默认的源.速度比较慢,这里先 ...
