Android中怎么自定义View实现标签流效果
发表于:2025-11-13 作者:千家信息网编辑
千家信息网最后更新 2025年11月13日,本篇内容主要讲解"Android中怎么自定义View实现标签流效果",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Android中怎么自定义View实现标签
千家信息网最后更新 2025年11月13日Android中怎么自定义View实现标签流效果
本篇内容主要讲解"Android中怎么自定义View实现标签流效果",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Android中怎么自定义View实现标签流效果"吧!
一、概述
Android自定义View实现标签流效果,一行放不下时会自动换行,用户可以自己定义单个标签的样式,可以选中和取消,可以监听单个标签的点击事件,功能还算强大,可以满足大部分开发需求,值得推荐,效果图如下:

二、实现代码
1.自定义View
定义属性文件
FlowTagConfig.java
package com.czhappy.effectdemo.flowtag;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import com.czhappy.effectdemo.R;/** * Description: * User: chenzheng * Date: 2017/2/17 0017 * Time: 10:23 */public class FlowTagConfig { private static final int DEFAULT_LINE_SPACING = 5;//默认行间距 private static final int DEFAULT_TAG_SPACING = 10;//各个标签之间的默认距离 private static final int DEFAULT_FIXED_COLUMN_SIZE = 3; //默认列数 private int lineSpacing; private int tagSpacing; private int columnSize; private boolean isFixed; public FlowTagConfig(Context context,AttributeSet attrs){ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowTagView); try { lineSpacing = a.getDimensionPixelSize(R.styleable.FlowTagView_lineSpacing, DEFAULT_LINE_SPACING); tagSpacing = a.getDimensionPixelSize(R.styleable.FlowTagView_tagSpacing, DEFAULT_TAG_SPACING); columnSize = a.getInteger(R.styleable.FlowTagView_columnSize, DEFAULT_FIXED_COLUMN_SIZE); isFixed = a.getBoolean(R.styleable.FlowTagView_isFixed,false); } finally { a.recycle(); } } public int getLineSpacing() { return lineSpacing; } public void setLineSpacing(int lineSpacing) { this.lineSpacing = lineSpacing; } public int getTagSpacing() { return tagSpacing; } public void setTagSpacing(int tagSpacing) { this.tagSpacing = tagSpacing; } public int getColumnSize() { return columnSize; } public void setColumnSize(int columnSize) { this.columnSize = columnSize; } public boolean isFixed() { return isFixed; } public void setIsFixed(boolean isFixed) { this.isFixed = isFixed; }}FlowTagView.java
package com.czhappy.effectdemo.flowtag;import android.content.Context;import android.database.DataSetObserver;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;/** * Description: * User: chenzheng * Date: 2017/2/17 0017 * Time: 10:23 */public class FlowTagView extends ViewGroup { private int mLineSpacing;//行间距 private int mTagSpacing;//各个标签之间的距离 private BaseAdapter mAdapter; private TagItemClickListener mListener; private DataChangeObserver mObserver; public FlowTagView(Context context) { super(context); init(context, null, 0); } public FlowTagView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } public FlowTagView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs, defStyle); } private void init(Context context, AttributeSet attrs, int defStyle) { //获取属性 FlowTagConfig config = new FlowTagConfig(context, attrs); mLineSpacing = config.getLineSpacing(); mTagSpacing = config.getTagSpacing(); } private void drawLayout() { if (mAdapter == null || mAdapter.getCount() == 0) { return; } this.removeAllViews(); for (int i = 0; i < mAdapter.getCount(); i++) { View view = mAdapter.getView(i,null,null); final int position = i; view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mListener != null) { mListener.itemClick(position); } } }); this.addView(view); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int wantHeight = 0; int wantWidth = resolveSize(0, widthMeasureSpec); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); int childLeft = paddingLeft; int childTop = paddingTop; int lineHeight = 0; //固定列的数量所需要的代码 for (int i = 0; i < getChildCount(); i++) { final View childView = getChildAt(i); LayoutParams params = childView.getLayoutParams(); childView.measure( getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, params.width), getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, params.height) ); //获取单个tag的宽高 int childHeight = childView.getMeasuredHeight(); int childWidth = childView.getMeasuredWidth(); lineHeight = Math.max(childHeight, lineHeight); //超过长度的新起一行 if (childLeft + childWidth + paddingRight > wantWidth) { childLeft = paddingLeft; childTop += mLineSpacing + childHeight; lineHeight = childHeight; } childLeft += childWidth + mTagSpacing; } wantHeight += childTop + lineHeight + paddingBottom; setMeasuredDimension(wantWidth, resolveSize(wantHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //固定列的数量所需要的代码 int width = r - l; int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int childLeft = paddingLeft; int childTop = paddingTop; int lineHeight = 0; for (int i = 0; i < getChildCount(); i++) { final View childView = getChildAt(i); if (childView.getVisibility() == View.GONE) { continue; } int childWidth = childView.getMeasuredWidth(); int childHeight = childView.getMeasuredHeight(); lineHeight = Math.max(childHeight, lineHeight); if (childLeft + childWidth + paddingRight > width) { childLeft = paddingLeft; childTop += mLineSpacing + lineHeight; lineHeight = childHeight; } childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); childLeft += childWidth + mTagSpacing; } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(this.getContext(), attrs); } public void setAdapter(BaseAdapter adapter){ if (mAdapter == null){ mAdapter = adapter; if (mObserver == null){ mObserver = new DataChangeObserver(); mAdapter.registerDataSetObserver(mObserver); } drawLayout(); } } public void setItemClickListener(TagItemClickListener mListener) { this.mListener = mListener; } /** * 单击监听接口 */ public interface TagItemClickListener { void itemClick(int position); } class DataChangeObserver extends DataSetObserver { @Override public void onChanged() { FlowTagView.this.drawLayout(); } @Override public void onInvalidated() { super.onInvalidated(); } }}2.测试类
FlowTagActivity.java
package com.czhappy.effectdemo.activity;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import com.czhappy.effectdemo.R;import com.czhappy.effectdemo.adapter.EvaluateAdapter;import com.czhappy.effectdemo.flowtag.FlowTagView;import com.czhappy.effectdemo.model.Evaluate;import java.util.ArrayList;import java.util.List;/** * Description: * User: chenzheng * Date: 2017/2/17 0017 * Time: 11:47 */public class FlowTagActivity extends AppCompatActivity { private FlowTagView mContainer; private EvaluateAdapter adapter; private List chooseList = new ArrayList(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_flowtag); initView(); initData(); } private void initData() { List list = new ArrayList(); Evaluate e1 = new Evaluate("热情", "1"); Evaluate e2 = new Evaluate("服务周到", "2"); Evaluate e3 = new Evaluate("一般", "3"); Evaluate e4 = new Evaluate("技术活杠杠的", "4"); Evaluate e5 = new Evaluate("专业精通", "5"); Evaluate e6 = new Evaluate("只会吹牛逼", "6"); Evaluate e7 = new Evaluate("地下第一仅此一家", "7"); list.add(e1); list.add(e2); list.add(e3); list.add(e4); list.add(e5); list.add(e6); list.add(e7); adapter.setItems(list); } private void initView() { mContainer = (FlowTagView) this.findViewById(R.id.container); adapter = new EvaluateAdapter(this); mContainer.setAdapter(adapter); mContainer.setItemClickListener(new FlowTagView.TagItemClickListener() { @Override public void itemClick(int position) { Evaluate e = (Evaluate) adapter.getItem(position); e.is_choosed = !e.is_choosed; if(e.is_choosed){ chooseList.add(e); }else{ chooseList.remove(e); } adapter.notifyDataSetChanged(); } }); }} EvaluateAdapter.java
package com.czhappy.effectdemo.adapter;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import com.czhappy.effectdemo.R;import com.czhappy.effectdemo.model.Evaluate;import java.util.ArrayList;import java.util.List;/** * Description: * User: chenzheng * Date: 2017/2/17 0017 * Time: 11:43 */public class EvaluateAdapter extends BaseAdapter { private Context context; private LayoutInflater mInflater; private List list; public EvaluateAdapter(Context context) { this.context = context; this.mInflater = LayoutInflater.from(context); this.list = new ArrayList(); } public List getList(){ return list; } public void setItems(List list){ this.list = list; notifyDataSetChanged(); } @Override public int getCount() { return list == null ? 0 : list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate( R.layout.evaluate_grid_item, null); holder.evaluate_tv = (TextView)convertView.findViewById(R.id.evaluate_tv); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final Evaluate ee = (Evaluate) getItem(position); holder.evaluate_tv.setText(ee.getName()); if(ee.is_choosed){ holder.evaluate_tv.setBackgroundResource(R.drawable.bg_round_corner_line_orange); }else{ holder.evaluate_tv.setBackgroundResource(R.drawable.bg_round_corner_line_gray); } return convertView; } private final class ViewHolder { private TextView evaluate_tv; }} 布局文件
bg_round_corner_line_orange.xml
bg_round_corner_line_gray.xml
到此,相信大家对"Android中怎么自定义View实现标签流效果"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
标签
效果
代码
之间
内容
单个
属性
数量
文件
行间
学习
监听
实用
强大
更深
周到
热情
一家
一行
专业
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
谁在旅行网络安全监督管理职责中
网络安全法的力道指挥思想是
ibmda400数据库
金融行业数据库面试
保护支付宝网络安全的是谁
网络不行显示dns服务器不可用
市面主流云服务器
金山区品牌软件开发优势
考研数据库复试
ug数据库结构
猎杀对决亚服连接服务器失败
计算机网络技术有没有必要升本
cod17失去与暴雪游戏服务器
食安网络安全知识
南京秦淮路王希贤软件开发
赛季服务器名称
居家户外网络安全作文
清远小程序软件开发
hpe服务器ilo
大专生毕业后学软件开发
普陀区咨询软件开发销售
数据库用什么做搜索引擎
四川中州量子软件开发
怎样安装网络安全证书
苹果在四川的服务器还有吗
国产软件开发平台招标文件约束
常用的五种安全网络技术
服务器和普通pc的区别
网络安全活动感言
厦门电路软件开发