首页 > 代码库 > 热门标签推荐的流式布局

热门标签推荐的流式布局

【应用场景】:热门标签、推荐

技术分享

技术分享

技术分享

【特点】:

在布局内,随意摆放任意个view,每行所摆放的view个数,根据实施计算出来的宽度,一旦当前要摆放的view宽度和之前摆放的所有view宽度加在一起,超过了布局的宽度,那么就把该view换行摆放。

 

【布局】:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.atguigu.p2pinvest0520.ui.FlowLayout
android:id="@+id/flow_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_purple">

</com.atguigu.p2pinvest0520.ui.FlowLayout>

</LinearLayout>

 

【自定义FlowLayout】:

public class FlowLayout extends ViewGroup {
public FlowLayout(Context context) {
this(context, null);
}

public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);


}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//获取宽度和高度的模式和数值
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

//如果是至多模式,需要我们自己去测量布局的宽度和高度
int width = 0;
int height = 0;

//声明一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0;

//获取子视图的个数
int childCount = getChildCount();
for(int i = 0;i < childCount;i++){
//获取每一个子视图的宽度和高度,边距值
View childView = getChildAt(i);
//需要调用如下的方法之后,才可以获取子视图的宽高
measureChild(childView,widthMeasureSpec,heightMeasureSpec);
//获取测量的宽高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//获取边距(要想获取边距,必须重写当前类的方法generateLayoutParams())
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();

if(lineWidth + childWidth + mp.leftMargin + mp.rightMargin <= widthSize){//不换行
lineWidth += childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = Math.max(lineHeight,childHeight + mp.topMargin + mp.bottomMargin);

}else{//换行
width = Math.max(width,lineWidth);
height += lineHeight;

//重置
lineWidth = childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = childHeight + mp.topMargin + mp.bottomMargin;
}

//单独的考虑一下最后一个!
if(i == childCount - 1){
width = Math.max(width,lineWidth);
height += lineHeight;
}


}

Log.e("TAG", "widthSize = " + widthSize + ",heightSize = " + heightSize);
Log.e("TAG", "width = " + width + ",height = " +height);
//设置当前布局的宽高
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY? widthSize : width,
heightMode == MeasureSpec.EXACTLY ? heightSize : height);
}

private List<Integer> allHeights = new ArrayList<>();//每一行的高度构成的集合
private List<List<View>> allViews = new ArrayList<>();//每一行view集合构成的集合

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

int width = getWidth();//获取布局的宽度

//每一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0;

 

int childCount = getChildCount();
List<View> lineViews = new ArrayList<>();//用于保存一行的所有的View
//目的:给allHeights 和 allViews 赋值
for(int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();

MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();

if(lineWidth + childWidth + mp.leftMargin + mp.rightMargin <= width){//不换行
lineWidth += childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = Math.max(lineHeight,childHeight + mp.topMargin + mp.bottomMargin);

lineViews.add(childView);
}else{//换行
allHeights.add(lineHeight);
allViews.add(lineViews);

lineViews = new ArrayList<>();
lineViews.add(childView);

lineWidth = childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = childHeight + mp.topMargin + mp.bottomMargin;

}

//单独考虑最后一个元素
if(i == childCount - 1){
allViews.add(lineViews);
allHeights.add(lineHeight);

}
}

Log.e("TAG", "allViews.size = " + allViews.size() + "allHeight.size = " + allHeights.size());

int lineNumber = allViews.size();

int x = 0;
int y = 0;

for(int i = 0; i < lineNumber; i++) {
List<View> singleLineViews = allViews.get(i);//获取一行中元素构成的集合
int singleLineHeight = allHeights.get(i);//获取一行的高度
for(View view : singleLineViews){//遍历一行元素
MarginLayoutParams mp = (MarginLayoutParams) view.getLayoutParams();
int left = x + mp.leftMargin;
int top = y + mp.topMargin;
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();


view.layout(left,top,right,bottom);

x += view.getMeasuredWidth() + mp.leftMargin + mp.rightMargin;


}
//换行
x = 0;
y += singleLineHeight;


}
}

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
MarginLayoutParams mp = new MarginLayoutParams(getContext(), attrs);
return mp;

}
}

【动态添加TextView】

public class ProductHotFragment extends BaseFragment {


@Bind(R.id.flow_layout)
FlowLayout flowLayout;

//提供页面要显示的数据
private String[] datas = new String[]{"新手计划", "乐享活系列90天计划", "钱包", "30天理财计划(加息2%)",
"林业局投资商业经营与大捞一笔", "中学老师购买车辆", "屌丝下海经商计划", "新西游影视拍",
"Java培训老师自己周转", "HelloWorld", "C++-C-ObjectC-java", "Android vs ios", "算法与数据结构", "JNI与NDK", "team working"};
private Random random;

@Override
protected RequestParams getParams() {
return null;
}

@Override
protected String getUrl() {
return null;
}

@Override
protected void initData(String content) {
random = new Random();
//1.动态的创建TextView
for(int i = 0; i < datas.length; i++) {

final TextView tv = new TextView(getActivity());

//设置TextView的属性
tv.setText(datas[i]);
tv.setTextSize(UIUtils.dp2Px(10));

ViewGroup.MarginLayoutParams mp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
mp.leftMargin = UIUtils.dp2Px(8);
mp.rightMargin = UIUtils.dp2Px(8);
mp.topMargin = UIUtils.dp2Px(8);
mp.bottomMargin = UIUtils.dp2Px(8);
tv.setLayoutParams(mp);

//设置textView的背景
int red = random.nextInt(211);
int green = random.nextInt(211);
int blue = random.nextInt(211);
//测试一:
// tv.setBackground(DrawUtils.getDrawable(Color.rgb(red,green,blue),UIUtils.dp2Px(5)));

//测试二:
tv.setBackground(DrawUtils.getSelector(DrawUtils.getDrawable(Color.rgb(red, green, blue),UIUtils.dp2Px(5)),DrawUtils.getDrawable(Color.WHITE,UIUtils.dp2Px(5))));
//保存按下能显示selector的效果,需要设置一个如下的属性
// tv.setClickable(true);

//添加点击事件,也是实现显示selector的效果的一种方式
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ProductHotFragment.this.getActivity(), tv.getText(), Toast.LENGTH_SHORT).show();
}
});

//设置内边距
int padding = UIUtils.dp2Px(5);
tv.setPadding(padding,padding,padding,padding);

// 2.添加到FlowLayout布局中
flowLayout.addView(tv);
}

}

@Override
protected void initTitle() {

}

@Override
public int getLayoutId() {
return R.layout.fragment_product_hot;
}


}

 

热门标签推荐的流式布局