流式布局

来源:互联网 发布:高端黑 知乎 编辑:程序博客网 时间:2024/04/19 16:05

    步骤1:写一个自定义的继承ViewGroup的控件

public class YpFLowLayout extends ViewGroup {private List<Line> mLines = new ArrayList<YpFLowLayout.Line>(); // 用来记录描述有多少行Viewprivate Line mCurrrenLine; // 用来记录当前已经添加到了哪一行private int mHorizontalSpace = 20;private int mVerticalSpace = 10;public YpFLowLayout(Context context, AttributeSet attrs) {super(context, attrs);}public YpFLowLayout(Context context) {super(context);}public void setSpace(int horizontalSpace, int verticalSpace) {this.mHorizontalSpace = horizontalSpace;this.mVerticalSpace = verticalSpace;}@SuppressLint("DrawAllocation")@Overrideprotected 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添加到linemCurrrenLine.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);}@Overrideprotected 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<View> mViews = new ArrayList<View>(); // 用来记录每一行有几个Viewprivate float mMaxWidth; // 行最大的宽度private float mUsedWidth; // 已经使用了多少宽度private float mHeigth; // 行的高度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) {// 说还没有添加Viewif (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) {// 判断是否能添加Viewint 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 right = left + viewWidth;int bottom = top + viewHeight;view.layout(left, top, right, bottom);currentLeft += viewWidth + mHorizontalSpace;}}}}

    步骤2;在xml中添加

<xxxx.YpFLowLayout            android:id="@+id/fragment_attend_leave_flowlayout"            android:layout_width="match_parent"            android:layout_height="wrap_content" >        </xxxx.YpFLowLayout>


    步骤3:在activity或者fragment中先进行实例化,然后就可以通过addView的方式显示

private void showFlowLayout() {String leave_reason[] = BaseAttendFunction.leave_reason;for (int i = 0; i < leave_reason.length; i++) {TextView text = new TextView(getActivity());text.setText(leave_reason[i]);text.setId(i);text.setPadding(15, 10, 15, 10);text.setGravity(Gravity.CENTER_HORIZONTAL);if (isEdit && TOUCH_REASON_ID == i) {text.setSelected(true);tv_front = text;}LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);text.setBackgroundResource(R.drawable.tv_selector_bg);text.setLayoutParams(lp);text.setTextSize(14);text.setTextColor(Color.BLACK);text.setOnTouchListener(new mOnTouchListener());text.setFocusableInTouchMode(true);flowlayout.addView(text);}}class mOnTouchListener implements OnTouchListener {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {if (tv_front != null) {tv_front.setSelected(false);}v.requestFocus();tv_front = v;v.setSelected(true);TOUCH_REASON_ID = v.getId();return true;}return false;}}


补充:需要定义一个来记录当前点击的是那个TextView的View以及该View的ID或name.


附1 :tv_selector_bg.xml

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:drawable="@color/transparent" android:state_window_focused="false"/>    <!-- 触摸模式下单击时的背景图片 -->    <item android:drawable="@color/green_hui" android:state_focused="false" android:state_pressed="true"/>    <!-- 获得焦点时的图片背景 -->    <item android:drawable="@color/green_hui" android:state_focused="true"/>    <!-- 选中时的图片背景 -->    <item android:drawable="@color/green_hui" android:state_selected="true"/></selector> 

附2:

public static final String leave_reason[] = { "病假", "事假", "婚假", "丧假","产(检)假", "年休假", "带薪假", "工伤假", "公假" };public static final int leave_reason_value[] = { 146, 147, 148, 149, 150,151, 152, 153, 154 };


编辑进入,展示已保存的状态时:

for (int i = 0; i < BaseAttendFunction.leave_reason_value.length; i++) {if (xxxID == BaseAttendFunction.leave_reason_value[i]) {TOUCH_REASON_ID = i;break;}}

最后再来一张效果图:





    



0 0