側滑菜單在項目中大量應用,我們一般會用SlidingMenu或者DrawLayout,今天就讓我們走進自定義側滑的世界,仿照酷狗音樂的側滑來做。
1.類的創建和自定義屬性
類繼承HorizontalScrollView,自定義屬性都會,就直接上代碼
<resources>
<declare-styleable name="KGSlidingMenu">
<attr name="menuRightMargin" format="dimension"/>
</declare-styleable>
</resources>
public class KGSlidingMenu extends HorizontalScrollView {
private Context mContext;
private int mMenuWidth;
public KGSlidingMenu(Context context) {
this(context, null);
}
public KGSlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KGSlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext=context;
//初始化自定義屬性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.KGSlidingMenu);
float rightMargin = typedArray.getDimension(R.styleable.KGSlidingMenu_menuRightMargin, ScreenUtils.dip2px(mContext, 50));
//菜單頁的寬度 是 屏幕的寬度減右邊一部分的距離
mMenuWidth= (int) (ScreenUtils.getScreenWidth(mContext)-rightMargin);
typedArray.recycle();
} }
2.onFinishInflate() 方法調用,來設置寬度問題
onFinishInflate();方法是在xml解析之后調用,在Activity的onCreate()中調用,獲取子view,設置寬度
//獲取子view 相當于LinearLayout
ViewGroup viewGroup=(ViewGroup) getChildAt(0);
int childCount = viewGroup.getChildCount();
//只能有兩個子view
if (childCount!=2){
throw new RuntimeException("只能放置兩個子View");
}
//菜單頁的寬度 是 屏幕的寬度減右邊一部分的距離
mMenuView= viewGroup.getChildAt(0);
ViewGroup.LayoutParams menuParams = mMenuView.getLayoutParams();
menuParams.width=mMenuWidth;
//7.0以下手機 必須添加這句
mMenuView.setLayoutParams(menuParams);
//內容頁的寬度 屏幕的寬度
mContentView= viewGroup.getChildAt(1);
ViewGroup.LayoutParams contentParams = mContentView.getLayoutParams();
contentParams.width=ScreenUtils.getScreenWidth(getContext());
mContentView.setLayoutParams(contentParams);
}
3.onLayout()方法,初始化設置
onLayout()方法是在Activity的onResume()中調用
/**
* 初始化是關閉的
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 用來排放子布局的 等子View全部拜訪完才能去滾動
scrollTo(mMenuWidth,0);
}
4.觸摸事件onTouchEvent(),設置打開和關閉
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_UP:
int currentScrollX= getScrollX();
if (currentScrollX>mMenuWidth/2){
//關閉
closeMenu();
}else {
//打開
openMenu();
}
break;
}
return true;
}
/**
* 打開菜單
*
* smoothScrollTo() 有動畫的滾動
*/
private void openMenu() {
smoothScrollTo(0,0);
}
/**
* 關閉菜單
*/
private void closeMenu() {
smoothScrollTo(mMenuWidth,0);
}
5.onScrollChanged()可以實時獲取坐標位置,來完成縮放和透明度變化
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale=1f*l/mMenuWidth;
//右邊的縮放
float rightScale=0.7f+0.3f*scale;
//設置右邊的縮放
ViewCompat.setPivotX(mContentView,0);
ViewCompat.setScaleY(mContentView,mContentView.getMeasuredHeight()/2);
ViewCompat.setScaleX(mContentView,rightScale);
ViewCompat.setScaleY(mContentView,rightScale);
//菜單的縮放和透明度
//透明度 半透明到完全透明
float alpha=0.7f+(1-scale)*0.3f;
ViewCompat.setAlpha(mMenuView,alpha);
//縮放 0.7f -1.0f
float leftScale=0.7f+(1-scale)*0.3f;
ViewCompat.setScaleX(mMenuView,leftScale);
ViewCompat.setScaleY(mMenuView,leftScale);
//退出按鈕剛 開始在右邊,劃出時出字的變化
//設置平移 平移l*0.1f
ViewCompat.setTranslationX(mMenuView,0.25f*l);
}
6.手勢處理類GestureDetector,解決快速滑動
創建對象
mGestureDetector=new GestureDetector(mContext,mGestureListener);
監聽的實現
private class GestureDetectorListener implements GestureDetector.OnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
// 按下
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;// 單擊
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;// 滾動
}
@Override
public void onLongPress(MotionEvent e) {
// 長安
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 快速滑動
return false;
}
}
我們只需要快速滑動方法,所以可以實現下面監聽
private GestureDetector.OnGestureListener mGestureListener=new GestureDetector.SimpleOnGestureListener(
){
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//只關注 快速滑動
//打開時往右邊快速滑動 關閉的時候往左邊快速滑動
if (mMenuIsOpen){
//打開時往右邊快速滑動(關閉)
if (velocityX<0){
closeMenu();
return true;
}
}else {
// 關閉的時候往左邊快速滑動(打開)
if (velocityX>0){
openMenu();
return true;
}
}
return super.onFling(e1, e2, velocityX, velocityY);
}
};
定義字段標記是否是打開
private boolean mMenuIsOpen=false;
在openMenu() 和closeMenu()中設置標記值
因為onTouchEvent()和onFling()方法不能重復走所以,在onTouchEvent()中
if (mGestureDetector.onTouchEvent(ev)){
//快速滑動觸發了,下面就不要走
return true;
};
6.onInterceptTouchEvent()攔截事件的處理,菜單打開時點擊右邊關閉菜單
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mIsIntercept=false;
if (mMenuIsOpen){
float currentX=ev.getX();
if (currentX>mMenuWidth){
//關閉菜單
closeMenu();
//子view不需要相應任何事件,攔截子view的事件
mIsIntercept=true;
return true;
}
}
return super.onInterceptTouchEvent(ev);
}
在onTouchEvent()中也要做相應處理
if (mIsIntercept){
return true;
}