【应用场景】:热门标签、推荐
【特点】:
在布局内,随意摆放任意个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;
}
}