Without further ado, let’s go directly to the code:
/** * Word wrap layout * Created by 马占柱 on 2018/4/20. */ public class WrapLayout extends ViewGroup { private static final String TAG = "WrapLayout"; /** * style of TextView */ public int TEXTVIEW_STYLE = 0; /** * Button's style */ public int BUTTON_STYLE = 1; private int style; private View btn; private MarkClickListener markClickListener; /** * Store all Views, record by row */ private List<List<View>> mAllViews = new ArrayList<>(); /** * Record the maximum height of each line */ private List<Integer> mLineHeight = new ArrayList<>(); public WrapLayout(Context context) { super(context); } public WrapLayout(Context context, AttributeSet attrs) { super(context, attrs); } public WrapLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setData(List<String> data, Context context, int textSize) { String[] mydata = null; if (data != null) { int length = data.size(); mydata = new String[length]; for (int i = 0; i < length; i++) { mydata[i] = data.get(i); } } createChild(mydata, context, textSize, 8, 5, 5, 8, 5, 5, 5, 5); } private void createChild(String[] data, final Context context, int textSize, int pl, int pt, int pr, int pb, int ml, int mt, int mr, int mb) { int size = data.length; for (int i = 0; i < size; i++) { String text = data[i]; //By judging whether the style is TextView or Button for different operations, you can continue to add different views if (style == TEXTVIEW_STYLE) { btn = LayoutInflater.from(context).inflate(R.layout.textview, null); ((TextView) btn).setGravity(Gravity.CENTER); ((TextView) btn).setText(text); ((TextView) btn).setTextSize(textSize); } else if (style == BUTTON_STYLE) { btn = new Button(context); ((Button) btn).setGravity(Gravity.CENTER); ((Button) btn).setText(text); ((Button) btn).setTextSize(textSize); } btn.setClickable(true); btn.setPadding(dip2px(context, pl), dip2px(context, pt), dip2px(context, pr), dip2px(context, pb)); MarginLayoutParams params = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); params.setMargins(ml, mt, mr, mb); btn.setLayoutParams (params); final int finalI = i; //Add click event to each view btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { markClickListener.clickMark(finalI); } }); this.addView(btn); } } /** * Convert from dp unit to px (pixel) according to the resolution of the phone */ private int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public void setData(String[] data, Context context, int textSize, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, int marginLeft, int marginTop, int marginRight, int marginButton) { createChild(data, context, textSize, paddingLeft, paddingTop, paddingRight, paddingBottom, marginLeft, marginTop, marginRight, marginButton); } public void setData(String[] data, Context context, int textSize) { createChild(data, context, textSize, 8, 5, 5, 8, 5, 5, 5, 5); } public void setData(List<String> data, Context context, int textSize, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, int marginLeft, int marginTop, int marginRight, int marginButton) { String[] mydata = null; if (data != null) { int length = data.size(); mydata = new String[length]; for (int i = 0; i < length; i++) { mydata[i] = data.get(i); } } createChild(mydata, context, textSize, paddingLeft, paddingTop, paddingRight, paddingBottom, marginLeft, marginTop, marginRight, marginButton); } public void setMarkClickListener(MarkClickListener markClickListener) { this.markClickListener = markClickListener; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childCount = getChildCount(); int lineWidth = 0; int lineHeight = 0; int width = 0;//warpcontet is the width that needs to be recorded int height = 0; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); // measure the width and height of each child measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; Log.e(TAG, "onMeasure: lineHeight = " + lineHeight + " childHeight = " + childHeight); if (lineWidth + childWidth > widthSize) { width = Math.max(lineWidth, childWidth);//This case is to exclude the case where a single label is very long lineWidth = childWidth;//Open a new line height += lineHeight;//Record the total line height lineHeight = childHeight;//Because a new line is opened, the height of this line should be recorded } else { lineWidth += childWidth; // lineHeight = Math.max(lineHeight, childHeight); //Record line height lineHeight = Math.max(height, childHeight); //Record line height } // If it is the last one, compare the maximum width of the current record with the current lineWidth if (i == childCount - 1) { width = Math.max(width, lineWidth); //宽度 height += lineHeight; // } } setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height); } //OnLayout completes the specification of the position and size of all childViews @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllViews.clear(); //Clear the list of child controls mLineHeight.clear(); //Clear the height record list int width = getWidth();//Get the width of the current control (it has been measured in the onmeasure method) int childCount = getChildCount(); // store all childViews for each row List<View> lineViews = new ArrayList<View>(); int lineWidth = 0; //line width int lineHeight = 0; //Total line height for (int i = 0; i < childCount; i++) { View child = getChildAt(i); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//Get property parameters int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // if a newline is already required if (i == 3) { i = 3; } if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) // greater than the width of the parent layout { // Record all Views in this row and the maximum height mLineHeight.add(lineHeight); // Save the childView of the current row, then open a new ArrayList to save the childView of the next row mAllViews.add(lineViews); lineWidth = 0; // reset the line width lineViews = new ArrayList<View>(); } /** * Accumulate if no newline is required */ lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // record the last line (because the last line is definitely larger than the width of the parent layout, so adding the last line is necessary) mLineHeight.add(lineHeight); mAllViews.add(lineViews); int left = 0; int top = 0; int lineNums = mAllViews.size(); for (int i = 0; i < lineNums; i++) { // all views for each row lineViews = mAllViews.get(i); // The maximum height of the current row is the same for each row, so use (i+1) to set the height lineHeight = (i + 1) * mLineHeight.get(i); for (int j = 0; j < lineViews.size(); j++) { View lineChild = lineViews.get(j); if (lineChild.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) lineChild.getLayoutParams(); //Start drawing labels. The distance between the left and the top is determined according to the accumulated number. int lc = left + lp.leftMargin; int tc = top + lp.topMargin; int rc = lc + lineChild.getMeasuredWidth(); int bc = tc + lineChild.getMeasuredHeight(); lineChild.layout(lc, tc, rc, bc); left += lineChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin; } left = 0; // reset left to zero top = lineHeight; } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } public void setStyle(int style) { this.style = style; } public interface MarkClickListener { void clickMark(int position); } }
Here is the layout of the textview:
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/tv_bg" android:ellipsize="end" android:gravity="center" android:maxLines="1" android:textColor="@android:color/white" />
This is the background color of the textview:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="10dp" /> <solid android:color="#227652" /> </shape>
Use this in the layout:
<com.love5653520.mazhanzhu.utils.WrapLayout android:id="@+id/wrap" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/text_remen" android:background="@color/white" android:layout_marginBottom="5dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp"> </com.love5653520.mazhanzhu.utils.WrapLayout>Okay, if you say too much, it's all nonsense. You can directly see the effect after copying it and running it. There are other requirements that can be changed slowly according to this. Basically, it can meet the effect of more than 80%!