流式布局TagsLayout(简单)

作者: 残非 | 来源:发表于2019-09-27 11:34 被阅读0次

什么是自定义控件

  1. 原生控件:SDK已经有,Google提供

  2. 自定义控件: 开发者自己开发的控件,分三种

a. 组合式控件:将现有控件进行组合,实现功能更加强大控件;

b. 继承现有控件: 对其控件的功能进行拓展;

c. 重写View实现全新的控件.

为什么要自定义View?

1.原有控件无法满足我们的需求,所以需要自己实现想要的效果

实现步骤:

a.继承布局,重写构造;
b.创建布局xml,加入到自定义控件里面;
c.实现对应的功能.

实现:

1. onMeasure()用于测量子控件的宽高
2. onLayout()用于摆放子控件在父控件中的位置,只有ViewGroup才能让子控件显示在自己的什么位置.只会触发,执行一次
3. onDraw() 用于绘制需要的图形

Xml布局

<com.example.lenovo.zm0904_textview.views.TagsLayout
    android:id="@+id/tag_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    app:tagHorizontalSpace="10dp"
    app:tagVerticalSpace="10dp"
    tools:ignore="MissingConstraints">

</com.example.lenovo.zm0904_textview.views.TagsLayout>

其中的TagsLayout就是自己绘制的
首先要创建一个类,继承ViewGroup

TagsLayout的编写

package com.example.lenovo.zm0904_textview.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.example.lenovo.zm0904_textview.R;

public class TagsLayout extends ViewGroup {


private int childHorizontalSpace;
private int childVerticalSpace;

public TagsLayout(Context context) {
    super(context);
}

public TagsLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    //动态获取Style中的属性值
    TypedArray attrArray = context.obtainStyledAttributes(attrs, R.styleable.TagsLayout);
    if(attrArray != null){
        childHorizontalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagHorizontalSpace,0);
        childVerticalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagVerticalSpace,0);
        attrArray.recycle();
    }

}

@Override
protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //获取它的父容器为它设置的测量模式和大小
    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
    //如果是warp_content情况下,记录宽和高
    int width=0,height = 0;
    //记录每一行的宽度,width不断取得最大宽度
    int lineWidth=0,lineHeight = 0;
    int count = getChildCount();
    int left = getPaddingLeft();
    int top = getPaddingTop();
    //遍历每个子元素
    for(int i=0; i<count; i++){
        View child = getChildAt(i);
        if(child.getVisibility() == GONE) continue;
        //测量每一个child的宽和高
        measureChild(child,widthMeasureSpec,heightMeasureSpec);
        //得到child的属性
        MarginLayoutParams lp = (MarginLayoutParams)child.getLayoutParams();
        //当前控件的实际占据宽高
        int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin+childHorizontalSpace;
        //获取控件实际占据的高度
        int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin+childVerticalSpace;
        /**
         * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行
         */
        if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
            width = Math.max(lineWidth, childWidth);// 取最大的
            lineWidth = childWidth; // 重新开启新行,开始记录
            // 叠加当前高度,
            height += lineHeight;
            // 开启记录下一行的高度
            lineHeight = childHeight;
            child.setTag(new Location(left, top + height, childWidth + left - childHorizontalSpace, height + child.getMeasuredHeight() + top));
        } else {
            // 否则累加值lineWidth,lineHeight取最大高度
            child.setTag(new Location(lineWidth + left, top + height, lineWidth + childWidth - childHorizontalSpace + left, height + child.getMeasuredHeight() + top));
            lineWidth += childWidth;
            lineHeight = Math.max(lineHeight, childHeight);
        }
    }
    //计算当前对象的大小
    width = Math.max(width, lineWidth) + getPaddingLeft() + getPaddingRight();
    height += lineHeight;
    sizeHeight += getPaddingTop() + getPaddingBottom();
    height += getPaddingTop() + getPaddingBottom();
    setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}

//布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int count = getChildCount();
    //计算子布局中的子元素的位置和大小
    for (int i = 0; i < count; i++) {
        View child = getChildAt(i);
        if (child.getVisibility() == GONE)
            continue;
        Location location = (Location) child.getTag();
        child.layout(location.left, location.top, location.right, location.bottom);
    }
}

/**
 * 记录子控件的坐标
 */
public class Location {
    public Location(int left, int top, int right, int bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    public int left;
    public int top;
    public int right;
    public int bottom;

}
}

MainActivity的编写

package com.example.lenovo.zm0904_textview;

import android.graphics.Color;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.lenovo.zm0904_textview.views.TagsLayout;

/**
 * Created by Lenovo on 2019/9/4.
 */

public class View1Activity extends AppCompatActivity {
private TagsLayout tagsLayout;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_view1);

initView();
}

private void initView() {
tagsLayout = findViewById(R.id.tag_layout);
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
//这是由自己写的,里面的内容可以更改
String[] strs = {"这就是一个自定义组件","android入门","你是一个程序猿","java","python从入门到放弃"};
for(int i=0; i< strs.length; i++){
TextView txt = new TextView(this);
txt.setText(strs[i]);
Log.i("tag", "initView: "+strs[i]);
txt.setTextColor(Color.WHITE);
txt.setBackgroundResource(R.drawable.txt_bg);
tagsLayout.addView(txt,lp);
}
}
}

最后的效果图:

image.png

相关文章

网友评论

    本文标题:流式布局TagsLayout(简单)

    本文链接:https://www.haomeiwen.com/subject/amckyctx.html