一、概述:
在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何

自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈)

类似的自定义布局。下面我们就来详细介绍流式布局的应用特点以及用的的技术点:

1.流式布局的特点以及应用场景
    特点:当上面一行的空间不够容纳新的TextView时候,
    才开辟下一行的空间

  原理图:

  

场景:主要用于关键词搜索或者热门标签等场景
2.自定义ViewGroup,重点重写下面两个方法

1、onMeasure:测量子view的宽高,设置自己的宽和高

2、onLayout:设置子view的位置

onMeasure:根据子view的布局文件中属性,来为子view设置测量模式和测量值
    测量=测量模式+测量值;

测量模式有3种:
    EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
    AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
    UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
3.LayoutParams
    ViewGroup LayoutParams :每个 ViewGroup 对应一个 LayoutParams; 即 ViewGroup -> LayoutParams
    getLayoutParams 不知道转为哪个对应的LayoutParams ,其实很简单,就是如下:
    子View.getLayoutParams 得到的LayoutParams对应的就是 子View所在的父控件的LayoutParams;
    例如,LinearLayout 里面的子view.getLayoutParams ->LinearLayout.LayoutParams
    所以 咱们的FlowLayout 也需要一个LayoutParams,由于上面的效果图是子View的 margin,
    所以应该使用MarginLayoutParams。即FlowLayout->MarginLayoutParams

4.最后来看看实现的最终效果图:

二、热门标签的流式布局的实现:

1. 自定义热门标签的ViewGroup实现

  根据上面的技术分析,自定义类继承于ViewGroup,并重写 onMeasure和onLayout等方法。具体实现代码如下:

package com.czm.flowlayout;

import java.util.ArrayList;
import java.util.List; import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
*
* @author caizhiming
* @created on 2015-4-13
*/
public class XCFlowLayout extends ViewGroup{ //存储所有子View
private List<List<View>> mAllChildViews = new ArrayList<>();
//每一行的高度
private List<Integer> mLineHeight = new ArrayList<>(); public XCFlowLayout(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
public XCFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public XCFlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub //父控件传进来的宽度和高度以及对应的测量模式
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec); //如果当前ViewGroup的宽高为wrap_content的情况
int width = 0;//自己测量的 宽度
int height = 0;//自己测量的高度
//记录每一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0; //获取子view的个数
int childCount = getChildCount();
for(int i = 0;i < childCount; i ++){
View child = getChildAt(i);
//测量子View的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//得到LayoutParams
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//子View占据的宽度
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
//子View占据的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
//换行时候
if(lineWidth + childWidth > sizeWidth){
//对比得到最大的宽度
width = Math.max(width, lineWidth);
//重置lineWidth
lineWidth = childWidth;
//记录行高
height += lineHeight;
lineHeight = childHeight;
}else{//不换行情况
//叠加行宽
lineWidth += childWidth;
//得到最大行高
lineHeight = Math.max(lineHeight, childHeight);
}
//处理最后一个子View的情况
if(i == childCount -1){
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
//wrap_content
setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width,
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height); } @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
mAllChildViews.clear();
mLineHeight.clear();
//获取当前ViewGroup的宽度
int width = getWidth(); int lineWidth = 0;
int lineHeight = 0;
//记录当前行的view
List<View> lineViews = new ArrayList<View>();
int childCount = getChildCount();
for(int i = 0;i < childCount; i ++){
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight(); //如果需要换行
if(childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width){
//记录LineHeight
mLineHeight.add(lineHeight);
//记录当前行的Views
mAllChildViews.add(lineViews);
//重置行的宽高
lineWidth = 0;
lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
//重置view的集合
lineViews = new ArrayList();
}
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin);
lineViews.add(child);
}
//处理最后一行
mLineHeight.add(lineHeight);
mAllChildViews.add(lineViews); //设置子View的位置
int left = 0;
int top = 0;
//获取行数
int lineCount = mAllChildViews.size();
for(int i = 0; i < lineCount; i ++){
//当前行的views和高度
lineViews = mAllChildViews.get(i);
lineHeight = mLineHeight.get(i);
for(int j = 0; j < lineViews.size(); j ++){
View child = lineViews.get(j);
//判断是否显示
if(child.getVisibility() == View.GONE){
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int cLeft = left + lp.leftMargin;
int cTop = top + lp.topMargin;
int cRight = cLeft + child.getMeasuredWidth();
int cBottom = cTop + child.getMeasuredHeight();
//进行子View进行布局
child.layout(cLeft, cTop, cRight, cBottom);
left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
left = 0;
top += lineHeight;
} }
/**
* 与当前ViewGroup对应的LayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
// TODO Auto-generated method stub return new MarginLayoutParams(getContext(), attrs);
}
}

2.相关的布局文件:

引用自定义控件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.czm.flowlayout.XCFlowLayout
android:id="@+id/flowlayout"
android:layout_width="match_parent"
android:layout_height="match_parent" > </com.czm.flowlayout.XCFlowLayout> </RelativeLayout>

TextView的样式文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#666666" />
<corners android:radius="10dp" />
<padding
android:left="5dp"
android:right="5dp"
android:top="5dp"
android:bottom="5dp"
/> </shape>

三、使用该自定义布局控件类

最后,如何使用该自定义的热门标签控件类呢?很简单,请看下面实例代码:

package com.czm.flowlayout;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.TextView;
/**
*
* @author caizhiming
* @created on 2015-4-13
*/
public class MainActivity extends Activity { private String mNames[] = {
"welcome","android","TextView",
"apple","jamy","kobe bryant",
"jordan","layout","viewgroup",
"margin","padding","text",
"name","type","search","logcat"
};
private XCFlowLayout mFlowLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); initChildViews(); }
private void initChildViews() {
// TODO Auto-generated method stub
mFlowLayout = (XCFlowLayout) findViewById(R.id.flowlayout);
MarginLayoutParams lp = new MarginLayoutParams(
LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
lp.leftMargin = 5;
lp.rightMargin = 5;
lp.topMargin = 5;
lp.bottomMargin = 5;
for(int i = 0; i < mNames.length; i ++){
TextView view = new TextView(this);
view.setText(mNames[i]);
view.setTextColor(Color.WHITE);
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.textview_bg));
mFlowLayout.addView(view,lp);
}
} }

四、源码下载

最后给出源码的下载:http://www.demodashi.com/demo/14686.html

感谢真题园网提供支持:http://www.zhentiyuan.com

Android 自定义View修炼-Android中常见的热门标签的流式布局的实现的更多相关文章

  1. Android 自定义View修炼-Android 实现自定义的卫星式菜单(弧形菜单)View

    一.总述 Android 实现卫星式菜单也叫弧形菜单的主要要做的工作如下:1.动画的处理2.自定义ViewGroup来实现卫星式菜单View (1)自定义属性       a. 在attrs.xml中 ...

  2. Android 自定义View修炼-Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)

    一.概述 Android实现圆角矩形,圆形或者椭圆等图形,一般主要是个自定义View加上使用Xfermode实现的.实现圆角图片的方法其实不少,常见的就是利用Xfermode,Shader.本文直接继 ...

  3. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  4. Android 自定义View修炼-如何打造Android自定义的下拉列表框控件

    一.概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的那种下拉列表控件,类似下图这样的 ...

  5. Android 自定义View修炼-实现自定义圆形、圆角和椭圆ImageView(使用Xfermode图形渲染方法)

    一:简介: 在上一篇<Android实现圆形.圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)>博文中,采用BitmapShader方法实现自定义的圆形.圆角等自定 ...

  6. Android 自定义View修炼-仿360手机卫士波浪球进度的实现

    像360卫士的波浪球进度的效果,一般最常用的方法就是 画线的方式,先绘sin线或贝塞尔曲线,然后从左到右绘制竖线,然后再裁剪圆区域. 今天我这用图片bitmap的方式,大概的方法原理是: (1)首先用 ...

  7. Android 自定义View修炼-仿QQ5.0 的侧滑菜单效果的实现

    有一段时间没有写博客了,最近比较忙,没什么时间写,刚好今天有点时间, 我就分享下,侧滑菜单的实现原理,一般android侧滑的实现原理和步骤如下:(源码下载在下面最后给出哈) 1.使用ViewGrou ...

  8. Android 自定义View修炼-自定义View-带百分比进度的圆形进度条(采用自定义属性)

    很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己来自定义一个能满足我们需求的View,自定义View我们需要先继承View,添加类的构造方法,重写父类View的一些方法,例如o ...

  9. Android 自定义View修炼-自定义HorizontalScrollView视图实现仿ViewPager效果

    开发过程中,需要达到 HorizontalScrollView和ViewPager的效果,于是直接重写了HorizontalScrollView来达到实现ViewPager的效果. 实际效果图如下: ...

随机推荐

  1. matlab mex入门简介

    mex 的目的 通过C/C++语言编写代码,在Matlab中将其编译成mex文件主要可以做以下几方面的事情: 1.加快程序的执行速度. Matlab在for上如老牛拉车的速度确实让人抓狂. 2.将Ma ...

  2. 用slf4j+logback实现多功能日志解决方案 --- 转

    大家都知道,slf4j是原来log4j的作者写的一个新的日志组件,意思是简单日志门面接口,可以跟其他日志组件配合使用,常用的配合是slf4j+logback,无论从功能上还是从性能上都较之log4j有 ...

  3. delphi 中使用WaitForMultipleObjects等待线程执行,再执行后续代码

    unit1 [delphi] view plain copyunit Unit1; interface uses Windows, Messages, SysUtils, Variants, Clas ...

  4. POJ 1151 Atlantis 线段树+离散化+扫描线

    这次是求矩形面积并 /* Problem: 1151 User: 96655 Memory: 716K Time: 0MS Language: G++ Result: Accepted */ #inc ...

  5. 工具http://www.architexa.com/learn-more/install使用

    准备写篇博客,用来研究开源软件的利器.

  6. oracle spfile和pfile文件

    pfile(Parameter File)从oracle8i开始使用,在oracle9i中也可以用.它以文本文件的形式存在,可以用vi等编辑器对 其中数据库参数进行修改.文件格式为initSID.or ...

  7. 【暑假】[深入动态规划]UVa 1380 A Scheduling Problem

     UVa 1380 A Scheduling Problem 题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=41557 ...

  8. 使用weka进行Cross-validation实验

    Generating cross-validation folds (Java approach) 文献: http://weka.wikispaces.com/Generating+cross-va ...

  9. QQMusic绿钻兄,你可安好?我需要晴天。

    不好意思,年纪这样大了,还依靠吐槽来保持呆毛的正能量,实在对不住,先说对不起. QQMusic是我最喜欢的腾讯增值服务,正版内容,海量歌手,高清下载.实在是音乐软件中高大上的典范,除了歌手排名中前十中 ...

  10. yum cdh4

    cdh4 install for Centos6那个最美的年代,最好的时光,一路梦想,一路流泪,流的不会是懦弱的泪,而是对奋斗的寄于;1,repo配置>>>/etc/yum.repo ...