1. 程式人生 > >Android 自定義控制元件-自動換行的流線性佈局-DragFlowLayout

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<View> 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<View> 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佈局中新增子元素
    <com.dragflowlayout.www.ui.DragFlowLayout
        android:id="@+id/dfl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    </com.dragflowlayout.www.ui.DragFlowLayout>
  • 如果不想子控制元件可以拖拽,可輸入程式碼,true為可拖拽,false不可拖拽
    setDragEnable(false);

  • 設定子元素被點選
    setOnItemClickCallback回撥

  • 得到子元素的內容
    getItemContent

  • 修改子元素的內容
    setItemContent

  • 修改子元素的內容
    setItemContent

  • 刪除子元素
    removeItem

程式碼下載

感謝&&參考

hongyang先生