同步發表於http://avenwu.net/2015/02/24/custom_slide_panel_layout_as_reside_style_on_dribble_and_qq
Fork on github https://github.com/avenwu/support
14年的時候曾經在csdn上看到過一篇介紹Android中ResideMenu實現的推薦項目,也分析了一把,詳見Android控件源碼分析--AndroidResideMenu菜單
,那時候QQ還沒有redisemenu的效果。效果還是不錯的,但是有一個確定就是不支持平滑拖動時對菜單,容器的控制,只是簡單的動畫實現。
項目靈感據說是來自於dribble網站上的兩個交互設計原型:
相關鏈接如下:
1116265-Instasave-iPhone-App
https://dribbble.com/shots/1114754-Social-Feed-iOS7
現在QQ的側滑ResideMenu效果和上面非常類似,同時支持平滑拖動的處理;
網絡上也有一些高仿QQ策划效果的實現,比如基於HorizonalScrollView的,基於SlideMenu修改的,都是不錯的思路;本文打算基於android 官方v4包進行擴展,還是更偏向使用官方的東西:)
下面是qq效果和自定義的效果:
實現
基於v4擴展包的SlidePanelLayout可以比較方便就實現需要的效果。
核心在於對互動狀態的處理,SlidePanelLayout有一個setPanelSlideListener監聽,類似於onScroll,通過這個回調可以很容易知道當前歡動的百分比,從而改變視圖的位置,實現動畫中效果。
需要注意的是這里用到了level 11以后的方法,所以如果要兼容11以前的設備可以用nineoldedroid做修改。
package net.avenwu.support.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.widget.SlidingPaneLayout;
import android.util.AttributeSet;
import android.view.View;
/**
* Support level 11 and later;
* TODO use nineolddroid for devices under level 11
* <p/>
* Created by chaobin on 2/18/15.
*/
@TargetApi(11)
public class CustomSlidePanelLayout extends SlidingPaneLayout {
protected View mMenuPanel;
protected float mSlideOffset;
protected boolean isCustomable = false;
public CustomSlidePanelLayout(Context context) {
this(context, null);
}
public CustomSlidePanelLayout(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public CustomSlidePanelLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
isCustomable = true;
setPanelSlideListener(new SimplePanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {
mSlideOffset = slideOffset;
if (mMenuPanel == null) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child != panel) {
mMenuPanel = child;
break;
}
}
}
final float scaleLeft = 1 - 0.3f * (1 - slideOffset);
mMenuPanel.setPivotX(-0.3f * mMenuPanel.getWidth());
mMenuPanel.setPivotY(mMenuPanel.getHeight() / 2f);
mMenuPanel.setScaleX(scaleLeft);
mMenuPanel.setScaleY(scaleLeft);
final float scale = 1 - 0.2f * slideOffset;
panel.setPivotX(0);
panel.setPivotY(panel.getHeight() / 2.0f);
panel.setScaleX(scale);
panel.setScaleY(scale);
}
});
setSliderFadeColor(0);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isCustomable) {
dimOnForeground(canvas);
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean result = super.drawChild(canvas, child, drawingTime);
if (isCustomable && child == mMenuPanel) {
dimOnForeground(canvas);
}
return result;
}
/**
* dim the view
*
* @param canvas
*/
private void dimOnForeground(Canvas canvas) {
canvas.drawColor(Color.argb((int) (0xff * (1 - mSlideOffset)), 0, 0, 0));
}
}