網站首頁 編程語言 正文
本文實例為大家分享了Android自定義View實現標簽流效果的具體代碼,供大家參考,具體內容如下
一、概述
Android自定義View實現標簽流效果,一行放不下時會自動換行,用戶可以自己定義單個標簽的樣式,可以選中和取消,可以監聽單個標簽的點擊事件,功能還算強大,可以滿足大部分開發需求,值得推薦,效果圖如下:
二、實現代碼
1.自定義View
定義屬性文件
<declare-styleable name="FlowTagView"> ? ? ? ? <attr name="lineSpacing" format="dimension" /> ? ? ? ? <attr name="tagSpacing" format="dimension" /> ? ? ? ? <!-- 是否是固定布局 --> ? ? ? ? <attr name="isFixed" format="boolean" /> ? ? ? ? <attr name="columnSize" format="integer" /> </declare-styleable>
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<Evaluate> chooseList = new ArrayList<Evaluate>(); ? ? @Override ? ? protected void onCreate(@Nullable Bundle savedInstanceState) { ? ? ? ? super.onCreate(savedInstanceState); ? ? ? ? setContentView(R.layout.activity_flowtag); ? ? ? ? initView(); ? ? ? ? initData(); ? ? } ? ? private void initData() { ? ? ? ? List<Evaluate> 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<Evaluate> list; ? ? public EvaluateAdapter(Context context) { ? ? ? ? this.context = context; ? ? ? ? this.mInflater = LayoutInflater.from(context); ? ? ? ? this.list = ?new ArrayList<Evaluate>(); ? ? } ? ? public List<Evaluate> getList(){ ? ? ? ? return list; ? ? } ? ? public void setItems(List<Evaluate> 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; ? ? } }
布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ? ? xmlns:app="http://schemas.android.com/apk/res-auto" ? ? android:layout_width="match_parent" ? ? android:layout_height="match_parent" ? ? android:orientation="vertical"> ? ? <com.czhappy.effectdemo.flowtag.FlowTagView ? ? ? ? android:id="@+id/container" ? ? ? ? android:layout_width="match_parent" ? ? ? ? android:layout_height="wrap_content" ? ? ? ? android:padding="10dp" ? ? ? ? app:tagSpacing="10dp" ? ? ? ? app:lineSpacing="10dp"/> </LinearLayout>
bg_round_corner_line_orange.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > ? ? <solid android:color="#ffffff" /> ? ? <corners android:radius="5dp" /> ? ? <stroke android:width="0.5dp" ? ? ? ? android:color="#FF6700"/> </shape>
bg_round_corner_line_gray.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > ? ? <solid android:color="#ffffff" /> ? ? <corners android:radius="5dp" /> ? ? <stroke android:width="0.5dp" ? ? ? ? android:color="#cccccc"/> </shape>
原文鏈接:https://blog.csdn.net/chenzheng8975/article/details/55511498
相關推薦
- 2022-04-25 老生常談C語言中指針的使用_C 語言
- 2022-07-30 Linux的磁盤配額設置
- 2022-08-18 C#開發Windows?UWP系列之對話框MessageDialog和ContentDialog_C
- 2022-06-24 Windows?Server?2012遠程默認端口3389的修改方法_win服務器
- 2022-09-08 Python如何將list中的string轉換為int_python
- 2022-09-24 C#中的引用類型以及特殊引用類型詳解_C#教程
- 2023-10-31 springboot 線程池參數解釋
- 2022-08-22 Python利用watchdog模塊監控文件變化_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支