美文网首页
Android 自定义Layout 实现ChildView的动态

Android 自定义Layout 实现ChildView的动态

作者: _年少 | 来源:发表于2019-08-18 21:50 被阅读0次

    效果演示

    演示

    关键代码

    1. 创建一个DynamicLayout继承ViewGroup

    2. 实现onMeasure方法

        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //测量自己
            int maxWidth = 0;
            int maxHeight = 0;
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                    maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                }
            }
            setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), resolveSize(maxHeight, heightMeasureSpec));
    
            //测量childView
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    
                final int childWidthMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    final int width = Math.max(0, getMeasuredWidth() - lp.leftMargin - lp.rightMargin);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin, lp.width);
                }
    
                final int childHeightMeasureSpec;
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    final int height = Math.max(0, getMeasuredHeight() - lp.topMargin - lp.bottomMargin);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
                }
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    
    1. 实现onLayout方法
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    
                    final int width = child.getMeasuredWidth();
                    final int height = child.getMeasuredHeight();
    
                    int childLeft = lp.leftMargin;
                    int childTop = lp.topMargin;
    
                    child.layout(childLeft, childTop, childLeft + width, childTop + height);
                }
            }
        }
    
    1. 拦截TouchEvent事件
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return true;
        }
    
    1. 实现onTouchEvent方法
        public boolean onTouchEvent(MotionEvent event) {
            Log.d(TAG, "onTouchEvent: " + event.getAction());
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //处理触摸按下事件
                    handleTouchDown(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    //处理触摸移动事件
                    handleTouchMove(event);
                    break;
            }
    
            return true;
        }
    
        private void handleTouchMove(MotionEvent event) {
            if (mSelectedChild == null) {
                return;
            }
    
            float x = event.getX();
            float y = event.getY();
    
            MarginLayoutParams lp = (MarginLayoutParams) mSelectedChild.getLayoutParams();
            //计算view新的x轴位置并且防止超出左右边界
            int childX = (int) (x - mTouchOffsetX);
            if (childX < 0) {
                lp.leftMargin = 0;
            } else {
                int maxX = getMeasuredWidth() - mSelectedChild.getMeasuredWidth();
                lp.leftMargin = childX > maxX ? maxX : childX;
            }
            //计算view新的y轴位置并且防止超出上下边界
            int childY = (int) (y - mTouchOffsetY);
            if (childY < 0) {
                lp.topMargin = 0;
            } else {
                int maxY = getMeasuredHeight() - mSelectedChild.getMeasuredHeight();
                lp.topMargin = childY > maxY ? maxY : childY;
            }
            //重新布局 刷新位置
            requestLayout();
        }
    
        private void handleTouchDown(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            setViewTranslationZ(0);
            mSelectedChild = null;
            int count = getChildCount();
            for (int i = count - 1; i >= 0; i--) {
                View child = getChildAt(i);
                //获取View上下左右的坐标
                Rect rect = getChildRect(child);
                //通过判断按下位置是否在View之内进行选中View
                if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
                    //将选中View存为成员变量供Move事件使用
                    mSelectedChild = child;
                    setViewTranslationZ(mTranslationZ);
                    //计算出按下位置相对于选中View的坐标并存为成员变量供Move事件使用
                    mTouchOffsetX = x - rect.left;
                    mTouchOffsetY = y - rect.top;
                    break;
                }
            }
        }
    
        //获取View的上下左右坐标
        private Rect getChildRect(View child) {
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            return new Rect(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(), lp.topMargin + child.getMeasuredHeight());
        }
    

    Github

    相关文章

      网友评论

          本文标题:Android 自定义Layout 实现ChildView的动态

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