Android自定义ViewGroup实现选择面板
背景
在做社交类平台开发的小伙伴都躲不开选择社交个性标签的业务需求,那么实现这个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); }
到此这篇关于Android自定义ViewGroup实现选择面板的文章就介绍到这了,更多相关Android ViewGroup内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Android中EditText 设置 imeOptions 无效问题的解决方法
有时候我们需要在EditText 输出完之后 需要在键盘出现 右下角变成“Go”或“前往 搜索时;通常我们需要设置Android:imeOptions属性,但是今天我发现设置了无效,下面给大家分享下解决方案2016-12-12Android查看电池电量的方法(基于BroadcastReceiver)
这篇文章主要介绍了Android查看电池电量的方法,结合实例分析了Android使用BroadcastReceiver实现针对电池电量的查询技巧,需要的朋友可以参考下2016-01-01Android 后台发送邮件示例 (收集应用异常信息+Demo代码)
今天介绍个更简单的方法,我们把异常信息收集后,通过后台发送邮件方法,把相关异常信息发送到我们指定的邮箱里面2013-07-07Android实现将一个Activity设置成窗口样式的方法
这篇文章主要介绍了Android实现将一个Activity设置成窗口样式的方法,涉及Android的窗口样式设置与布局技巧,具有一定参考借鉴价值,需要的朋友可以参考下2016-02-02
最新评论