android搜索热词(热门标签)流式布局的实现
16lz
2021-01-24
先看下效果图
1、流式布局实现
继承ViewGroup,重写onMeasure,onLayout方法。代码如下:
package com.example.lin.flowlayoutdemo;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;/** * Created by lin.zhou on 2015/8/12. * 流式布局 */public class YhFlowLayout extends ViewGroup { private List mLines = new ArrayList(); // 用来记录描述有多少行View private Line mCurrrenLine; // 用来记录当前已经添加到了哪一行 private int mHorizontalSpace = 10; private int mVerticalSpace = 6; public YhFlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public YhFlowLayout(Context context) { super(context); } public void setSpace(int horizontalSpace, int verticalSpace) { this.mHorizontalSpace = horizontalSpace; this.mVerticalSpace = verticalSpace; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //清空 mLines.clear(); mCurrrenLine = null; int layoutWidth = MeasureSpec.getSize(widthMeasureSpec); // 获取行最大的宽度 int maxLineWidth = layoutWidth - getPaddingLeft() - getPaddingRight(); //测量孩子 int count = getChildCount(); for (int i = 0; i < count; i++) { View v = getChildAt(i); //如果孩子不可见 if (v.getVisibility() == GONE) { continue; } measureChild(v, widthMeasureSpec, heightMeasureSpec); // 往lines添加孩子 if (mCurrrenLine == null) { // 说明还没有开始添加孩子 mCurrrenLine = new Line(maxLineWidth, mHorizontalSpace); // 添加到 Lines中 mLines.add(mCurrrenLine); // 行中一个孩子都没有 mCurrrenLine.addView(v); } else { // 行中有孩子了 Boolean canAdd = mCurrrenLine.canAdd(v); if (canAdd) { mCurrrenLine.addView(v); } else { //装不下,换行 mCurrrenLine = new Line(maxLineWidth, mHorizontalSpace); mLines.add(mCurrrenLine); // 将view添加到line mCurrrenLine.addView(v); } } } // 设置自己的宽度和高度 int measuredWidth = layoutWidth; float allHeight = 0; for (int i = 0; i < mLines.size(); i++) { float mHeigth = mLines.get(i).mHeigth; // 加行高 allHeight += mHeigth; // 加间距 if (i != 0) { allHeight += mVerticalSpace; } } int measuredHeight = (int) (allHeight + getPaddingTop() + getPaddingBottom() + 0.5f); setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 给Child 布局---> 给Line布局 int paddingLeft = getPaddingLeft(); int offsetTop = getPaddingTop(); for (int i = 0; i < mLines.size(); i++) { Line line = mLines.get(i); // 给行布局 line.layout(paddingLeft, offsetTop); offsetTop += line.mHeigth + mVerticalSpace; } } class Line { // 属性 private List mViews = new ArrayList(); // 用来记录每一行有几个View private float mMaxWidth; // 行最大的宽度 private float mUsedWidth; // 已经使用了多少宽度 private float mHeigth; // 行的高度 private float mMarginLeft; private float mMarginRight; private float mMarginTop; private float mMarginBottom; private float mHorizontalSpace; // View和view之间的水平间距 // 构造 public Line(int maxWidth, int horizontalSpace) { this.mMaxWidth = maxWidth; this.mHorizontalSpace = horizontalSpace; } // 方法 /** * 添加view,记录属性的变化 * * @param view */ public void addView(View view) { // 加载View的方法 int size = mViews.size(); int viewWidth = view.getMeasuredWidth(); int viewHeight = view.getMeasuredHeight(); // 计算宽和高 if (size == 0) { // 说还没有添加View if (viewWidth > mMaxWidth) { mUsedWidth = mMaxWidth; } else { mUsedWidth = viewWidth; } mHeigth = viewHeight; } else { // 多个view的情况 mUsedWidth += viewWidth + mHorizontalSpace; mHeigth = mHeigth < viewHeight ? viewHeight : mHeigth; } // 将View记录到集合中 mViews.add(view); } /** * 用来判断是否可以将View添加到line中 * * @param view * @return */ public boolean canAdd(View view) { // 判断是否能添加View int size = mViews.size(); if (size == 0) { return true; } int viewWidth = view.getMeasuredWidth(); // 预计使用的宽度 float planWidth = mUsedWidth + mHorizontalSpace + viewWidth; if (planWidth > mMaxWidth) { // 加不进去 return false; } return true; } /** * 给孩子布局 * * @param offsetLeft * @param offsetTop */ public void layout(int offsetLeft, int offsetTop) { // 给孩子布局 int currentLeft = offsetLeft; int size = mViews.size(); // 判断已经使用的宽度是否小于最大的宽度 float extra = 0; float widthAvg = 0; if (mMaxWidth > mUsedWidth) { extra = mMaxWidth - mUsedWidth; widthAvg = extra / size; } for (int i = 0; i < size; i++) { View view = mViews.get(i); int viewWidth = view.getMeasuredWidth(); int viewHeight = view.getMeasuredHeight(); // 判断是否有富余 if (widthAvg != 0) { // 改变宽度 int newWidth = (int) (viewWidth + widthAvg + 0.5f); int widthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY); int heightMeasureSpec = MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY); view.measure(widthMeasureSpec, heightMeasureSpec); viewWidth = view.getMeasuredWidth(); viewHeight = view.getMeasuredHeight(); } // 布局 int left = currentLeft; int top = (int) (offsetTop + (mHeigth - viewHeight) / 2 + 0.5f); // int top = offsetTop; int right = left + viewWidth; int bottom = top + viewHeight; view.layout(left, top, right, bottom); currentLeft += viewWidth + mHorizontalSpace; } } }}
2、Demo演示
布局文件
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <com.example.lin.flowlayoutdemo.YhFlowLayout android:id="@+id/flowlayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp"> com.example.lin.flowlayoutdemo.YhFlowLayout>RelativeLayout>
下面根据需求设置一些流失布局中标签的属性:
private void displayUI() { for (int i = 0; i < mDatas.size(); i++) { final String data = mDatas.get(i); TextView tv = new TextView(this); tv.setText(data); tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); tv.setGravity(Gravity.CENTER); int paddingy = DisplayUtils.dp2Px(this, 7); int paddingx = DisplayUtils.dp2Px(this, 6); tv.setPadding(paddingx, paddingy, paddingx, paddingy); tv.setClickable(false); int shape = GradientDrawable.RECTANGLE; int radius = DisplayUtils.dp2Px(this, 4); int strokeWeight = DisplayUtils.dp2Px(this, 1); int stokeColor = getResources().getColor(R.color.text_color_gray); int stokeColor2 = getResources().getColor(R.color.green); GradientDrawable normalBg = DrawableUtils.getShape(shape, radius, strokeWeight, stokeColor, Color.WHITE); GradientDrawable pressedBg = DrawableUtils.getShape(shape, radius, strokeWeight, stokeColor2, getResources().getColor(R.color.green)); StateListDrawable selector = DrawableUtils.getSelector(normalBg, pressedBg); tv.setBackgroundDrawable(selector); ColorStateList colorStateList = DrawableUtils.getColorSelector(getResources().getColor(R.color.text_color_gray), getResources().getColor(R.color.white)); tv.setTextColor(colorStateList); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); flowLayout.addView(tv); } }
如果需要设置viewgroup的间距
flowLayout = (YhFlowLayout)findViewById(R.id.flowlayout); flowLayout.setSpace(DisplayUtils.dp2Px(this, 5), DisplayUtils.dp2Px(this, 5)); flowLayout.setPadding(DisplayUtils.dp2Px(this, 5), DisplayUtils.dp2Px(this, 5), DisplayUtils.dp2Px(this, 5), DisplayUtils.dp2Px(this, 5));
ok,现在好看多了。
3、下面贴demo的下载地址
http://download.csdn.net/detail/zhoulin541/9574989
更多相关文章
- 解决ListView或ExpandableListView滚动时变黑
- Android(安卓)DataBinding 基础使用
- android 设置对话框的宽度和高度
- Android(安卓)PopWindow的简单应用
- 在页面添加一个透明Layout
- Android(安卓)ListView根据项数的大小自动改变高度
- 强大的 Android(安卓)属性动画 ObjectAnimator
- Android(安卓)EditView属性解析
- Android(安卓)Animation Tween动画效果的使用