ViewDragHelper 实战案例2 《下拉抽屉菜单,微信小程序效果》 TopDrawerLayout

版权声明:本文为博主原创文章,转载请说明出处。 https://blog.csdn.net/qq_30889373/article/details/81841596

系列大纲

https://blog.csdn.net/qq_30889373/article/details/80538211


先来张效果图

这里写图片描述

红色部分为 抽屉,也就是隐藏在下面的布局
黑色部分为 正常布局, 是一个 NestedScrollView

demo

前面几章 已经清楚的说明了, ViewDragHelper的使用了,这里我们直接贴代码
给各位一点思路

xml中使用

    <com.xm.viewdemo.TopDrawerLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <View
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@color/colorAccent"/>

        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black">
            <LinearLayout
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />

                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
                <Button
                    android:text="测试"
                    android:layout_width="match_parent"
                    android:layout_height="200dp" />
            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>


    </com.xm.viewdemo.TopDrawerLayout>

自定义 FrameLayout

class TopDrawerLayout @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    companion object {
        /**
         * 拖拽百分比
         */
        const val DRAG_RANGE = 0.5f
    }

    // 显示的布局
    private lateinit var swipeLayout: View
    // 抽屉菜单
    private lateinit var drawerLayout: View
    // 抽屉最大拉开距离
    private var drawerRange: Int = 0
    // ViewDragHelper
    private val viewDragHelper by lazy {
        ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {
            override fun tryCaptureView(p0: View, p1: Int): Boolean {
                return true
            }

            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                Log.e("tag", "clampViewPositionVertical -> " + dy.toString())
                requestDisallowInterceptTouchEvent(true)
                if (child == swipeLayout) {
                    if (top < 0) {
                        return 0
                    } else if (top > drawerRange) {
                        return drawerRange
                    }
                } else if (child == drawerLayout) {
                    if (top > 0) {
                        return 0

                    } else if (top > drawerRange) {
                        return drawerRange
                    }
                }
                if (isScrollTop()){
                    if (child is NestedScrollView){
                        child.scrollBy(0,-top)
                    }
                    return 0
                }
                return top
            }

            override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
                requestDisallowInterceptTouchEvent(true)
                Log.e("tag", "onViewReleased -> " + yvel.toString())
                if (yvel.toInt() == 0 && swipeLayout.top > drawerRange * DRAG_RANGE) {
                    open()
                } else if (yvel > 0) {
                    open()
                } else {
                    close()
                }
            }

            override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
                if (changedView == swipeLayout) {
                    drawerLayout.offsetTopAndBottom(dy)
                } else if (changedView == drawerLayout) {
                    swipeLayout.offsetTopAndBottom(dy)
                }
                // 兼容低版本
                invalidate()
            }

            override fun getViewHorizontalDragRange(child: View): Int = 1
            override fun getViewVerticalDragRange(child: View): Int = 1

        })
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        // 布局是否符合规范
        if (childCount < 2) {
            throw IllegalArgumentException("you need 2 children view")
        }
        drawerLayout = getChildAt(0)
        swipeLayout = getChildAt(1)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        //布局 swipe
        val swipeRect = getLayoutSwipeRect(false)
        swipeLayout.layout(swipeRect.left, swipeRect.top, swipeRect.right, swipeRect.bottom)
        //布局 drawer
        val drawerRect = getLayoutDrawer(swipeRect)
        drawerLayout.layout(drawerRect.left, drawerRect.top, drawerRect.right, drawerRect.bottom)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        drawerRange = drawerLayout.measuredWidth
    }

    /**
     * 获取 layoutSwipe 布局的位置
     * @param isOpen  是否打开抽屉
     * @return 布局的区域
     */
    private fun getLayoutSwipeRect(isOpen: Boolean): Rect {
        val paddingLeft = paddingLeft
        val paddingRight = paddingRight
        val paddingTop = paddingTop
        val paddingBottom = paddingBottom
        return if (isOpen) {
            //打开抽屉的状态
            val right = width - paddingLeft - paddingRight
            val bottom = height - paddingTop - paddingBottom
            Rect(paddingLeft, paddingTop + drawerRange, right, bottom)
        } else {
            //关闭抽屉状态
            val right = width - paddingLeft - paddingRight
            val bottom = height - paddingTop - paddingBottom
            Rect(paddingLeft, paddingTop, right, bottom)
        }
    }

    /**
     * 获取 layoutDrawer 位置
     * @param  swipeRect        swipe的位置,因为我们要出现再它后面
     * @return 布局的区域
     */
    private fun getLayoutDrawer(swipeRect: Rect): Rect {
        val top = swipeRect.top - drawerRange
        return Rect(swipeRect.left, top, swipeRect.right, swipeRect.top)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return viewDragHelper.shouldInterceptTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        viewDragHelper.processTouchEvent(event)
        return true
    }

    override fun computeScroll() {
        if (viewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    /**
     * 打开抽屉
     */
    fun open() {
        if (viewDragHelper.smoothSlideViewTo(swipeLayout, 0, drawerRange)) {
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    /**
     * 关闭抽屉
     */
    fun close() {
        if (viewDragHelper.smoothSlideViewTo(swipeLayout, 0, 0)) {
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    /*
     * 判断是否划动到了底部
     */
    private fun isScrollTop(): Boolean {
        if (swipeLayout is NestedScrollView) {
            return swipeLayout.scrollY > 0
        }
        return false
    }

}

猜你喜欢

转载自blog.csdn.net/qq_30889373/article/details/81841596
今日推荐