Android自定義ViewGroup實(shí)現(xiàn)選擇面板
背景
在做社交類平臺(tái)開發(fā)的小伙伴都躲不開選擇社交個(gè)性標(biāo)簽的業(yè)務(wù)需求,那么實(shí)現(xiàn)這個(gè)UI效果我想大伙第一時(shí)間想到的必定是RecycleView或GridView,其實(shí)這兩者都可以實(shí)現(xiàn)需求,但我們的標(biāo)簽長(zhǎng)度是不固定的,有可能是4個(gè)字符也有可能是10個(gè)字符,這時(shí)使用這兩者就很能實(shí)現(xiàn)根據(jù)每個(gè)標(biāo)簽的寬度來(lái)自適應(yīng)換行顯示,那么這時(shí)就離不開自定義ViewGroup
效果
至于我這里的效果為什么不根據(jù)字體的數(shù)量進(jìn)行自適應(yīng)寬度的問(wèn)題,是因?yàn)槲疫@邊的產(chǎn)品要求每行顯示四個(gè)且寬高一致,所以我在每個(gè)item外面加了一層RelativeLayout,需要自適應(yīng)寬度的朋友可以在創(chuàng)建item時(shí)不要在item外面多加一層
思路
1,我們先把每一行的標(biāo)簽看作一個(gè)對(duì)象
2,在onMeasure()方法中獲取ViewGroup寬度,減去padding值便是ViewGroup的可用寬度
3,獲取所有的子View進(jìn)行遍歷,創(chuàng)建一個(gè)對(duì)象來(lái)存儲(chǔ)每一行的標(biāo)簽view,每次添加一個(gè)標(biāo)簽view先判斷剩余空間能否存放得下這個(gè)標(biāo)簽view,如果不能則換行
4,在onLayout()方法中進(jìn)行布局,循環(huán)子view并調(diào)用其layout()方法進(jìn)行布局,每布局一個(gè)子view就計(jì)算出下一個(gè)子view的x坐標(biāo),y坐標(biāo)
完整代碼
/** * create by lijianhui * on 2022-7-6 * <p> * description: 自定義標(biāo)簽選擇面板 */ public class TagSelectView extends ViewGroup implements View.OnClickListener { private int mMaxWidth; private int mHorizontalSpace = DensityUtil.dp2px(5); private int mVerticalSpace = DensityUtil.dp2px(10); private List<RowTag> mRows = new ArrayList<>(); private TagClickCallback mTagClickCallback; private int mTitleHeight; private boolean mUpdateTabState = true; public TagSelectView(@NonNull Context context) { super(context); } public TagSelectView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TagSelectView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 測(cè)量寬高 */ @SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mRows.clear(); // 獲取總寬度 int width = MeasureSpec.getSize(widthMeasureSpec); mMaxWidth = width - getPaddingStart() - getPaddingEnd(); //測(cè)量子view int childCount = this.getChildCount(); RowTag currentLine = null; for (int i = mTitleHeight > 0 ? 1 : 0; i < childCount; i++) { View childView = getChildAt(i); childView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); childView.setOnClickListener(this); if (currentLine == null) { currentLine = new RowTag(mMaxWidth, mHorizontalSpace); currentLine.addTagView(childView); mRows.add(currentLine); } else { if (currentLine.canAddView(childView)) { currentLine.addTagView(childView); } else { currentLine = new RowTag(mMaxWidth, mHorizontalSpace); currentLine.addTagView(childView); mRows.add(currentLine); } } } //測(cè)量自己 int height = getPaddingTop() + getPaddingBottom(); for (int i = 0; i < mRows.size(); i++) { height += mRows.get(i).mHeight; } height += (mRows.size() - 1) * mVerticalSpace; height += mTitleHeight; setMeasuredDimension(width, height); } /** * 布局子view */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { left = getPaddingStart(); top = getPaddingTop() + mTitleHeight; for (int i = 0; i < mRows.size(); i++) { // 獲取行 RowTag line = mRows.get(i); // 管理 line.layoutView(top, left); // 更新高度 top += line.mHeight; if (i != mRows.size() - 1) { top += mVerticalSpace; } } } /** * 添加標(biāo)題 * * @param view 標(biāo)題 */ public void addTitleView(View view) { view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); mTitleHeight = view.getMeasuredHeight() + DensityUtil.dp2px(15); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); addView(view); } /** * 標(biāo)簽被點(diǎn)擊 * * @param v 點(diǎn)擊的標(biāo)簽 */ @Override public void onClick(View v) { if (mUpdateTabState) { v.setSelected(!v.isSelected()); } if (mTagClickCallback != null) { mTagClickCallback.tagClick(v); } } /** * 設(shè)置標(biāo)簽點(diǎn)擊回調(diào)接口 * * @param tagClickCallback 點(diǎn)擊回調(diào)接口 */ public void setTagClickCallback(TagClickCallback tagClickCallback) { mTagClickCallback = tagClickCallback; } /** * 設(shè)置點(diǎn)擊表情是否改變狀態(tài) * * @param updateTabState true:改變 */ public void setUpdateTabState(boolean updateTabState) { this.mUpdateTabState = updateTabState; } /** * 設(shè)置水平間距 * * @param horizontalSpace 間距 */ public void setHorizontalSpace(int horizontalSpace) { this.mHorizontalSpace = horizontalSpace; } /** * 標(biāo)簽點(diǎn)擊回調(diào)接口 */ public interface TagClickCallback { void tagClick(View view); } /** * 每一行的數(shù)據(jù) */ private static class RowTag { private final int mMaxWidth; private final int mHorizontalSpace; private final List<View> mTagViews; private int mUsedWidth; private int mHeight; public RowTag(int maxWidth, int horizontalSpace) { this.mMaxWidth = maxWidth; this.mHorizontalSpace = horizontalSpace; this.mTagViews = new ArrayList<>(); } /** * 添加標(biāo)簽 * * @param view 標(biāo)簽view */ public void addTagView(View view) { int childWidth = view.getMeasuredWidth(); int childHeight = view.getMeasuredHeight(); if (mTagViews.size() == 0) { if (childWidth > mMaxWidth) { mUsedWidth = mMaxWidth; } else { mUsedWidth = childWidth + mHorizontalSpace; } mHeight = childHeight; } else { mUsedWidth += childWidth + mHorizontalSpace; mHeight = Math.max(childHeight, mHeight); } mTagViews.add(view); } /** * 判斷是否可添加 * * @param view 要添加的標(biāo)簽view * @return 如果剩余寬度可以裝下要添加的標(biāo)簽view返回true */ public boolean canAddView(View view) { if (mTagViews.size() == 0) { return true; } return view.getMeasuredWidth() <= (mMaxWidth - mUsedWidth - mHorizontalSpace); } /** * 布局標(biāo)簽 * * @param t 頭坐標(biāo) * @param l 左坐標(biāo) */ public void layoutView(int t, int l) { int avg = 0; if (mTagViews.size() > 1) { avg = (mMaxWidth - mUsedWidth) / (mTagViews.size() - 1); } for (View view : mTagViews) { // 獲取寬高 如需填充空余空間:measuredWidth = view.getMeasuredWidth() + avg int measuredWidth = view.getMeasuredWidth(); int measuredHeight = view.getMeasuredHeight(); // 重新測(cè)量 view.measure(MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)); // 重新獲取寬度值 measuredWidth = view.getMeasuredWidth(); int top = t; int left = l; int right = measuredWidth + left; int bottom = measuredHeight + top; // 指定位置 view.layout(left, top, right, bottom); // 更新坐標(biāo) l += measuredWidth + mHorizontalSpace; } } } }
使用
/** * 構(gòu)建標(biāo)簽 * * @param tagName 標(biāo)簽名稱 */ private void buildTagView(String tagName, boolean social) { RelativeLayout relativeLayout = new RelativeLayout(getContext()); SuperTextView superTextView = new SuperTextView(getContext()); superTextView.setGravity(Gravity.CENTER); superTextView.setText(tagName.length() > 5 ? tagName.substring(0, 5) : tagName); superTextView.setSolid(social ? Color.parseColor("#A68CFF") : Color.TRANSPARENT); if (!social) { superTextView.setStrokeColor(Color.parseColor("#EDEDED")); superTextView.setStrokeWidth(DensityUtil.dp2px(1)); } superTextView.setTextColor(social ? Color.WHITE : Color.parseColor("#727272")); superTextView.setTextSize(11); superTextView.setCorner(DensityUtil.dp2px(14)); superTextView.setOnClickListener(this); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(DensityUtil.dp2px(70), DensityUtil.dp2px(28)); relativeLayout.addView(superTextView, params); mBinding.tagSelectView.addView(relativeLayout); }
到此這篇關(guān)于Android自定義ViewGroup實(shí)現(xiàn)選擇面板的文章就介紹到這了,更多相關(guān)Android ViewGroup內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流效果
- Android自定義ViewGroup多行多列效果
- Android自定義ViewGroup實(shí)現(xiàn)朋友圈九宮格控件
- Android自定義ViewGroup實(shí)現(xiàn)流式布局
- Android進(jìn)階教程之ViewGroup自定義布局
- Android自定義ViewGroup實(shí)現(xiàn)豎向引導(dǎo)界面
- Android自定義ViewGroup實(shí)現(xiàn)淘寶商品詳情頁(yè)
- 一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)
- Android自定義控件ViewGroup實(shí)現(xiàn)標(biāo)簽云
相關(guān)文章
Android Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的方法示例
本篇文章主要介紹了Android Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Android自定義控件實(shí)現(xiàn)萬(wàn)能的對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)萬(wàn)能對(duì)話框的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android應(yīng)用接入微信分享的實(shí)例代碼
本篇文章主要介紹了Android應(yīng)用接入微信分享的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-07-07Win10下android studio開發(fā)環(huán)境配置圖文教程
這篇文章主要為大家詳細(xì)介紹了Win10下android studio開發(fā)環(huán)境配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android TextView(圓弧)邊框和背景實(shí)例詳解
這篇文章主要介紹了 Android TextView(圓?。┻吙蚝捅尘皩?shí)例詳解的相關(guān)資料,這里提供了實(shí)現(xiàn)代碼和實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-11-11