版权声明:本文为博主原创文章,转载请说明出处。 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
}
}