背景
在做社交类平台开发的小伙伴都躲不开选择社交个性标签的业务需求,那么实现这个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坐标
完整代码
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);
}
@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;
}
}
}
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);
}
@Override
public void onClick(View v) {
if (mUpdateTabState) {
v.setSelected(!v.isSelected());
}
if (mTagClickCallback != null) {
mTagClickCallback.tagClick(v);
}
}
public void setTagClickCallback(TagClickCallback tagClickCallback) {
mTagClickCallback = tagClickCallback;
}
public void setUpdateTabState(boolean updateTabState) {
this.mUpdateTabState = updateTabState;
}
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<>();
}
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);
}
public boolean canAddView(View view) {
if (mTagViews.size() == 0) {
return true;
}
return view.getMeasuredWidth() <= (mMaxWidth - mUsedWidth - mHorizontalSpace);
}
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;
}
}
}
}
使用
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);
}
到此这篇关于Android自定义ViewGroup实现选择面板的文章就介绍到这了,更多相关Android ViewGroup内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!