SlidingMenu原始碼分析及拓展:監聽拉出選單時的滾動事件,將主頁變暗
這些效果以前都寫過,但是之前沒有積累的習慣,導致每次開始一個新專案時又要重新去翻閱資料、api,挺浪費時間的,所以打算記錄一下,養成好習慣。
SlidingMenu與DrawerLayout:
SlidingMenu是一個非常火的側拉選單開源元件,基本上99%的專案都要用上他,Material Design之後出了一個DrawerLayout抽屜元件,也非常好用,兩者對比了一下,DrawerLayout只能實現抽屜的效果,選單拉出時覆蓋在activity上面從而擋住了activity左邊的部分試圖,而slidingmenu的效果是推拉,會隨著選單的拉出慢慢的將activity平移推出螢幕,主要看需求吧。
效果演示
首先簡單回顧一下slidingmenu的基本用法吧,以程式碼為主,不過多解釋:
SlidingMenu menu = new SlidingMenu(this);
//設定菜單方向
menu.setMode(SlidingMenu.LEFT_RIGHT);
//設定滑動的範圍
menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
//設定滑動的寬度
menu.setBehindOffset(200);
//設定陰影部分的圖片
menu.setShadowDrawable(R.color.colorPrimaryDark);
menu.setShadowWidth(10);
//設定淡入淡出的效果
menu.setFadeEnabled(true);
menu.setFadeDegree(0.4f);
//附加到activity
menu.attachToActivity(this,SlidingMenu.SLIDING_CONTENT);
//設定左右滑動的選單
menu.setMenu(R.layout.slide_method_left);
這個不是今天的重點,側拉選單是初始化出來了,但是如果我要監聽選單的滑出的距離來實時的設定某些view的值,該怎麼實現呢?翻閱api及sliding原始碼,就這有這3個監聽,根本不能滿足我們的需要:
沒辦法只能去翻閱原始碼咯,原始碼太多,從哪開始入手呢,擒賊先禽王,誰滑動先找誰!
/**
* Set the behind view (menu) content to the given View.
*
* @param view The desired content to display.
*/
public void setMenu(View v) {
mViewBehind.setContent(v);
}
//找到初始化的地方
mViewBehind = new CustomViewBehind(context);
mViewAbove = new CustomViewAbove(context);
mViewBehind就是我們的選單view,那mViewAbove是什麼呢?其實看名字就已經猜到了,behind代表在下面的選單,那麼above應該是上面的主頁面,抱著這個想法我們去證實一下自己的猜測:
還記得menu.attachToActivity(this,SlidingMenu.SLIDING_CONTENT)這個方法嗎?是讓activity依附與slidingmenu,他有兩種模式:
case SLIDING_WINDOW:
mActionbarOverlay = false;
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
// save ActionBar themes that have transparent assets
decorChild.setBackgroundResource(background);
decor.removeView(decorChild);
decor.addView(this);
setContent(decorChild);
break;
case SLIDING_CONTENT:
mActionbarOverlay = actionbarOverlay;
// take the above view out of
ViewGroup contentParent = (ViewGroup)activity.findViewById(android.R.id.content);
View content = contentParent.getChildAt(0);
contentParent.removeView(content);
contentParent.addView(this);
setContent(content);
// save people from having transparent backgrounds
if (content.getBackground() == null)
content.setBackgroundResource(background);
break;
/**
* Set the above view content to the given View.
*
* @param view The desired content to display.
*/
public void setContent(View view) {
mViewAbove.setContent(view);
showContent();
}
程式碼中我們可以明確的看到,不管是那種模式,都是找到當前activity的根佈局,然後先把裡面的view刪掉,然後把this也就是slidingmenu放入根佈局中,最後將我們原來的這個主頁view設定給了mViewAbove!!是不是也就是證實了我們的猜測,mViewAbove就是我們的主頁,我們再找找mViewAbove、mViewBehind的一些相關事件,看能發現些什麼。
一路find,終於有了發現:
mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {
public static final int POSITION_OPEN = 0;
public static final int POSITION_CLOSE = 1;
public static final int POSITION_SECONDARY_OPEN = 2;
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
public void onPageSelected(int position) {
if (position == POSITION_OPEN && mOpenListener != null) {
mOpenListener.onOpen();
} else if (position == POSITION_CLOSE && mCloseListener != null) {
mCloseListener.onClose();
} else if (position == POSITION_SECONDARY_OPEN && mSecondaryOpenListner != null ) {
mSecondaryOpenListner.onOpen();
}
}
});
這個是不是很熟悉,有點想viewpage的滑動事件,就在這裡列印log試試效果吧!
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
Log.i("TAG","positionOffset:"+positionOffset+" , positionOffsetPixels:"+positionOffsetPixels);
}
列印結果:
對了,就是它了!當主頁向左滑動,偏移量數值在慢慢的減少,找到了根源就好辦事咯,我給他加一個滾動監聽回撥,不就能解決我們的問題了嗎,這是我自己加的程式碼:
private OnScrollDistanceListener mOnScrollDistanceListener;
public void setOnScrollDistanceListener(OnScrollDistanceListener listener){
this.mOnScrollDistanceListener = listener;
}
/**
* 監聽SlidingMenu移動距離
*/
public interface OnScrollDistanceListener{
void onPageScrolled( float positionOffset,
int positionOffsetPixels);
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
if(mOnScrollDistanceListener != null){
mOnScrollDistanceListener.onPageScrolled(Math.abs(positionOffset),Math.abs(positionOffsetPixels));
}
}
有了移動的距離,是不是該給主頁view設定半透明黑色遮障了,大家一定第一個想到的就是getWindow().getAttributes();然後設定alpha,這樣搞了你會發現,什麼鬼!我明明只想將滑出時的主頁變成灰色,為什麼我的選單也變灰了。要知道getWindow()指的是整個activity介面,在剛剛的原始碼中我們已經看到slidingmenu已經將選單和主頁都放在了activity中,那麼當然是全部都變灰了!!!
有人在想了,那我們來設定主頁的alpha行嗎?行,這會變色的範圍定位準了,但是alpha是從不透明到透明的一個過度,不透明的時候介面變成了白色,而不是我們需要的灰色,這怎麼搞?其實很簡單,用最原始的辦法,給主頁佈局最上層加一個view,大小與整個主頁相等,然後通過設定它的背景色和透明度來實現,雖然聽上去low一點,但是暫時真的沒有找到更好的方法來實現:
<!--遮障view-->
<View
android:id="@+id/v_filter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#494949"
android:visibility="gone"
/>
//滑動距離監聽
slidingMenu.setOnScrollDistanceListener(new SlidingMenu.OnScrollDistanceListener() {
@Override
public void onPageScrolled(float positionOffset, int positionOffsetPixels) {
setActivityFilter(positionOffset);
}
});
/**
* 設定activity濾鏡效果
*/
private void setActivityFilter(float positionOffset) {
float alpha = positionOffset * 0.7f;
iv_filter.setAlpha(alpha);
if (alpha <= 0) {
if (iv_filter.isShown())
iv_filter.setVisibility(View.GONE);
} else {
if (!iv_filter.isShown())
iv_filter.setVisibility(View.VISIBLE);
}
}