Android 自定义控件-自动换行的流线性布局-DragFlowLayout

效果图

这里写图片描述

步骤

1.继承RelativeLayout
2.复写onMeasure
3.复写onLayout
4.提供接口回调
5.使用ViewDragHelper实现对子控件拖拽

  • 继承RelativeLayout
public class DragFlowLayout extends RelativeLayout
  • 复写onMeasure
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        1.measure children and  measure  self
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0;
        int height = 0;

        int lineWidth = 0;
        int lineHeight = 0;


        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            MarginLayoutParams childLayoutParams = (MarginLayoutParams) child.getLayoutParams();
            int childMeasuredHeight = child.getMeasuredHeight() + childLayoutParams.bottomMargin + childLayoutParams.topMargin;
            int childMeasuredWidth = child.getMeasuredWidth() + childLayoutParams.leftMargin + childLayoutParams.rightMargin;

            if (lineWidth + childMeasuredWidth > widthSpecSize) {//The line is full
                width = Math.max(lineWidth, childMeasuredWidth);
                height = lineHeight + height;
                lineWidth = childMeasuredWidth;
                lineHeight = childMeasuredWidth;
            } else {
                //record line info
                lineWidth = lineWidth + childMeasuredWidth;
                lineHeight = Math.max(lineHeight, childMeasuredHeight);
            }

            if (i == childCount - 1) { //The last one
                width = Math.max(width, lineWidth);
                height += lineHeight;
            }

        }
//        2.set params
        setMeasuredDimension(widthSpecMode == MeasureSpec.EXACTLY ? widthSpecSize : width,
                heightSpecMode == MeasureSpec.EXACTLY ? heightSpecSize : height
        );

    }
  • 复写onLayout

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//        1.record how many views each row and   get max height of the row
        eachLineHeight.clear();
        allViews.clear();

        int childCount = getChildCount();
        int lineWidth = 0;
        int lineHeight = 0;
        List lineViews = new ArrayList<>();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
            int childMeasuredHeight = child.getMeasuredHeight() + layoutParams.bottomMargin + layoutParams.topMargin;
            int childMeasuredWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;


            if (lineWidth + childMeasuredWidth > getWidth()) {//The line is full
                lineWidth = 0;
                eachLineHeight.add(lineHeight);
                allViews.add(lineViews);
                lineViews = new ArrayList<>();
            }//The line is not full


            lineHeight = Math.max(lineHeight, childMeasuredHeight);
            lineWidth = lineWidth + childMeasuredWidth;
            lineViews.add(child);

            if (i == childCount - 1) {//The last one
                eachLineHeight.add(lineHeight);
                allViews.add(lineViews);
            }

            final int finalI = i;
            child.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mCallback != null) {// set callback
                        mCallback.onItemClick(finalI);
                    }
                }
            });
        }

        int left = 0;
        int top = 0;

        for (int i = 0; i < allViews.size(); i++) {

            List views = allViews.get(i);
            lineHeight = eachLineHeight.get(i);

//        2. layout   row
            for (int j = 0; j < views.size(); j++) {
                View child = views.get(j);
                if (child.getVisibility() == View.GONE) {
                    continue;
                }
                MarginLayoutParams lp = (MarginLayoutParams) child
                        .getLayoutParams();
                //calc childView's left,top,right,bottom
                int lc = left + lp.leftMargin;
                int tc = top + lp.topMargin;
                int rc = lc + child.getMeasuredWidth();
                int bc = tc + child.getMeasuredHeight();
                child.layout(lc, tc, rc, bc);

                left += child.getMeasuredWidth() + lp.rightMargin
                        + lp.leftMargin;
                Point point = new Point();
                point.x = lc;
                point.y = tc;
                child.setTag(point);
            }
            top = top + lineHeight;
            left = 0;
        }
    }

  • 对外提供接口回调
    //*********************about callback
    Callback mCallback;

    public void setOnItemClickCallback(Callback callback) {
        mCallback = callback;
    }

    public void setItemContent(int position, String content) {
        if (getChildAt(position) instanceof TextView) {
            TextView tv = (TextView) getChildAt(position);
            tv.setText(content);
            tv.requestLayout();
        }
    }

    public String getItemContent(int position) {
        String content = "";
        if (getChildAt(position) instanceof TextView) {
            TextView tv = (TextView) getChildAt(position);
            content = tv.getText().toString();
        }
        return content;
    }

    public interface Callback {
        void onItemClick(int position);
    }
  • 使用ViewDragHelper实现对子控件拖拽

    //*********************about view drag

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        if (mDragEnable) {
            return mViewDragHelper.shouldInterceptTouchEvent(ev);
        }

        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mDragEnable) {
            mViewDragHelper.processTouchEvent(event);
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            invalidate();
        }
    }

    @NonNull
    private ViewDragHelper.Callback createDrawCallback() {
        return new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                return true;
            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                //range
                int leftBound = getPaddingLeft();
                int rightBound = getWidth() - child.getWidth() - getPaddingRight();
                left = Math.min(Math.max(left, leftBound), rightBound);
                return left;
            }

            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                //range
                int topBound = getPaddingTop();
                int bottomBound = getHeight() - child.getHeight() - getPaddingBottom();
                top = Math.min(Math.max(topBound, top), bottomBound);
                return top;
            }

            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                Point point = (Point) releasedChild.getTag();
                mViewDragHelper.settleCapturedViewAt(point.x, point.y);
                invalidate();
            }

            //                    solve children onclick
            public int getViewHorizontalDragRange(View child) {
                return child.getWidth();
            }

            public int getViewVerticalDragRange(View child) {
                return child.getHeight();
            }
        };
    }

    public boolean isDragEnable() {
        return mDragEnable;
    }

    public void setDragEnable(boolean dragEnable) {
        mDragEnable = dragEnable;
    }

使用

  • 可在代码中动态添加子元素
mDragFlowLayout.addView(childView);
  • 也可在Xml布局中添加子元素
    
    
    
  • 如果不想子控件可以拖拽,可输入代码,true为可拖拽,false不可拖拽
    setDragEnable(false);

  • 设置子元素被点击
    setOnItemClickCallback回调

  • 得到子元素的内容
    getItemContent

  • 修改子元素的内容
    setItemContent

  • 修改子元素的内容
    setItemContent

  • 删除子元素
    removeItem

代码下载

https://github.com/agxxxx/DragFlowLayout

感谢&&参考

hongyang先生

你可能感兴趣的:(Android 自定义控件-自动换行的流线性布局-DragFlowLayout)