Android自定義ViewGroup實現(xiàn)選擇面板
背景
在做社交類平臺開發(fā)的小伙伴都躲不開選擇社交個性標(biāo)簽的業(yè)務(wù)需求,那么實現(xiàn)這個UI效果我想大伙第一時間想到的必定是RecycleView或GridView,其實這兩者都可以實現(xiàn)需求,但我們的標(biāo)簽長度是不固定的,有可能是4個字符也有可能是10個字符,這時使用這兩者就很能實現(xiàn)根據(jù)每個標(biāo)簽的寬度來自適應(yīng)換行顯示,那么這時就離不開自定義ViewGroup
效果
至于我這里的效果為什么不根據(jù)字體的數(shù)量進行自適應(yīng)寬度的問題,是因為我這邊的產(chǎn)品要求每行顯示四個且寬高一致,所以我在每個item外面加了一層RelativeLayout,需要自適應(yīng)寬度的朋友可以在創(chuàng)建item時不要在item外面多加一層

思路
1,我們先把每一行的標(biāo)簽看作一個對象
2,在onMeasure()方法中獲取ViewGroup寬度,減去padding值便是ViewGroup的可用寬度
3,獲取所有的子View進行遍歷,創(chuàng)建一個對象來存儲每一行的標(biāo)簽view,每次添加一個標(biāo)簽view先判斷剩余空間能否存放得下這個標(biāo)簽view,如果不能則換行
4,在onLayout()方法中進行布局,循環(huán)子view并調(diào)用其layout()方法進行布局,每布局一個子view就計算出下一個子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);
}
/**
* 測量寬高
*/
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mRows.clear();
// 獲取總寬度
int width = MeasureSpec.getSize(widthMeasureSpec);
mMaxWidth = width - getPaddingStart() - getPaddingEnd();
//測量子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);
}
}
}
//測量自己
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)簽被點擊
*
* @param v 點擊的標(biāo)簽
*/
@Override
public void onClick(View v) {
if (mUpdateTabState) {
v.setSelected(!v.isSelected());
}
if (mTagClickCallback != null) {
mTagClickCallback.tagClick(v);
}
}
/**
* 設(shè)置標(biāo)簽點擊回調(diào)接口
*
* @param tagClickCallback 點擊回調(diào)接口
*/
public void setTagClickCallback(TagClickCallback tagClickCallback) {
mTagClickCallback = tagClickCallback;
}
/**
* 設(shè)置點擊表情是否改變狀態(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à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();
// 重新測量
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實現(xiàn)選擇面板的文章就介紹到這了,更多相關(guān)Android ViewGroup內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android自定義ViewGroup實現(xiàn)標(biāo)簽流效果
- Android自定義ViewGroup多行多列效果
- Android自定義ViewGroup實現(xiàn)朋友圈九宮格控件
- Android自定義ViewGroup實現(xiàn)流式布局
- Android進階教程之ViewGroup自定義布局
- Android自定義ViewGroup實現(xiàn)豎向引導(dǎo)界面
- Android自定義ViewGroup實現(xiàn)淘寶商品詳情頁
- 一篇文章弄懂Android自定義viewgroup的相關(guān)難點
- Android自定義控件ViewGroup實現(xiàn)標(biāo)簽云
相關(guān)文章
Android開發(fā)必備知識 為什么說Kotlin值得一試
為什么說值得一試,這篇文章主要為大家詳細(xì)介紹了Android開發(fā)必備知識,Kotlin的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05
Android學(xué)習(xí)之BottomSheetDialog組件的使用
BottomSheetDialog是底部操作控件,可在屏幕底部創(chuàng)建一個支持滑動關(guān)閉視圖。本文將通過示例詳細(xì)講解它的使用,感興趣的小伙伴可以了解一下2022-06-06
SQLiteStudio優(yōu)雅調(diào)試Android手機數(shù)據(jù)庫Sqlite(推薦)
這篇文章主要介紹了SQLiteStudio優(yōu)雅調(diào)試Android手機數(shù)據(jù)庫Sqlite的相關(guān)資料,需要的朋友可以參考下2017-11-11
Android?無障礙服務(wù)?performAction?調(diào)用過程分析
這篇文章主要介紹了Android?無障礙服務(wù)?performAction?調(diào)用過程分析,無障礙服務(wù)可以模擬一些用戶操作,無障礙可以處理的對象,通過類?AccessibilityNodeInfo?表示,通過無障礙服務(wù),可以通過它的performAction方法來觸發(fā)一些action2022-06-06
詳解Android開發(fā)錄音和播放音頻的步驟(動態(tài)獲取權(quán)限)
這篇文章主要介紹了詳解Android開發(fā)錄音和播放音頻的步驟(動態(tài)獲取權(quán)限),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Android編程之內(nèi)存溢出解決方案(OOM)實例總結(jié)
這篇文章主要介紹了Android編程之內(nèi)存溢出解決方案(OOM),結(jié)合實例實例總結(jié)分析了Android編程過程中常見的內(nèi)存溢出情況與對應(yīng)的解決方法,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11
Android4.4 WebAPI實現(xiàn)拍照上傳功能
這篇文章主要介紹了Android4.4 WebAPI實現(xiàn)拍照上傳功能,本文給出4.4版本后拍照上傳的具體實現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-07-07
android原生實現(xiàn)多線程斷點續(xù)傳功能
這篇文章主要為大家詳細(xì)介紹了android原生實現(xiàn)多線程斷點續(xù)傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07

