美文网首页
Android自定义控件:自定义分隔栏控件

Android自定义控件:自定义分隔栏控件

作者: 超级绿茶 | 来源:发表于2020-05-12 11:20 被阅读0次
    Screenshot_1593337284.png

    源码SplitLayout.java

    package com.example.splitlayoutdemo;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.Configuration;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.graphics.drawable.Drawable;
    import android.graphics.drawable.StateListDrawable;
    import android.util.AttributeSet;
    import android.view.HapticFeedbackConstants;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class SplitLayout extends ViewGroup {
    
        static final String TAG = "SplitLayout";
        public static final int HORIZONTAL = 0;
        public static final int VERTICAL = 1;
    
        private static final float INVAID_SPLITPOSITION = Float.MIN_VALUE;
        private static final int DEFAULT_SPLIT_HANDLE_SIZE_DP = 15;//这个是控制上下拖动的 拖动时线的粗细.前提是把拖拽图片隐藏
        private static final int DEFAULT_CHILD_MIN_SIZE_DP = 1;//这个是各个方向拉伸 缩小的那边的最小值
        public static final int[] PRESSED_STATE_SET = {android.R.attr.state_pressed};
        public static final int[] EMPTY_STATE_SET = {};
    
        private int mOrientation;
    
        public float mSplitFraction;
        private float mSplitPosition = INVAID_SPLITPOSITION;
    
        public Drawable mHandleDrawable;
        private int mHandleSize;
        private boolean mHandleHapticFeedback;
    
        private View mChild0, mChild1;
        private float mLastMotionX, mLastMotionY;
        private int mChildMinSize;
        public int mWidth, mHeight;
    
        private boolean mIsDragging = false;
        private Callback touchCallback;
    
        public SplitLayout(Context context) {
            this(context, null, 0);
        }
    
        public SplitLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        private Context ctx;        //新增
        private AttributeSet att;  //新增
        private int def;  //新增
    
        public SplitLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            ctx = context;  //新增
            att = attrs;   //新增
            def = defStyleAttr;//新增
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SplitLayout, defStyleAttr, 0);
            mOrientation = a.getInteger(R.styleable.SplitLayout_splitOrientation, HORIZONTAL);
            mChildMinSize = a.getDimensionPixelSize(R.styleable.SplitLayout_splitChildMinSize,
                    dp2px(DEFAULT_CHILD_MIN_SIZE_DP));
    
            //defValue是控制左右比例,左边的占比
            mSplitFraction = a.getFloat(R.styleable.SplitLayout_splitFraction, 0.5f);
            checkSplitFraction();
            mHandleDrawable = a.getDrawable(R.styleable.SplitLayout_splitHandleDrawable);
            if (mHandleDrawable == null) {
                StateListDrawable stateListDrawable = new StateListDrawable();
                stateListDrawable.addState(new int[]{}, new ColorDrawable(Color.TRANSPARENT));
                mHandleDrawable = stateListDrawable;
            }
            mHandleDrawable.setCallback(this);
            mHandleSize = Math.round(a.getDimension(R.styleable.SplitLayout_splitHandleSize, 0f));
            if (mHandleSize <= 0) {
                mHandleSize = mOrientation == HORIZONTAL ?
                        mHandleDrawable.getIntrinsicWidth() : mHandleDrawable.getIntrinsicHeight();
            }
            if (mHandleSize <= 0) {
                mHandleSize = dp2px(DEFAULT_SPLIT_HANDLE_SIZE_DP);
            }
            mHandleHapticFeedback = a.getBoolean(R.styleable.SplitLayout_splitHandleHapticFeedback, false);
            a.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
            checkChildren();
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            if (widthSize > 0 && heightSize > 0) {
                mWidth = widthSize;
                mHeight = heightSize;  //修改   获取到的heightSize值有问题  特别小  应该是系统bug 所以外部自己重新赋值
                setMeasuredDimension(mWidth, mHeight);//修改
    
                checkSplitPosition();
                final int splitPosition = Math.round(mSplitPosition);
                if (mOrientation == VERTICAL) {
                    mChild0.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
                            MeasureSpec.makeMeasureSpec(splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY));
                    mChild1.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
                            MeasureSpec.makeMeasureSpec(heightSize - splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY));
                } else {
                    mChild0.measure(MeasureSpec.makeMeasureSpec(splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY),
                            MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));
                    mChild1.measure(
                            MeasureSpec.makeMeasureSpec(widthSize - splitPosition - mHandleSize / 2, MeasureSpec.EXACTLY),
                            MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY));
                }
            }
        }
    
        //初始化会调用,拖拽布局之后也会调用
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int w = r - l;
            int h = b - t;
            final int splitPosition = Math.round(mSplitPosition);
            if (mOrientation == VERTICAL) {
                mChild0.layout(0, 0, w, splitPosition - mHandleSize / 2);
                mChild1.layout(0, splitPosition + mHandleSize / 2, w, h);
            } else {
                mChild0.layout(0, 0, splitPosition - mHandleSize / 2, h);
                mChild1.layout(splitPosition + mHandleSize / 2, 0, w, h);
            }
        }
    
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (mHandleDrawable == null) {
                return false;
            }
            final int action = ev.getAction();
            float x = ev.getX();
            float y = ev.getY();
            switch (action & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    if (isUnderSplitHandle(x, y)) {
                        if (mHandleHapticFeedback) {
                            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                        }
                        if (mHandleDrawable != null) {
                            mHandleDrawable.setState(PRESSED_STATE_SET);//新增
                        }
                        mIsDragging = true;
                        getParent().requestDisallowInterceptTouchEvent(true);
                        invalidate();
                        if (touchCallback != null) touchCallback.onPress(SplitLayout.this);
                    } else {
                        mIsDragging = false;
                    }
                    mLastMotionX = x;
                    mLastMotionY = y;
                    break;
                }
                case MotionEvent.ACTION_MOVE:
                    if (mIsDragging) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                        if (mOrientation == VERTICAL) {
                            float deltaY = y - mLastMotionY;
                            updateSplitPositionWithDelta(deltaY);
                        } else {
                            float deltaX = x - mLastMotionX;
                            updateSplitPositionWithDelta(deltaX);
                        }
                        mLastMotionX = x;
                        mLastMotionY = y;
                        if (touchCallback != null) touchCallback.onMove(SplitLayout.this);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    if (mIsDragging) {
                        if (mHandleDrawable != null) {
                            mHandleDrawable.setState(EMPTY_STATE_SET);
                        }
                        if (mOrientation == VERTICAL) {
                            float deltaY = y - mLastMotionY;
                            updateSplitPositionWithDelta(deltaY);
                        } else {
                            float deltaX = x - mLastMotionX;
                            updateSplitPositionWithDelta(deltaX);
                        }
                        mLastMotionX = x;
                        mLastMotionY = y;
                        mIsDragging = false;
                        if (touchCallback != null) touchCallback.onRelease(SplitLayout.this);
                    }
                    break;
            }
            return mIsDragging;
        }
    
        private boolean isUnderSplitHandle(float x, float y) {
            int halfSize = mHandleSize == 0 ? 0 : mHandleSize / 2;
            if (mOrientation == VERTICAL) {
                return y >= (mSplitPosition - halfSize) && y <= (mSplitPosition + halfSize);
            } else {
                return x >= (mSplitPosition - halfSize) && x <= (mSplitPosition + halfSize);
            }
        }
    
        private void updateSplitPositionWithDelta(float delta) {
            mSplitPosition = mSplitPosition + delta;
            checkSplitPosition();
            requestLayout();
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            super.dispatchDraw(canvas);
            if (mSplitPosition != INVAID_SPLITPOSITION && mHandleDrawable != null) {
                final int splitPosition = Math.round(mSplitPosition);
                if (mOrientation == VERTICAL) {
                    mHandleDrawable.setBounds(0, splitPosition - mHandleSize / 2, mWidth, splitPosition + mHandleSize / 2);
                } else {
                    mHandleDrawable.setBounds(splitPosition - mHandleSize / 2, 0, splitPosition + mHandleSize / 2, mHeight);
                }
                mHandleDrawable.draw(canvas);
            }
        }
    
        private void checkSplitFraction() {
            if (mSplitFraction < 0) {
                mSplitFraction = 0;
            } else if (mSplitFraction > 1) {
                mSplitFraction = 1;
            }
        }
    
        private void checkSplitPosition() {
            if (mOrientation == VERTICAL) {
                if (mSplitPosition == INVAID_SPLITPOSITION) {
                    mSplitPosition = mHeight * mSplitFraction;
                }
                final int min = mChildMinSize + mHandleSize / 2;
                if (mSplitPosition < min) {
                    mSplitPosition = min;
                } else {
                    final int max = mHeight - mChildMinSize - mHandleSize / 2;
                    if (mSplitPosition > max) {
                        mSplitPosition = max;
                    }
                }
            } else {
                if (mSplitPosition == INVAID_SPLITPOSITION) {
                    mSplitPosition = mWidth * mSplitFraction;
                }
                final int min = mChildMinSize + mHandleSize / 2;
                if (mSplitPosition < min) {
                    mSplitPosition = min;
                } else {
                    final int max = mWidth - mChildMinSize - mHandleSize / 2;
                    if (mSplitPosition > max) {
                        mSplitPosition = max;
                    }
                }
            }
        }
    
        private void checkChildren() {
            if (getChildCount() == 2) {
                mChild0 = getChildAt(0);
                mChild1 = getChildAt(1);
            } else {
                throw new IllegalStateException("SplitLayout ChildCount must be 2.");
            }
        }
    
        @Override
        public void jumpDrawablesToCurrentState() {
            super.jumpDrawablesToCurrentState();
            if (mHandleDrawable != null) {
                mHandleDrawable.jumpToCurrentState();
            }
        }
    
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            updateSplitPositionWithDelta(0);
        }
    
        @Override
        protected boolean verifyDrawable(Drawable who) {
            return super.verifyDrawable(who) || who == mHandleDrawable;
        }
    
        private int dp2px(float dp) {
            return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f);
        }
    
        public int getSplitPosition() {
            return Math.round(mSplitPosition);
        }
    
        public View getChild0() {
            return mChild0;
        }
    
        public View getChild1() {
            return mChild1;
        }
    
        public void setCallback(Callback callback) {
            this.touchCallback = callback;
        }
    
        public interface Callback {
            void onPress(SplitLayout splitLayout);
    
            void onMove(SplitLayout splitLayout);
    
            void onRelease(SplitLayout splitLayout);
        }
    }
    

    自定义分隔条Drawable - splite_div.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="false">
            <layer-list>
                <item>
                    <shape>
                        <size android:width="15dp" android:height="15dp"/>
                        <solid android:color="@android:color/holo_blue_light" />
                    </shape>
                </item>
                <item android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp">
                    <shape>
                        <stroke android:width="0.5dp" android:color="@android:color/holo_orange_light" />
                        <corners android:radius="2.5dp" />
                        <size android:height="5dp" android:width="5dp"/>
                        <solid android:color="@android:color/holo_orange_dark" />
                    </shape>
                </item>
            </layer-list>
        </item>
        <item android:state_pressed="true">
            <layer-list>
                <item>
                    <shape>
                        <size android:width="15dp" android:height="15dp"/>
                        <solid android:color="@android:color/holo_blue_dark" />
                    </shape>
                </item>
                <item android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp">
                    <shape>
                        <stroke android:width="0.5dp" android:color="@android:color/white" />
                        <corners android:radius="2.5dp" />
                        <size android:width="5dp" android:height="5dp" />
                        <solid android:color="@android:color/darker_gray" />
                    </shape>
                </item>
            </layer-list>
        </item>
    </selector>
    

    创建资源参数文件 values/splitlayout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="SplitLayout">
            <attr name="splitOrientation" format="enum">
                <enum name="horizontal" value="0"/>
                <enum name="vertical" value="1"/>
            </attr>
            <attr name="splitChildMinSize" format="dimension"/>
            <attr name="splitFraction" format="float"/>
            <attr name="splitHandleDrawable" format="reference"/>
            <attr name="splitHandleSize" format="dimension"/>
            <attr name="splitHandleHapticFeedback" format="boolean"/>
        </declare-styleable>
    </resources>
    

    MainActivity.kt

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            splitTop.setCallback(object : SplitLayout.Callback {
                override fun onPress(splitLayout: SplitLayout) {
                    tvTop.text = getSplitInfo(splitLayout, 0)
                }
    
                override fun onRelease(splitLayout: SplitLayout) {
                    tvTop.text = getSplitInfo(splitLayout, 0)
                }
    
                override fun onMove(splitLayout: SplitLayout) {
                    tvTop.text = getSplitInfo(splitLayout, 0)
                }
            })
            splitBottom.setCallback(object : SplitLayout.Callback {
                override fun onPress(splitLayout: SplitLayout) {
                    tvLeft.text = getSplitInfo(splitLayout, 0)
                    tvRight.text = getSplitInfo(splitLayout, 1)
                }
    
                override fun onRelease(splitLayout: SplitLayout) {
                    tvLeft.text = getSplitInfo(splitLayout, 0)
                    tvRight.text = getSplitInfo(splitLayout, 1)
                }
    
                override fun onMove(splitLayout: SplitLayout) {
                    tvLeft.text = getSplitInfo(splitLayout, 0)
                    tvRight.text = getSplitInfo(splitLayout, 1)
                }
    
            })
        }
    
        private fun getSplitInfo(splitLayout: SplitLayout, which: Int): String {
            val size = when (which) {
                0 -> {
                    val width = splitLayout.child0.width
                    val height = splitLayout.child0.height
                    "width:$width\nheight:$height\n"
                }
                1 -> {
                    val width = splitLayout.child1.width
                    val height = splitLayout.child1.height
                    "width:$width\nheight:$height\n"
                }
                else -> ""
            }
            val position = splitLayout.splitPosition
            return size + "Split:$position"
        }
    }
    

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.example.splitlayoutdemo.SplitLayout
            android:id="@+id/splitTop"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:splitHandleDrawable="@drawable/splite_div"
            app:splitOrientation="vertical">
    
            <TextView
                android:id="@+id/tvTop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="1" />
    
            <com.example.splitlayoutdemo.SplitLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/splitBottom"
                app:splitHandleDrawable="@drawable/splite_div"
                app:splitOrientation="horizontal">
    
                <TextView
                    android:id="@+id/tvLeft"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:text="2" />
    
                <TextView
                    android:id="@+id/tvRight"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:text="3" />
            </com.example.splitlayoutdemo.SplitLayout>
        </com.example.splitlayoutdemo.SplitLayout>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    点击链接加入QQ群聊:https://jq.qq.com/?_wv=1027&k=5z4fzdT
    或关注微信公众号:口袋里的安卓

    相关文章

      网友评论

          本文标题:Android自定义控件:自定义分隔栏控件

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