最近在项目中需要使用到类似托盘的滚动效果,github search后找到了AndroidSlidingUpPanel。
github地址:点击打开链接
github上作者对该项目的介绍如下:
This library provides a simple way to add a draggable sliding up panel (popularized by Google Music and Google Maps) to your Android application. Brought to you by Umano.
大致的意思就是该库提供了一个简单的方法在你的项目中使用可拖动向上滑动面板。
一、该控件的使用方式:
(1)在你moudle的gradle中引用:
compile 'com.sothree.slidinguppanel:library:3.3.0'
(2)使用com.sothree.slidinguppanel.SlidingUpPanelLayout作为活动布局的根元素。
(3)这个元素必须把gravity
设置为top 或bottom
(4)确保你有两个子元素 第一个是main layout 第二个是你要滑到上面的托盘 layout
(5)main layout 必须把宽和高设置为 match_parent
(6)滑到上面的layout必须把宽设置为 match_parent,而高 或者为 match_parent 或者为 最大的任意高度
二、Xml中配置:
<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:sothree="http://schemas.android.com/apk/res-auto" android:id="@+id/sliding_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom" sothree:umanoPanelHeight="68dp" sothree:umanoShadowHeight="4dp"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="Main Content" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center|top" android:text="The Awesome Sliding Up Panel" android:textSize="16sp" /> </com.sothree.slidinguppanel.SlidingUpPanelLayout>如果 当向上滑动的时候actionbar也是跟着慢慢隐藏的,这种效果必须使用
ActionBarOverlay:
<style name="AppTheme"> <item name="android:windowActionBarOverlay">true</item> </style>
同时这种情况你需要为主区域的布局设置margintop为actionbar的高度:
?android:attr/actionBarSize
还需要在代码中动态的改变actionbar:
public void setActionBarTranslation(float y) {
// Figure out the actionbar height int actionBarHeight = getActionBarHeight(); // A hack to add the translation to the action bar ViewGroup content = ((ViewGroup) findViewById(android.R.id.content).getParent()); int children = content.getChildCount(); for (int i = 0; i < children; i++) { View child = content.getChildAt(i); if (child.getId() != android.R.id.content) { if (y <= -actionBarHeight) { child.setVisibility(View.GONE); } else { child.setVisibility(View.VISIBLE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { child.setTranslationY(y); } else { AnimatorProxy.wrap(child).setTranslationY(y); } } } }
}
三、属性介绍
(1)您可以通过使用setPanelHeight方法或属性umanoPanelHeight改变面板的高度。
(2)如果你想隐藏的滑动面板上方的阴影,设置属性shadowHeight为0。
(3)使用的setEnabled(假)完全禁用的滑动面板(包括触摸和滑动编程)
(4)使用setTouchEnabled(false)来禁用面板的触摸响应速度(阻力和点击),你仍然可以控制面板。
(5)使用getPanelState来获得当前面板的状态
(6)使用setPanelState设置当前面板的状态
(7)您可以通过设置umanoParallaxOffset属性添加视差主视图
(8)可以使用setAnchorPoint以允许面板的中间展开状态(类似于谷歌地图)在屏幕的中间设置一个锚点。
(9)您可以设置PanelSlideListener监控左右推拉窗格事件。
(10)您也可以通过更改布局顶部的layout_gravity属性使得从顶部面板滑出。
(10)通过设置umanoScrollInterpolator属性为面板移动滚动插补器。举例来说,如果你想为面板反弹或超调的效果。
(11)默认情况下,面板推高的主要内容。你可以把它叠加使用setOverlayed方法或属性umanoOverlay主要内容。这是有用的,如果你想使滑动布局半透明。您还可以设置umanoClipPanel为false,使面板非叠加模式透明。
(12)默认情况下,当面板向上滑动的主要内容变暗。您可以通过更改umanoFadeColor改变暗淡的颜色。将其设置为“@android:彩色/透明”以彻底删除调光。
四、嵌套滚动视图
如果你要在内容中嵌套上下滚动的内容视图,例如ListView、RecyclerView等等。你需要去处理事件的拦截和分发。不过作者为我们提供了一个Helper类来帮助我们简化程序,只需要我们继承ScrollableViewHelper然后使用setScrollable方法将我们自定义的helper设置到滑动面板上即可。
五、分析源码
作者在最新的版本修改的很多的地方,两个明显的地方:
(1)setPanelSlideListener的方法改成了addPanelSlideListener,并且提供了一个事件适配器SimplePanelSlideListener。
(2)smoothSlideTo 方法 由public改为了默认的访问方式:default或者说package。
1.下面来看PanelSlideListener的源码:
/** * Listener for monitoring events about sliding panes. */ public interface PanelSlideListener { /** * Called when a sliding pane's position changes. * * @param panel The child view that was moved * @param slideOffset The new offset of this sliding pane within its range, from 0-1 */ public void onPanelSlide(View panel, float slideOffset); /** * Called when a sliding panel state changes * * @param panel The child view that was slid to an collapsed position */ public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState); }
提供了两个方法:
(1) onPanelSlide:滑动面板的位置改变。
(2) onPanelStateChanged:滑动面板的状态改变。
下面是该事件的适配器:
/** * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset * of the listener methods you can extend this instead of implement the full interface. */ public static class SimplePanelSlideListener implements PanelSlideListener { @Override public void onPanelSlide(View panel, float slideOffset) { } @Override public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) { } }
2.smoothSlideTo:
/** * Smoothly animate mDraggingPane to the target X position within its range. * * @param slideOffset position to animate to * @param velocity initial velocity in case of fling, or 0. */ boolean smoothSlideTo(float slideOffset, int velocity) { if (!isEnabled() || mSlideableView == null) { // Nothing to do. return false; } int panelTop = computePanelTopPosition(slideOffset); if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), panelTop)) { setAllChildrenVisible(); ViewCompat.postInvalidateOnAnimation(this); return true; } return false; }
方法修饰变成了默认的。即我们不能再用SlidingUpPanelLayout去调用smoothSlideTo方法来移动panel。只能使用setPanelHeight来改变。我们来看setPanelHeight方法:
/** * Set the collapsed panel height in pixels * * @param val A height in pixels */ public void setPanelHeight(int val) { if (getPanelHeight() == val) { return; } mPanelHeight = val; if (!mFirstLayout) { requestLayout(); } if (getPanelState() == PanelState.COLLAPSED) { smoothToBottom(); invalidate(); return; } }该方法根据一个像素值去改变面板的高度。
PanelState提供了面板的几个状态:
/** * Current state of the slideable view. */ public enum PanelState { EXPANDED, COLLAPSED, ANCHORED, HIDDEN, DRAGGING }(1)EXPANDED : 全部展开状态
(2)COLLAPSED:默认状态
(3)ANCHORED:锚点
(4)HIDDEN:隐藏状态
(5)DRAGGING:拖动状态
从源码可以看到,默认的状态是:
/** * Default initial state for the component */ private static PanelState DEFAULT_SLIDE_STATE = PanelState.COLLAPSED;
protected void smoothToBottom() { smoothSlideTo(0, 0); }可以看到smoothToBottom方法最终还是调用了smoothSlideTo()。
看设置状态的方法:
/** * Change panel state to the given state with * * @param state - new panel state */ public void setPanelState(PanelState state) { if (state == null || state == PanelState.DRAGGING) { throw new IllegalArgumentException("Panel state cannot be null or DRAGGING."); } if (!isEnabled() || (!mFirstLayout && mSlideableView == null) || state == mSlideState || mSlideState == PanelState.DRAGGING) return; if (mFirstLayout) { setPanelStateInternal(state); } else { if (mSlideState == PanelState.HIDDEN) { mSlideableView.setVisibility(View.VISIBLE); requestLayout(); } switch (state) { case ANCHORED: smoothSlideTo(mAnchorPoint, 0); break; case COLLAPSED: smoothSlideTo(0, 0); break; case EXPANDED: smoothSlideTo(1.0f, 0); break; case HIDDEN: int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight); smoothSlideTo(computeSlideOffset(newTop), 0); break; } } }想必大家都恍然大悟了。
ok,AndroidSlidingUpPanel的基本使用就介绍到这里,相信大家也对该功能的使用有了非常清楚的了解,接下来我将带领大家一起做个小Demo来巩固。
首先,先来看不同状态下的效果:
(1)COLLAPSED:
(2)EXPANDED:
(3)只显示按钮的状态:
原理很简单,就是监听panel的state并且根据panel的高度来改变即可实现。从上图中可以发现,在panel中我使用了一个列表来显示。为了解决滑动冲突,我们可以使用
app:umanoScrollableView="@+id/lv_list"来指向我们的列表即可。
来看核心代码:
slidinglayout.addPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener(){ @Override public void onPanelSlide(View panel, float slideOffset) { //*当滑动面板的位置变化。 } @Override public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) { //面板状态改变 currentState = newState; if(newState == PanelState.EXPANDED) { isExpanded = true; } if(newState == PanelState.COLLAPSED){ isExpanded = false; } } });
Demo下载