Android自定義View實(shí)現(xiàn)標(biāo)簽流效果
本文實(shí)例為大家分享了Android自定義View實(shí)現(xiàn)標(biāo)簽流效果的具體代碼,供大家參考,具體內(nèi)容如下
一、概述
Android自定義View實(shí)現(xiàn)標(biāo)簽流效果,一行放不下時(shí)會(huì)自動(dòng)換行,用戶可以自己定義單個(gè)標(biāo)簽的樣式,可以選中和取消,可以監(jiān)聽(tīng)單個(gè)標(biāo)簽的點(diǎn)擊事件,功能還算強(qiáng)大,可以滿足大部分開(kāi)發(fā)需求,值得推薦,效果圖如下:
二、實(shí)現(xiàn)代碼
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;//默認(rèn)行間距 ? ? private static final int DEFAULT_TAG_SPACING = 10;//各個(gè)標(biāo)簽之間的默認(rèn)距離 ? ? private static final int DEFAULT_FIXED_COLUMN_SIZE = 3; //默認(rèn)列數(shù) ? ? 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;//各個(gè)標(biāo)簽之間的距離 ? ? 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; ? ? ? ? //固定列的數(shù)量所需要的代碼 ? ? ? ? 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) ? ? ? ? ? ? ); ? ? ? ? ? ? //獲取單個(gè)tag的寬高 ? ? ? ? ? ? int childHeight = childView.getMeasuredHeight(); ? ? ? ? ? ? int childWidth = childView.getMeasuredWidth(); ? ? ? ? ? ? lineHeight = Math.max(childHeight, lineHeight); ? ? ? ? ? ? //超過(guò)長(zhǎng)度的新起一行 ? ? ? ? ? ? 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) { ? ? ? ? //固定列的數(shù)量所需要的代碼 ? ? ? ? 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; ? ? } ? ? /** ? ? ?* 單擊監(jiān)聽(tīng)接口 ? ? ?*/ ? ? 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.測(cè)試類
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("服務(wù)周到", "2"); ? ? ? ? Evaluate e3 = new Evaluate("一般", "3"); ? ? ? ? Evaluate e4 = new Evaluate("技術(shù)活杠杠的", "4"); ? ? ? ? Evaluate e5 = new Evaluate("專業(yè)精通", "5"); ? ? ? ? Evaluate e6 = new Evaluate("只會(huì)吹牛逼", "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>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flutter學(xué)習(xí)之構(gòu)建、布局及繪制三部曲
這篇文章主要給大家介紹了關(guān)于Flutter學(xué)習(xí)之構(gòu)建、布局及繪制三部曲的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Android自定義引導(dǎo)玩轉(zhuǎn)ViewPager的方法詳解
這篇文章主要給大家介紹了關(guān)于Android自定義引導(dǎo)玩轉(zhuǎn)ViewPager的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06C#之Android手機(jī)App開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了C#之Android手機(jī)App開(kāi)發(fā),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06Android kotlin使用注解實(shí)現(xiàn)防按鈕連點(diǎn)功能的示例
這篇文章主要介紹了Android kotlin使用注解實(shí)現(xiàn)防按鈕連點(diǎn)功能的示例,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03詳解Android冷啟動(dòng)實(shí)現(xiàn)APP秒開(kāi)的方法
這篇文章給大家介紹的是Android冷啟動(dòng)實(shí)現(xiàn)APP秒開(kāi)的方法,對(duì)大家日常開(kāi)發(fā)APP還是很實(shí)用的,有需要的可以參考借鑒。2016-08-08Android短信驗(yàn)證碼倒計(jì)時(shí)驗(yàn)證的2種常用方式
各位開(kāi)發(fā)者們?cè)陂_(kāi)發(fā)中經(jīng)常會(huì)遇到獲取短信驗(yàn)證碼,獲取驗(yàn)證碼后需要等待1分鐘倒計(jì)時(shí),這段時(shí)間是不能再次發(fā)送短信請(qǐng)求的。這篇文章總結(jié)了兩種常用的Android​短信驗(yàn)證碼倒計(jì)時(shí)驗(yàn)證方式,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-12-12Android?AccessibilityService?事件分發(fā)原理分析總結(jié)
這篇文章主要介紹了Android?AccessibilityService?事件分發(fā)原理分析總結(jié),AccessibilityService有很多用來(lái)接收外部調(diào)用事件變化的方法,這些方法封裝在內(nèi)部接口Callbacks中,文章圍繞AccessibilityService相關(guān)資料展開(kāi)詳情,需要的朋友可以參考一下2022-06-06Android自定義狀態(tài)欄顏色與應(yīng)用標(biāo)題欄顏色一致
看IOS上的應(yīng)用,應(yīng)用中狀態(tài)欄的顏色總能與應(yīng)用標(biāo)題欄顏色保持一致,用戶體驗(yàn)很不錯(cuò),對(duì)于這種效果怎么實(shí)現(xiàn)的呢?下面小編給大家分享android自定義狀態(tài)欄顏色與應(yīng)用標(biāo)題欄顏色一致的實(shí)現(xiàn)方法,一起看看吧2016-09-09