網站首頁 編程語言 正文
背景
在做社交類平臺開發的小伙伴都躲不開選擇社交個性標簽的業務需求,那么實現這個UI效果我想大伙第一時間想到的必定是RecycleView或GridView,其實這兩者都可以實現需求,但我們的標簽長度是不固定的,有可能是4個字符也有可能是10個字符,這時使用這兩者就很能實現根據每個標簽的寬度來自適應換行顯示,那么這時就離不開自定義ViewGroup
效果
至于我這里的效果為什么不根據字體的數量進行自適應寬度的問題,是因為我這邊的產品要求每行顯示四個且寬高一致,所以我在每個item外面加了一層RelativeLayout,需要自適應寬度的朋友可以在創建item時不要在item外面多加一層
思路
1,我們先把每一行的標簽看作一個對象
2,在onMeasure()方法中獲取ViewGroup寬度,減去padding值便是ViewGroup的可用寬度
3,獲取所有的子View進行遍歷,創建一個對象來存儲每一行的標簽view,每次添加一個標簽view先判斷剩余空間能否存放得下這個標簽view,如果不能則換行
4,在onLayout()方法中進行布局,循環子view并調用其layout()方法進行布局,每布局一個子view就計算出下一個子view的x坐標,y坐標
完整代碼
/**
* create by lijianhui
* on 2022-7-6
* <p>
* description: 自定義標簽選擇面板
*/
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;
}
}
}
/**
* 添加標題
*
* @param view 標題
*/
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);
}
/**
* 標簽被點擊
*
* @param v 點擊的標簽
*/
@Override
public void onClick(View v) {
if (mUpdateTabState) {
v.setSelected(!v.isSelected());
}
if (mTagClickCallback != null) {
mTagClickCallback.tagClick(v);
}
}
/**
* 設置標簽點擊回調接口
*
* @param tagClickCallback 點擊回調接口
*/
public void setTagClickCallback(TagClickCallback tagClickCallback) {
mTagClickCallback = tagClickCallback;
}
/**
* 設置點擊表情是否改變狀態
*
* @param updateTabState true:改變
*/
public void setUpdateTabState(boolean updateTabState) {
this.mUpdateTabState = updateTabState;
}
/**
* 設置水平間距
*
* @param horizontalSpace 間距
*/
public void setHorizontalSpace(int horizontalSpace) {
this.mHorizontalSpace = horizontalSpace;
}
/**
* 標簽點擊回調接口
*/
public interface TagClickCallback {
void tagClick(View view);
}
/**
* 每一行的數據
*/
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<>();
}
/**
* 添加標簽
*
* @param view 標簽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 要添加的標簽view
* @return 如果剩余寬度可以裝下要添加的標簽view返回true
*/
public boolean canAddView(View view) {
if (mTagViews.size() == 0) {
return true;
}
return view.getMeasuredWidth() <= (mMaxWidth - mUsedWidth - mHorizontalSpace);
}
/**
* 布局標簽
*
* @param t 頭坐標
* @param l 左坐標
*/
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);
// 更新坐標
l += measuredWidth + mHorizontalSpace;
}
}
}
}
使用
/**
* 構建標簽
*
* @param tagName 標簽名稱
*/
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);
}
原文鏈接:https://blog.csdn.net/qq_42359647/article/details/125849918
相關推薦
- 2023-01-08 Python?flask與fastapi性能測試方法介紹_python
- 2022-09-19 C/C++最短路徑算法之迪杰斯特拉Dijkstra的實現詳解_C 語言
- 2022-06-09 FreeRTOS軟件定時器apollo中斷狀態判斷_操作系統
- 2022-04-12 Cannot read property ‘forEach‘ of undefined
- 2022-07-01 python性能檢測工具函數運行內存及運行時間_python
- 2023-05-15 Go語言實現AES加密并編寫一個命令行應用程序_Golang
- 2022-07-06 C++詳細分析講解引用的概念與使用_C 語言
- 2022-06-28 一文搞懂Python中的進程,線程和協程_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同步修改后的遠程分支