部分可见的侧边菜单与交叉渐变效果的SlidingPaneLayout

Gmail的平板版本有一个特色的功能:

gmail.gif

侧边菜单面板总是可见的,当关闭的时候只显示图标,当打开的时候会显示更详细的内容,并且两个内容切换的时候有个交叉渐变效果,是如何实现的呢?

我观察到主面板是在侧边面板打开的时候滑动的,因此应该不是用的NavigationDrawer,那我们试试SlidingPaneLayout如何。

SlidingPaneLayout

<android.support.v4.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <TextView
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/blue"
      android:text="@string/pane_1"/>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</android.support.v4.widget.SlidingPaneLayout>

two_pane_gray.gif

看起来还不错,不过主面板会变灰,幸运的是我们可以通过setSliderFadeColor将渐变色设置为透明。

SlidingPaneLayout layout = (SlidingPaneLayout) 
    findViewById(R.id.sliding_pane_layout);
layout.setSliderFadeColor(Color.TRANSPARENT);

two_pane.gif

部分展开的侧边

现在我想让侧边部分在关闭的时候是部分可见的,一开始摸不着头脑,但是一旦下手去做发现其实挺简单的:为主面板加一个margin(left)就可以了。

<android.support.v4.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <TextView
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/blue"
      android:text="@string/pane_1"/>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:layout_marginLeft="64dp"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</android.support.v4.widget.SlidingPaneLayout>

With the margin, the side pane peeks from below when collapsed.

交叉渐变

 最后是交叉渐变效果的实现。我将侧边部分换成了FrameLayout(使用`FrameLayout`类是为了让部分侧边内容和完全展示内容重合在一起),`FrameLayout`下面的TextView是主面板。

<com.sqisland.android.CrossFadeSlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <FrameLayout
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/purple">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/full"/>
    <TextView
        android:layout_width="64dp"
        android:layout_height="match_parent"
        android:background="@color/blue"
        android:text="@string/partial"/>
  </FrameLayout>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:layout_marginLeft="64dp"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</com.sqisland.android.CrossFadeSlidingPaneLayout>

然后我写了一个SlidingPaneLayout的子类以实现侧边栏在部分显示与完全显示之间的交叉渐变效果,为此,我首先的获取到这两部分。

@Override
protected void onFinishInflate() {
  super.onFinishInflate();
  if (getChildCount() < 1) {
    return;
  }
  View panel = getChildAt(0);
  if (!(panel instanceof ViewGroup)) {
    return;
  }
  ViewGroup viewGroup = (ViewGroup) panel;
  if (viewGroup.getChildCount() != 2) {
    return;
  }
  fullView = viewGroup.getChildAt(0);
  partialView = viewGroup.getChildAt(1);
  super.setPanelSlideListener(crossFadeListener);
}

 SlidingPaneLayout的第一个子view是侧边,侧边的第一个子view是完整内容的侧边,第二个子view是部分显示的侧边,它们在这里分别是fullView 和 partialView。然后利用SlidingPaneLayout的listener来根据偏移距离设置两部分的透明度。

private SimplePanelSlideListener crossFadeListener 
    = new SimplePanelSlideListener() {
  @Override
  public void onPanelSlide(View panel, float slideOffset) {
    super.onPanelSlide(panel, slideOffset);
    if (partialView == null || fullView == null) {
      return;
    }
    partialView.setVisibility(isOpen() ? View.GONE : VISIBLE);
    partialView.setAlpha(1 - slideOffset);
    fullView.setAlpha(slideOffset);
  }
};

因为我不想在展开的时候部分显示的侧边内容区域响应触摸事件,因此在展开的时候将它设置为不可见。注意在平板上可能初始状态也是展开的,所以需要在onLayout中也判断一下,然后设置可见状态。

@Override
protected void onLayout(
    boolean changed, int l, int t, int r, int b) {
  super.onLayout(changed, l, t, r, b);
  if (partialView != null) {
    partialView.setVisibility(isOpen() ? View.GONE : VISIBLE);
  }
}

cross_fade.gif

这就是gmail的那种效果了。

源码: https://github.com/chiuki/sliding-pane-layout