- 浏览: 70500 次
- 性别:
- 来自: 北京
最新评论
转载请注明:http://renyuan-1991.iteye.com/blog/2232463
具体实现步骤:
1.继承ViewGroup,实现三个构造方法
2.通过generateLayoutParams给自定义的控件指定参数
3.实现onMeasure方法
a.在这个方法里面首先要做是要知道自己的大小,onMeasure方法会通过父类获取具体的模式和大小。通过getMode方法获得模式(三种模式就不详细说了),然后通过getSize方法获取具体的尺寸。
b.通过遍历子控件得到ViewGroup显示时的高度。当子控件(即标签)的宽度之和大于父控件的时候开启行并累加高度
c.setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
: width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
: height); 方法确定我们ViewGroup最终的大小
4.重写onLayout方法
a.同样的需要遍历子控件,根据宽度控制是否换行。并将每一行的空间用一个列表记录下来,并记录高度从而决定下一行需要显示的位置。
b.上面已经将标签以行为单位分别放到适当的list里面。这里就是将list里面的控件显示出来。说到底其实就是一个道理:不管通过什么样的算法,只要知道子控件正确的显示位置即可。
c.确定完位置后调用child.layout(lc, tc, rc, bc); 方法将它画出来即可。需要注意的地方是最后一行我们需要特殊处理。通过上面的判断我们实际的最后一行是永远不会大于ViewGroup的宽度的,而这一行的高度同样需要记录下来
完整的代码如下:
在界面中调用的方法
下载连接:http://download.csdn.net/detail/u010419467/8956027
希望爱好编程的小伙伴能加这个群,互相帮助,共同学习。群号: 141877583
最后附上完整项目(不需要积分)(ps:项目中的类请以博客中为准)
具体实现步骤:
1.继承ViewGroup,实现三个构造方法
2.通过generateLayoutParams给自定义的控件指定参数
3.实现onMeasure方法
a.在这个方法里面首先要做是要知道自己的大小,onMeasure方法会通过父类获取具体的模式和大小。通过getMode方法获得模式(三种模式就不详细说了),然后通过getSize方法获取具体的尺寸。
b.通过遍历子控件得到ViewGroup显示时的高度。当子控件(即标签)的宽度之和大于父控件的时候开启行并累加高度
c.setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
: width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
: height); 方法确定我们ViewGroup最终的大小
4.重写onLayout方法
a.同样的需要遍历子控件,根据宽度控制是否换行。并将每一行的空间用一个列表记录下来,并记录高度从而决定下一行需要显示的位置。
b.上面已经将标签以行为单位分别放到适当的list里面。这里就是将list里面的控件显示出来。说到底其实就是一个道理:不管通过什么样的算法,只要知道子控件正确的显示位置即可。
c.确定完位置后调用child.layout(lc, tc, rc, bc); 方法将它画出来即可。需要注意的地方是最后一行我们需要特殊处理。通过上面的判断我们实际的最后一行是永远不会大于ViewGroup的宽度的,而这一行的高度同样需要记录下来
完整的代码如下:
package com.xiaoying.widget; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.xiaoying.common.util.Utils; import com.xiaoying.cuishou.R; /** * @author Roy * @version V1.0 date:2017/11/30 下午6:07 */ public class MyFlowLayout extends ViewGroup { private MarkClickListener markClickListener; public MyFlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyFlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public MyFlowLayout(Context context) { super(context); } public void setData(String[] data){ createChild(data,10, 15, 10, 15, 10, 0, 12, 12 , 0); } public void setData(String[] data, int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){ createChild(data,textSize, pl, pt, pr, pb, ml, mt, mr, mb); } public void setData(List<String> data){ String[] mydata = null; if(data!=null){ int length = data.size(); mydata = new String[length]; for(int i = 0 ; i<length;i++){ mydata[i] = data.get(i); } } setData(mydata); } public void setData(List<String> data,int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){ String[] mydata = null; if(data!=null){ int length = data.size(); mydata = new String[length]; for(int i = 0 ; i<length;i++){ mydata[i] = data.get(i); } } setData(mydata, textSize,pl, pt, pr, pb, ml, mt, mr, mb); } public void setOnClickListener(MarkClickListener markClickListener){ this.markClickListener = markClickListener; } public interface MarkClickListener{ void onRemarkClick(String str); } private void createChild(String[] data,int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){ int size = data.length; for(int i = 0;i<size;i++){ String text = data[i]; TextView btn = new TextView(getContext()); btn.setClickable(true); btn.setGravity(Gravity.CENTER); btn.setText(text); btn.setTag(text); btn.setTextSize(textSize); btn.setPadding(Utils.dip2px(getContext(), pl), Utils.dip2px(getContext(), pt), Utils.dip2px(getContext(), pr), Utils.dip2px(getContext(), pb)); btn.setTextColor(0xff2b3041); /*btn.setTextColor(getResources().getColorStateList(R.color.selector_button_tc));*/ btn.setBackgroundResource(R.drawable.mark_green); MarginLayoutParams params = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); params.setMargins(Utils.dip2px(getContext(), ml), Utils.dip2px(getContext(), mt), Utils.dip2px(getContext(), mr), Utils.dip2px(getContext(), mb)); btn.setLayoutParams(params); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { markClickListener.onRemarkClick(((TextView)v).getText().toString()); } }); this.addView(btn); } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childCount = getChildCount(); int lineWidth = 0; int lineHeight = 0; int width = 0;//warpcontet是需要记录的宽度 int height = 0; for(int i = 0 ; i< childCount;i++){ View child = getChildAt(i); // 测量每一个child的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin; int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin; if(lineWidth+childWidth>widthSize){ //这种情况就是排除单个标签很长的情况 width = Math.max(lineWidth, childWidth); //开启新行 lineWidth = childWidth; //记录总行高 height += lineHeight; //因为开了新行,所以这行的高度要记录一下 lineHeight = childHeight; }else{ lineWidth += childWidth; //记录行高 lineHeight = Math.max(lineHeight, childHeight); } // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较 if (i == childCount - 1) { //宽度 width = Math.max(width, lineWidth); height += lineHeight; } } setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height); } /** * 存储所有的View,按行记录 */ private List<List<View>> mAllViews = new ArrayList<List<View>>(); /** * 记录每一行的最大高度 */ private List<Integer> mLineHeight = new ArrayList<Integer>(); //onLayout中完成对所有childView的位置以及大小的指定 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //清空子控件列表 mAllViews.clear(); //清空高度记录列表 mLineHeight.clear(); //得到当前控件的宽度(在onmeasure方法中已经测量出来了) int width = getWidth(); int childCount = getChildCount(); // 存储每一行所有的childView List<View> lineViews = new ArrayList<View>(); //行高 int lineWidth = 0; //总行高 int lineHeight = 0; 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 + lp.leftMargin + lp.rightMargin + lineWidth > width) { mLineHeight.add(lineHeight); // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView mAllViews.add(lineViews); // 重置行宽 lineWidth = 0; lineHeight = 0; lineViews = new ArrayList<View>(); } /** * 如果不需要换行,则累加 */ lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // 记录最后一行 (因为最后一行肯定大于父布局的宽度,所以添加最后一行是必要的) mLineHeight.add(lineHeight); mAllViews.add(lineViews); int left = 0; int top = 0; int lineNums = mAllViews.size(); for(int i = 0;i<lineNums;i++){ // 每一行的所有的views lineViews = mAllViews.get(i); // 当前行的最大高度 lineHeight = mLineHeight.get(i); for(int j = 0 ;j < lineViews.size() ; j++){ View lineChild = lineViews.get(j); if(lineChild.getVisibility() == View.GONE){ continue; } MarginLayoutParams lp = (MarginLayoutParams) lineChild.getLayoutParams(); //开始画标签了。左边和上边的距离是要根据累计的数确定的。 int lc = left + lp.leftMargin; int tc = top+lp.topMargin; int rc = lc+lineChild.getMeasuredWidth(); int bc = tc+lineChild.getMeasuredHeight(); lineChild.layout(lc, tc, rc, bc); left += lineChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin; } //将left归零 left = 0; top += lineHeight; } } }
在界面中调用的方法
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyFlowLayout myview = (MyFlowLayout) findViewById(R.id.myview); String[] myData = {"one","two","dkfjdkf","kdfkdfj","jdkfdfkjdkfdkfkdkdfj","kdjkfdjkjkjskkjkd"}; myview.setData(myData, this, 15, 10, 10, 10, 10, 10, 10, 10, 10); }
下载连接:http://download.csdn.net/detail/u010419467/8956027
希望爱好编程的小伙伴能加这个群,互相帮助,共同学习。群号: 141877583
最后附上完整项目(不需要积分)(ps:项目中的类请以博客中为准)
- FlowLayout_mark.zip (1.9 MB)
- 下载次数: 6
发表评论
-
通过Url打开app页面并传递参数
2017-12-09 17:56 3978转载请注明出处:http:// ... -
Retrofit+RxJava搭建网络请求和数据解析框架
2017-06-29 18:20 0好久没写博客了,实话说,这一年相比往年可以说没什么进步,工作四 ... -
viewpager指示器
2016-11-08 16:04 0viewpager指示器 实现该需求的几种方法的基本原理和缺点 ... -
Android Studio模板,省去界面重复部分的开发
2016-07-06 16:05 0Android Studio模板,省去界面重复部分的开发 -
android studio 运行java代码
2016-06-21 17:50 2428转载请注明出处: http://renyuan-1991.it ... -
自定义组合控件的总结
2016-06-21 16:27 1554自定义组合控件的总结 转载请注明出处:http://renyu ... -
只显示年月的DatePicker
2016-06-12 17:30 3376转载请注明出处:http://renyuan-1991.ite ... -
Android的约束布局ConstaintLayout
2016-06-24 15:05 9924ConstaintLayout的初次使用总结 转载请注明出处 ... -
Android遮罩层引导页的实现
2016-06-03 16:28 0实现遮罩层引导页可以通过以下几种方式,本文主要记录张洪洋Hig ... -
手势密码
2016-05-24 14:52 0我们公司做的是理财产品,所以手势密码这个东西少不了,在写手势密 ... -
线性布局的权重weight使用详解
2016-01-20 14:29 2439对线性布局中权重的理解 转载请注明出处:http://ren ... -
setBackgroundResource导致Padding失效,settextsize
2016-01-18 19:50 1575通过setBackground设置9 patc ... -
NestedScrolling的使用及ScrollView的惯性滑动
2015-12-08 18:14 21572NestedScrolling的使用及ScrollView的惯 ... -
android-async-http使用和取消请求
2015-10-15 12:06 4996android-async-http使用总结 下载最新的包可以 ... -
触摸屏幕取消键盘
2015-10-13 18:09 1587当点击文本框和发送按钮的时候不需要取消键盘,点击屏幕其他按钮或 ... -
四种方式实现ListView中的倒计时一()
2015-09-29 12:33 0的奋斗奋斗奋斗 -
SQLite详解
2015-10-10 15:07 1105概述 SQLite是D.Richar ... -
android 的屏幕适配问题,dp与px的换算
2015-08-08 19:11 1776在进入正题之前先了解 ... -
Android第三方框架之xListView的使用方法
2014-11-09 19:18 5152==最近向用xListView实现刷新效果,在网上没有找到相关 ...
相关推荐
标题"android 自动换行的自定义viewgroup"所指的,就是创建一个能够根据子视图的数量和大小自动调整布局,实现自动换行效果的自定义 ViewGroup 类。这样的组件通常用于展示网格或者流式布局,例如商品列表、图片墙等...
在Android开发中,"标签(Tags)"通常指的是在用户界面上用于表示多个选项或类别的元素,类似于传统的HTML中...开发者可以利用现有的开源库或自定义ViewGroup来实现这一功能,以创建更加灵活、美观的Android应用程序。
在Android开发中,自定义ViewGroup是创建复杂布局或实现特定功能的重要手段。"android自定义viewgroup实现等分格子布局"这个话题涉及到如何利用自定义ViewGroup类来创建一个能够平均分配屏幕空间的网格布局。下面...
本教程将深入探讨如何通过自定义ViewGroup来实现一个流式布局(FlowLayout)的示例,这在展示多个小图标或者创建类似瀑布流的效果时非常有用。流式布局能让元素自动换行,根据屏幕尺寸动态调整布局。 首先,我们...
在这个案例中,我们将自定义一个`ViewGroup`子类,比如命名为`AutoWrapTagLayout`,来实现自动换行的功能。 实现这个功能的关键在于计算每个标签的宽度,并根据屏幕宽度动态调整它们的布局。我们需要覆写`onMeasure...
在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。本实战篇将聚焦于如何实现一个名为FlowLayout的自定义布局,它允许子视图按行排列,类似于HTML中的`<div>`标签。在Android应用设计中,...
在Android开发中,自定义ViewGroup是提升应用界面交互性和个性化设计的重要手段。本实战篇主要探讨如何实现一个名为FlowLayout的自定义布局组件,它允许子View按行排列,达到类似HTML流式布局的效果。在Android SDK...
本示例将探讨如何创建一个自定义的ViewGroup来实现商品属性显示时的自动换行效果,类似于常见于电商应用的商品详情页。这个过程涉及到布局管理、事件处理以及自定义View的基本原理。 首先,我们需要理解Android中的...
总的来说,创建一个多标签自动换行的`ViewGroup`需要理解Android布局系统的底层工作原理,以及如何通过自定义`ViewGroup`来实现特定的布局需求。通过这样的实践,开发者可以更灵活地控制界面的布局,以适应各种屏幕...
在Android开发中,自定义ViewGroup是实现特定布局需求的重要手段。本文主要讲解如何自定义一个FlowLayout,这种布局方式能够适应各种需要动态排列元素的场景,例如关键字标签和搜索热词列表。在Android SDK中并未...
本教程将探讨如何通过自定义一个`ViewGroup`来实现类似电商平台商品属性的自动换行效果。这种效果通常用于展示商品的多维度信息,如颜色、尺寸、材质等,当一行无法容纳所有信息时,它们会自动换行到下一行。 首先...
1. **创建自定义ViewGroup:** 创建一个名为AutoWrapLinearLayout的新类,继承自LinearLayout,并在构造函数中设置布局参数。 2. **重写onMeasure()方法:** 这是实现自动换行的关键。遍历所有的子视图,依次测量...
在Android开发中,自定义ViewGroup是实现特定布局需求的重要手段。本文主要讲解如何通过自定义ViewGroup实现流式布局(FlowLayout)。流式布局是一种常见的布局方式,它允许子View按照从左到右的顺序填充容器,当一...
在Android开发中,有时系统默认的布局管理器如LinearLayout、RelativeLayout等并不能满足所有需求,这时我们就需要自定义ViewGroup来实现特定的布局效果。本文将详细介绍如何实现一个自定义的FlowLayout,这是一种...
在Android开发中,自定义ViewGroup是扩展UI功能和实现个性化布局的重要手段。本文将深入讲解如何实现一个自定义的标签流式布局——FlowLayout。在Android应用开发中,经常需要处理像热门标签那样能自动换行的布局,...
可以使用`LinearLayout`配合`android:orientation="vertical"`属性垂直排列标签,然后利用`FlowLayout`或者自定义布局实现换行。在`FlowLayout`中,可以通过重写`onMeasure()`方法来检测当前行是否已满,如果满了则...
通过自定义ViewGroup,我们可以灵活地控制视图的行为,以满足特定的设计需求。在本例中,使用FlowLayout或自定义的类似布局,结合恰当的测量逻辑,就能创建一个能够自动换行和自适应高度的标签样式组件。
我们可以创建一个自定义的`ViewGroup`来实现这个功能。例如,提供的`MyViewGroup.java`可能就是一个实现了自动换行功能的自定义布局。这个类可能会重写`onMeasure()`和`onLayout()`方法,以确定每个子视图的位置,...
在Android中,原生的FlowLayout并未包含在SDK中,但开发者可以通过自定义ViewGroup来实现类似的功能。 2. **自动添加标签**: 这个功能意味着FlowLayout被扩展以支持动态添加标签视图,比如TextView,这些标签可以...
本资源提供了一个自定义ViewGroup实现的流式布局,对于开发者来说,能够更加灵活地控制界面元素的展示,尤其是在需要动态添加或排列多个元素时。 在Android系统中,基础的布局管理器如LinearLayout、RelativeLayout...