[Android][SlidingMenu][Lollipop]SlidingMenuをLollipopで使うと、ContentViewがNavigationBarと被る。


[追記]解決策をいくつか追記しました。

今更、[DrawerLayout](https://developer.android.com/training/implementing-navigation/nav-drawer.html)を使っていないのがいけないのですが、、

# SlidingMenuとAppCompatでの問題。

一部のアプリで2年程前から、[SlidingMenu](https://github.com/jfeinstein10/SlidingMenu)というAndroidのドロワーメニューのライブラリを使っています。
最近アプリのLollipop対応の過程でAppCompatライブラリを使ったところ、ContentViewがNavigationViewとかぶってしまう、という問題に遭遇しました。

![test.png](https://qiita-image-store.s3.amazonaws.com/0/42594/d28a0e21-cfbc-f001-a0fc-0725fde86570.png)

左がkitkat、右がLollipopです。
setContentViewに入れるレイアウトでmatch_parentなViewの子供をlayout_gravityをBottomにしてみただけです。

これはSlidingMenuがDecorViewに直接Viewを追加していて、かつOSの仕様変更でDecorViewの範囲が変わったりActionBarの位置が変わったことに起因しているようです。

[Lollipop Bottom Nav Bar Overlays App When Moving Action Bar #680 ](https://github.com/jfeinstein10/SlidingMenu/issues/680)


# 解決策1（新）

簡単で確実な方法がありました。

[上記issueスレッドのコメント](https://github.com/jfeinstein10/SlidingMenu/issues/680#issuecomment-73912402)
[The getDecorView method return view include navigation bar view on lollipop？](http://stackoverflow.com/a/30664649)

DecorViewを触っているViewのConstructorで以下の記述をすれば良いようです。

```java
if(Build.VERSION.SDK_INT >= 21)
    setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
```

自分の場合は[SlidingMenu.java](https://github.com/jfeinstein10/SlidingMenu/blob/master/library/src/com/jeremyfeinstein/slidingmenu/lib/SlidingMenu.java)が該当クラスだったのでそのConstructorの初めに追記したところ、問題が解決しました。

詳しく調べていませんが、おそらくNavigationBar(ソフトキー)の背景にViewが配置できるかをコントロールしているものだと思います。
getDecorViewで取得できる範囲が、LollipopかつFLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSがtrueなWindowだとSystemBarやNavigationBarを含んでしまうのでしょうか。
そのため、View自身にNavigationBarと重複しないというFlagを立てて回避しているのだと思います。

時間があったらもう少し調べて見たいと思います。。

## デメリット
この方法のデメリットとしては、ライブラリに手を加える必要があるということでしょうか。。
Helperクラスなどを作って対処可能かもしれませんが、メンテされてないのでPRもできないようですので、ひとまずこちらで対処して他のライブラリへの移行を考えるべきかもしれません。

# 解決策2（古い）

~~上記スレでいろいろな解決策が議論されていますが、一番簡単なものを紹介します。~~
簡単な方法ですが、問題が一つありました。

* [The getDecorView method return view include navigation bar view on lollipop？](http://stackoverflow.com/questions/27165690/the-getdecorview-method-return-view-include-navigation-bar-view-on-lollipop/27535016#27535016)

```res/values-v21/styles.xml
<style name="Theme" parent="FrameworkRoot.Theme">
    <item name="android:windowDrawsSystemBarBackgrounds">false</item>
</style>

```

v21以上用のstyleを追加して、[windowDrawsSystemBarBackgrounds](http://developer.android.com/reference/android/R.attr.html#windowDrawsSystemBarBackgrounds)をfalseにしてしまう、という方法です。
このフラグは名前の通りかな、と思います。。

SystemBarとNavigationBarにどっちにも効いてしまうみたいなので、少し問題になることもあるかもしれませんが、[FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)を使えば動的にも変更できそうですね。。

## 問題点(追記)

この方法は簡単でかつライブラリのコードをいじらなくて済むのでいいのですが、一つ問題がありました。
SystemBarの背景に描画しない、というフラグを立ててしまうのでLollipopで可能になったSystemBarやNavigationBarの色をカスタマイズする、ということができなくなります。。

```xml:styles.xml
    <style name="AppTheme" parent="AppBaseTheme">
        <!-- colorPrimary is used for the default action bar background -->
        <item name="colorPrimary">@color/tone_actionbar_bg</item>
        <!-- colorPrimaryDark is used for the status bar -->
        <!-- これ以下の二つがうまく反映されない！ -->
        <item name="colorPrimaryDark">@color/tone_actionbar_bg</item>
        <item name="android:navigationBarColor">@color/tone_actionbar_bg</item>
        ...
    </style>
```

せっかくMaterial Designで画面全体を統合的にデザインできるようになったメリットが失われてしまうため、オススメできません。。

# 解決策3

[こちら](http://stackoverflow.com/a/27262388)の回答もあるように、NavigationBarのサイズを動的に求めて調整する方法もあります。

```java:MyActivity.java
private int getNavigationBarHeight() { 
    Resources resources = getResources();
    int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        return resources.getDimensionPixelSize(resourceId);
    } 
    return 0; 
} 

@Override 
public void onCreate(Bundle savedInstanceState) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        int navBarHeight = getNavigationBarHeight();
        findViewById(R.id.base_frame).setPadding(0, 0, 0, navBarHeight);
        findViewById(R.id.menu_frame).setPadding(0, 0, 0, navBarHeight);
    } 
} 
```

とりあえず解決できそうですが、あまりスマートでないのでオススメはしないでおきます。

# まとめ

当初は表現したいUIの違いから、DrawarLayoutに移行できていなかったのですが、特に深い理由がなければMaterial Designに準拠したり、開発が続行しているライブラリを使ったほうがいいでしょう。。
もしくは自分たちでパッチを当てていくなど。

今回のSlidingMenuも当時は結構メジャーだったのですが、個人プロジェクトのせいか、2年ほどメンテされてなかったり。。
似たようなこちらのライブラリはベンチャーチームのライブラリで最近も比較的更新されてるみたいですし。。。
[umano/AndroidSlidingUpPanel](https://github.com/umano/AndroidSlidingUpPanel)

Contributorの数も結構重要ですね。