自定義View系列(八) 仿酷狗側滑菜單的實現

側滑菜單在項目中大量應用,我們一般會用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;
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容