SlidingUpPanelLayout:可以上下滑动的菜单布

前言

这几天正在研究几个比较有名的开源项目,偶然发现了SlidingUpPanelLayout这个神器,经过一番研究,和大家一起分享一下学习心得。

如果要使用滑动菜单,目前最流行的应该是DrawerLayout,这个由谷歌开源的控件已经被放在android包中,使用起来非常的方便,唯一的遗憾是DrawerLayout只支持左右滑动的菜单,但是并不支持上下滑动的菜单,我们今天要介绍的SlidingUpPanelLayout,相当于竖向的DrawerLayout。

首先我们看一个动画:


在这里插入图片描述

很多的音乐播放器都有这样的交互,使用SlidingUpPanelLayout就可以实现。

SlidingUpPanelLayout早在2016年就已经发布了,肯定已经非常的稳定,大家可以到github上先看看基本的使用方法。

https://github.com/umano/AndroidSlidingUpPanel

正文

首先我们需要在build.gradle文件中依赖SlidingUpPanelLayout:

dependencies {
    repositories {
        mavenCentral()
    }
    compile 'com.sothree.slidinguppanel:library:3.4.0'
}

我们写一个最简单的布局:

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <LinearLayout 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"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:text="button"
            android:layout_height="wrap_content"/>

        <com.sothree.slidinguppanel.SlidingUpPanelLayout
            android:id="@+id/slidingUpPanelLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="bottom"
            app:umanoOverlay="true"
            app:umanoFadeColor="@color/colorAccent"
            app:umanoPanelHeight="95dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorPrimary"
                android:gravity="center"
                android:clickable="true"
                android:focusable="false"
                android:focusableInTouchMode="true"
                android:text="content" />

            <RelativeLayout
                android:id="@+id/panel"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:ellipsize="end"
                    android:gravity="center"
                    android:text="@string/content"
                    android:textColor="@color/white" />
                                
            </RelativeLayout>

        </com.sothree.slidinguppanel.SlidingUpPanelLayout>

    </LinearLayout>
</layout>
在这里插入图片描述

仅仅定义xml,一个可以上滑的菜单就完成了。接下来我们看看SlidingUpPanelLayout都提供了哪些方法。

setPanelHeight

setPanelHeight对应自定义属性umanoPanelHeight。

当菜单被折叠时,显示的高度

上面的demo中已经使用了这个自定义属性,非常好理解。

setOverlayed

setOverlayed对应自定义属性umanoOverlay。

意思是菜单是否要覆盖到主页面之上:
如果是false,相当于LinearLayout,主页面在上,菜单在下
如果是true,相当于FrameLayout

默认是false类型,具体按照UE的需求自行选择。

setClipPanel

setClipPanel对应自定义属性umanoClipPanel。

是否要裁剪菜单的区域,特别注意,此属性只会在 Overlayed = false的时候有效
如果是true,会把被菜单遮盖的区域,主页面的内容会被裁掉。
如果是false,菜单的内容和主页面的内容会同时显示,覆盖在上面。

这个属性,我猜测主要是为了优化绘制速度的,如果你的菜单有不透明背景,建议设置为true,缩小主页面绘制的区域范围,提高流畅度。

setDragView

setDragView对应自定义属性umanoDragView。

从字面的意思来看,就是可以拖拽的View,如果你不希望整个菜单都可以被手势展开或折叠,可以设置某一个View作为响应手势的热区。

setParallaxOffset

setParallaxOffset对应自定义属性umanoParallaxOffset。

当菜单在滑动的时候,主页面一起移动的视觉偏差。

例如菜单展开的过程中,主页面向上移动100dp,视觉上有一种主页面被顶上去的效果。

setFadeOnClickListener

setFadeOnClickListener没有对应的自定义属性。

如果你在滑动的过程中,点击了菜单以外的部分,你会发现菜单的展开或折叠动画被中断了,如果你希望在点击的时候做什么处理,你可以设置setFadeOnClickListener。不过此处有坑,那就是你的主页面必须是可点的,以刚才的demo为例:

    <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorPrimary"
                android:gravity="center"
                android:clickable="true"
                android:focusable="false"
                android:focusableInTouchMode="true"
                android:text="content" />

我设置的主页面只是一个TextView,TextView默认是不可点击的,所以无论怎么点setFadeOnClickListener都不会触发,所以需要手动加上android:clickable=true,才会响应setFadeOnClickListener。

setCoveredFadeColor

setCoveredFadeColor对应自定义属性umanoFadeColor。

菜单覆盖在主页面上的背景颜色,此颜色可以被rootView的背景颜色盖住,也可以理解为阴影的颜色

setAnchorPoint

setAnchorPoint对应自定义属性umanoAnchorPoint。

自定义的菜单展开的锚点比例。值为0-1之间的小数,非手动拖拽菜单的情况下(例如点击)只会展开到这个锚点的比例。

例如,setAnchorPoint(0.5f),菜单只会自动展开一半。

setPanelState

设置菜单的展开状态,目前主要有:

    public enum PanelState {
        EXPANDED,  // 展开
        COLLAPSED,   // 折叠
        ANCHORED,    // 锚点
        HIDDEN,         // 隐藏
        DRAGGING    // 拖拽中(个人觉得理解为变化中更合适)
    }

与之相关属性有umanoInitialState:菜单的初始状态。

setScrollableView

setScrollableView对应的自定义属性为umanoScrollableView。

可以滚动的Child。如果内部有ScrollView,ListView,RecyclerView,通过这个方法,会把手势的滑动让给他们,从而避免SlidingUpPanelLayout和child之间的手势冲突。

SlidingUpPanelLayout只能设置一个child作为ScrollableView。

setScrollableViewHelper

setScrollableViewHelper没有对应的自定义属性。

他的作用和setScrollableView类似,只不过setScrollableViewHelper更具有通用性,可以对的每一个View自定义手势处理:

public int getScrollableViewScrollPosition(View scrollableView, boolean isSlidingUp) {
        if (scrollableView == null) return 0;
        if (scrollableView instanceof ScrollView) {
            ...
        } else if (scrollableView instanceof ListView && ((ListView) scrollableView).getChildCount() > 0) {
            ...
        } else if (scrollableView instanceof RecyclerView && ((RecyclerView) scrollableView).getChildCount() > 0) {
            ....
        } else {
            return 0;
        }
    }

ScrollableViewHelper内部已经处理了ScrollView,ListView,RecyclerView,能够满足大部分的需求。

addPanelSlideListener

addPanelSlideListener是SlidingUpPanelLayout最核心的api,监听菜单展开的进度。他的用法和动画的的监听基本一样:

SlidingUpPanelLayout.PanelSlideListener {
            override fun onPanelSlide(panel: View?, slideOffset: Float) {
                    // panel :就是菜单的根布局
                    // slideOffset:菜单展开的比例,值为0到1,0为折叠状态,1为展开状态
            }

            override fun onPanelStateChanged(
                panel: View?,
                previousState: SlidingUpPanelLayout.PanelState?,
                newState: SlidingUpPanelLayout.PanelState?
            ) {
                // newState:目前SlidingUpPanelLayout的状态
            }
        }

EXPANDED, // 展开
COLLAPSED, // 折叠
ANCHORED, // 锚点,这个状态只有设置了锚点才会有
HIDDEN, // 隐藏
DRAGGING // 拖拽中(个人觉得理解为变化中更合适),无论是点击还是拖拽,只要是菜单在滑动,都会进入这个状态。

总结

关于SlidingUpPanelLayout的所有内容到此结束了,几乎所有的api我们都已经介绍了,但是具体的效果建议大家自己去尝试,尤其是ClipPanel和Overlayed,有什么问题可以留言讨论。

推荐阅读更多精彩内容

  • 01 “他们有明亮的眼睛,却拒绝对视; 有正常听力,却不理会呼唤; 有清脆的嗓音,却不歌唱交谈… 他们,是星星的孩...
    兰若荃阅读 561评论 0 4
  • 好多话,忍着,憋着,后来就懒得说了, 好多事情,失望多了,后来就不在意了, 就是这样,在一次一次失望中, 后来,突...
    宝a宝a阅读 43评论 0 0
  • 2017-12-21 姓名:徐祖德 公司:广东思沃精密机械有限公司 230期_利他1组 272期_乐观2组志工 【...
    徐祖德阅读 81评论 0 0
  • 时光时光慢些吧 不要再让他变老了 …… 每次听到《父亲》这首歌的时候总会突然很难过。可能是因为第一次看到MV的时候...
    橙汁的妈妈阅读 264评论 2 2