美文网首页UI
DragLayout可拖拉的自定义view

DragLayout可拖拉的自定义view

作者: dafaycoding | 来源:发表于2016-12-19 12:00 被阅读179次

    先上优雅又飘逸的运行效果

    darylayout_1.gif

    之前做题库项目的时候,有一个这样的需求,当题目是材料题的时候,上面是大段的文字,下面是几个选择题,这就要求上面的材料部分可以上下滚动查看,下面的选择题可以左右滑动查看,所以需要这样的自定义view。

    实现前的分析

    1. 要想同时改变上面部分和下面部分的高度,就不能用DrawLayout那样的侧滑View,因为DrawLayout底部和上面是互不影响的两层。
    2. 外层布局我用RelativeLayout,然后通过LayoutParams设置上面部分的高度,下面部分的即为外层RelativeLayout的高度减去上面部分高度
    3. 通过拖动中间的箭头图标来改变上面部分的高度。

    使用方式

    DragLayout所用到的布局

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <RelativeLayout
            android:id="@+id/rl_container_top"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </RelativeLayout>
        
        <ImageView
            android:id="@+id/iv_thumb"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/rl_container_bottom"
            android:layout_centerHorizontal="true"
            android:scaleType="fitStart"
            android:src="@mipmap/question_slide_stick_layout"
            android:visibility="gone" />
    
        <RelativeLayout
            android:id="@+id/rl_container_bottom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/rl_container_top"
            android:layout_marginTop="0dp"
            android:background="#f8f8f8"></RelativeLayout>
    </merge>
    

    DrayLayout用到的attrs

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="DragLayout">
    
            <attr name="layout_top" format="reference" />
            <attr name="layout_bottom" format="reference" />
            <attr name="questionType" format="enum">
                <enum name="choice" value="1" />
                <enum name="material" value="2" />
            </attr>
    
        </declare-styleable>
    
    </resources>
    

    由于DrayLayout的代码比较少,这里就直接粘了出来

    package com.example.idea.draglayout.view;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.RelativeLayout;
    
    import com.example.idea.draglayout.R;
    import com.example.idea.draglayout.utils.DpUtils;
    import com.example.idea.draglayout.utils.LogUtils;
    
    
    /**
     * Created by Deemo on 15/10/16.
     */
    public class DragLayout extends RelativeLayout {
    
        //默认高度
        private final int DEFAULT_HEIGHT_DP = 240;
    
        private RelativeLayout mRlContainerTop;
        private RelativeLayout mRlContainerBottom;
        private ImageView mIvThumb;
        private int resTop, resBottom;
        private int type;
        private Context mContext;
    
        //触发移动事件的最短距离,如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页
        private int mTouchSlop;
        private float mDownX, mDownY, mMoveY;
    
        private boolean isTouchThumb;
        private boolean isTendToMove;
    
        private float mThumbMinY;
    
        private int mBottomContentViewHeight;
        private int mLastBottomTop;
    
    
        public DragLayout(Context context) {
            this(context, null);
        }
    
        public DragLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            //自定义控件的属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DragLayout, defStyleAttr, 0);
            resTop = array.getResourceId(R.styleable.DragLayout_layout_top, 0);
            resBottom = array.getResourceId(R.styleable.DragLayout_layout_bottom, 0);
            setType(array.getInteger(R.styleable.DragLayout_questionType, 0));
            array.recycle();
    
            init(context);
        }
    
        private void init(Context context) {
            mContext = context;
            LayoutInflater.from(context).inflate(R.layout.view_draglayout, this, true);
            mRlContainerTop = (RelativeLayout) findViewById(R.id.rl_container_top);
            mRlContainerBottom = (RelativeLayout) findViewById(R.id.rl_container_bottom);
            mIvThumb = (ImageView) findViewById(R.id.iv_thumb);
            setTopAndBottomLayout(resTop, resBottom);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            initHeight();
        }
    
        private void initHeight() {
            float defaultHeight = DpUtils.dp2px(mContext.getResources(), DEFAULT_HEIGHT_DP);
            //通过LayoutParams来设置上面部分的高度
            ViewGroup.LayoutParams layoutParams = mRlContainerTop.getLayoutParams();
            layoutParams.height = (int) defaultHeight;
            mRlContainerTop.setLayoutParams(layoutParams);
        }
    
        public void setType(int type) {
            this.type = type;
        }
    
        public void setTopLayout(View view) {
            if (view != null) {
                mRlContainerTop.addView(view);
            }
        }
    
    
        public void setBottomLayout(final View view) {
            if (view != null) {
                mRlContainerBottom.addView(view);
                mIvThumb.setVisibility(View.VISIBLE);
    
                if (view.getMeasuredHeight() == 0) {
                    view.measure(0, 0);
                }
                mBottomContentViewHeight = view.getMeasuredHeight();
    
            } else {
                mRlContainerTop.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                mRlContainerBottom.setVisibility(View.GONE);
                mIvThumb.setVisibility(View.GONE);
            }
        }
    
        public void setTopAndBottomLayout(int top, int bottom) {
            if (top != 0) {
                View topView = LayoutInflater.from(getContext()).inflate(top, null);
                setTopLayout(topView);
            }
    
            if (bottom != 0) {
                View bottomView = LayoutInflater.from(getContext()).inflate(bottom, null);
                setTopLayout(bottomView);
            }
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            LogUtils.d(this, String.format("DragLayout w=%d, h=%d, ow=%d, oh=%d", w, h, oldw, oldh));
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = ev.getX();
                    mDownY = ev.getY();
                    mMoveY = mDownY;
                    isTouchThumb = isTouchThumb();
                    if (isTouchThumb())
                        return true;
                    return super.dispatchTouchEvent(ev);
                case MotionEvent.ACTION_MOVE:
                    float moveY = ev.getY();
                    if (isTouchThumb) {
                        float movedY = moveY - mMoveY;
                        if (!isTendToMove) {
                            isTendToMove = isTendToMove(movedY);
                        }
                        if (isTendToMove) {
                            tryToMoveThumb(movedY);
                            mMoveY = moveY;
                            return true;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    reset();
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        private void reset() {
            isTendToMove = false;
        }
    
        /**
         * 拖动图片的有效按压部分
         */
        private boolean isTouchThumb() {
            RectF rect = new RectF(mIvThumb.getLeft(), mIvThumb.getTop(), mIvThumb.getRight(), mIvThumb.getBottom());
            return rect.contains(mDownX, mDownY);
        }
    
        private boolean isTendToMove(float y) {
            return Math.abs(mDownY - y) >= mTouchSlop;
        }
    
        /**
         * 拖动范围限制,拖动的图片不能划出界面
         *
         * @param movedY
         */
        private void tryToMoveThumb(float movedY) {
            boolean canMove = (mIvThumb.getBottom() + movedY) < getHeight() && (mIvThumb.getTop() + movedY) > mThumbMinY;
            if (canMove) {
                mIvThumb.offsetTopAndBottom((int) movedY);
                adjustTopAndBottom((int) movedY);
                mLastBottomTop = mRlContainerBottom.getTop();
            }
        }
    
        /**
         * 改变上面部分高度
         *
         * @param movedY
         */
        public void adjustTopAndBottom(int movedY) {
            ViewGroup.LayoutParams layoutParams = mRlContainerTop.getLayoutParams();
            layoutParams.height += movedY;
            if (layoutParams.height < 0) {
                layoutParams.height = 0;
            }
            mRlContainerTop.setLayoutParams(layoutParams);
    
            LogUtils.d(this, "adjustTopAndBottom" + movedY + ", mRlytContainerTop=" + mRlContainerTop);
        }
    
    
        protected final int getBottomContentViewHeight() {
            return mBottomContentViewHeight;
        }
    
        protected int getBottomTop() {
            return mLastBottomTop;
        }
    
    }
    
    

    存在问题

    由于动态改变LayoutParams的高度,导致TopLayout和BottmoLayouy的子view的onMeasure()和onLayout方法不断执行,从而在2.3版本性能不好的安卓手机上测试,会有卡顿感觉。

    ok,基本完工了,后期如果有了更好的实现方式再来更新。


    源码地址 https://github.com/idea007/DragLayout

    相关文章

      网友评论

        本文标题:DragLayout可拖拉的自定义view

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