代码分析Android实现侧滑菜单

Android 侧滑菜单的实现,参考网上的代码,实现侧滑菜单。最重要的是这个动画类UgcAnimations,如何使用动画类来侧滑的封装FlipperLayout。

1、实现效果

2、动画类UgcAnimations

package com.mmsx.base; 
import android.content.Context; 
import android.view.View; 
import android.view.ViewGroup.MarginLayoutParams; 
import android.view.animation.AlphaAnimation; 
import android.view.animation.Animation; 
import android.view.animation.AnimationSet; 
import android.view.animation.AnticipateInterpolator; 
import android.view.animation.OvershootInterpolator; 
import android.view.animation.RotateAnimation; 
import android.view.animation.ScaleAnimation; 
import android.view.animation.TranslateAnimation; 
import android.widget.ImageView; 
import android.widget.RelativeLayout; 
 
/** 
 * Path动画类 
 * 
 */ 
public class UgcAnimations { 
  private static int xOffset = 15; 
  private static int yOffset = -13; 
 
  public static void initOffset(Context context) { 
    xOffset = (int) (15 * context.getResources().getDisplayMetrics().density); 
    yOffset = -(int) (13 * context.getResources().getDisplayMetrics().density); 
  } 
 
  public static Animation getRotateAnimation(float fromDegrees, 
      float toDegrees, long durationMillis) { 
    RotateAnimation rotate = new RotateAnimation(fromDegrees, toDegrees, 
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 
        0.5f); 
    rotate.setDuration(durationMillis); 
    rotate.setFillAfter(true); 
    return rotate; 
  } 
 
  public static Animation getAlphaAnimation(float fromAlpha, float toAlpha, 
      long durationMillis) { 
    AlphaAnimation alpha = new AlphaAnimation(fromAlpha, toAlpha); 
    alpha.setDuration(durationMillis); 
    alpha.setFillAfter(true); 
    return alpha; 
  } 
 
  public static Animation getScaleAnimation(long durationMillis) { 
    ScaleAnimation scale = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f, 
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 
        0.5f); 
    scale.setDuration(durationMillis); 
    return scale; 
  } 
 
  public static Animation getTranslateAnimation(float fromXDelta, 
      float toXDelta, float fromYDelta, float toYDelta, 
      long durationMillis) { 
    TranslateAnimation translate = new TranslateAnimation(fromXDelta, 
        toXDelta, fromYDelta, toYDelta); 
    translate.setDuration(durationMillis); 
    translate.setFillAfter(true); 
    return translate; 
  } 
 
  public static void startOpenAnimation(RelativeLayout relativeLayout, 
      ImageView background, ImageView menu, long durationMillis) { 
    background.setVisibility(View.VISIBLE); 
    relativeLayout.setVisibility(View.VISIBLE); 
    background.startAnimation(getAlphaAnimation(0f, 1f, durationMillis)); 
    menu.startAnimation(getRotateAnimation(0, 90, durationMillis)); 
    for (int i = 0; i < relativeLayout.getChildCount(); i++) { 
      ImageView imageView = (ImageView) relativeLayout.getChildAt(i); 
      imageView.setVisibility(View.VISIBLE); 
      MarginLayoutParams params = (MarginLayoutParams) imageView 
          .getLayoutParams(); 
      AnimationSet set = new AnimationSet(true); 
      set.addAnimation(getRotateAnimation(-270, 0, durationMillis)); 
      set.addAnimation(getAlphaAnimation(0.5f, 1.0f, durationMillis)); 
      set.addAnimation(getTranslateAnimation( 
          -params.leftMargin + xOffset, 0f, params.bottomMargin 
              + yOffset, 0f, durationMillis)); 
      set.setFillAfter(true); 
      set.setDuration(durationMillis); 
      set.setStartOffset((i * 100) 
          / (-1 + relativeLayout.getChildCount())); 
      set.setInterpolator(new OvershootInterpolator(1f)); 
      imageView.startAnimation(set); 
    } 
  } 
  public static void startCloseAnimation(final RelativeLayout relativeLayout, 
      final ImageView background, ImageView menu, long durationMillis) { 
    background.startAnimation(getAlphaAnimation(1f, 0f, durationMillis)); 
    menu.startAnimation(getRotateAnimation(90, 0, durationMillis)); 
    for (int i = 0; i < relativeLayout.getChildCount(); i++) { 
      final ImageView imageView = (ImageView) relativeLayout 
          .getChildAt(i); 
      MarginLayoutParams params = (MarginLayoutParams) imageView 
          .getLayoutParams(); 
      AnimationSet set = new AnimationSet(true); 
      set.addAnimation(getRotateAnimation(0, -270, durationMillis)); 
      set.addAnimation(getAlphaAnimation(1.0f, 0.5f, durationMillis)); 
      set.addAnimation(getTranslateAnimation(0f, -params.leftMargin 
          + xOffset, 0f, params.bottomMargin + yOffset, 
          durationMillis)); 
      set.setFillAfter(true); 
      set.setDuration(durationMillis); 
      set.setStartOffset(((relativeLayout.getChildCount() - i) * 100) 
          / (-1 + relativeLayout.getChildCount())); 
      set.setInterpolator(new AnticipateInterpolator(1f)); 
      set.setAnimationListener(new Animation.AnimationListener() { 
        public void onAnimationStart(Animation arg0) { 
        } 
        public void onAnimationRepeat(Animation arg0) { 
        } 
        public void onAnimationEnd(Animation arg0) { 
          relativeLayout.setVisibility(View.GONE); 
          background.setVisibility(View.GONE); 
        } 
      }); 
      imageView.startAnimation(set); 
    } 
  } 
  public static Animation clickAnimation(long durationMillis) { 
    AnimationSet set = new AnimationSet(true); 
    set.addAnimation(getAlphaAnimation(1.0f, 0.3f, durationMillis)); 
    set.addAnimation(getScaleAnimation(durationMillis)); 
    set.setDuration(durationMillis); 
    return set; 
  } 
} 

3、封装使用动画类FlipperLayout

package com.mmsx.base; 
import android.content.Context; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.view.MotionEvent; 
import android.view.VelocityTracker; 
import android.view.View; 
import android.view.ViewConfiguration; 
import android.view.ViewGroup; 
import android.widget.Scroller; 
 
/** 
 * 自己重写的ViewGroup,用与滑动切换界面使用,代码不详解,慢点看的话应该能看懂的... 
 */ 
public class FlipperLayout extends ViewGroup { 
 
  private Scroller mScroller; 
  private VelocityTracker mVelocityTracker; 
  private int mWidth; 
 
  public static final int SCREEN_STATE_CLOSE = 0; 
  public static final int SCREEN_STATE_OPEN = 1; 
  public static final int TOUCH_STATE_RESTART = 0; 
  public static final int TOUCH_STATE_SCROLLING = 1; 
  public static final int SCROLL_STATE_NO_ALLOW = 0; 
  public static final int SCROLL_STATE_ALLOW = 1; 
  private int mScreenState = 0; 
  private int mTouchState = 0; 
  private int mScrollState = 0; 
  private int mVelocityValue = 0; 
  private boolean mOnClick = false; 
  private onUgcDismissListener mOnUgcDismissListener; 
  private onUgcShowListener mOnUgcShowListener; 
 
  public FlipperLayout(Context context) { 
    super(context); 
    mScroller = new Scroller(context); 
    mWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 
        54, getResources().getDisplayMetrics()); 
 
  } 
 
  public FlipperLayout(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
  } 
 
  public FlipperLayout(Context context, AttributeSet attrs) { 
    super(context, attrs); 
  } 
 
  protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    for (int i = 0; i < getChildCount(); i++) { 
      View child = getChildAt(i); 
      int height = child.getMeasuredHeight(); 
      int width = child.getMeasuredWidth(); 
      child.layout(0, 0, width, height); 
    } 
  } 
 
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int width = MeasureSpec.getSize(widthMeasureSpec); 
    int height = MeasureSpec.getSize(heightMeasureSpec); 
    setMeasuredDimension(width, height); 
    for (int i = 0; i < getChildCount(); i++) { 
      getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 
    } 
  } 
 
  public boolean dispatchTouchEvent(MotionEvent ev) { 
    obtainVelocityTracker(ev); 
    switch (ev.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
      mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESTART 
          : TOUCH_STATE_SCROLLING; 
      if (mTouchState == TOUCH_STATE_RESTART) { 
        int x = (int) ev.getX(); 
        int screenWidth = getWidth(); 
        if (x <= mWidth && mScreenState == SCREEN_STATE_CLOSE 
            && mTouchState == TOUCH_STATE_RESTART 
            || x >= screenWidth - mWidth 
            && mScreenState == SCREEN_STATE_OPEN 
            && mTouchState == TOUCH_STATE_RESTART) { 
          if (mScreenState == SCREEN_STATE_OPEN) { 
            mOnClick = true; 
          } 
          mScrollState = SCROLL_STATE_ALLOW; 
        } else { 
          mOnClick = false; 
          mScrollState = SCROLL_STATE_NO_ALLOW; 
        } 
      } else { 
        return false; 
      } 
      break; 
    case MotionEvent.ACTION_MOVE: 
      mVelocityTracker.computeCurrentVelocity(1000, 
          ViewConfiguration.getMaximumFlingVelocity()); 
      if (mScrollState == SCROLL_STATE_ALLOW 
          && getWidth() - (int) ev.getX() < mWidth) { 
        return true; 
      } 
      break; 
    case MotionEvent.ACTION_UP: 
      releaseVelocityTracker(); 
      if (mOnClick) { 
        mOnClick = false; 
        mScreenState = SCREEN_STATE_CLOSE; 
        mScroller.startScroll(getChildAt(1).getScrollX(), 0, 
            -getChildAt(1).getScrollX(), 0, 800); 
        invalidate(); 
      } 
      break; 
    } 
    return super.dispatchTouchEvent(ev); 
  } 
 
  public boolean onInterceptTouchEvent(MotionEvent ev) { 
    obtainVelocityTracker(ev); 
    switch (ev.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
      mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESTART 
          : TOUCH_STATE_SCROLLING; 
      if (mTouchState == TOUCH_STATE_SCROLLING) { 
        return false; 
      } 
      break; 
 
    case MotionEvent.ACTION_MOVE: 
      mOnClick = false; 
      mVelocityTracker.computeCurrentVelocity(1000, 
          ViewConfiguration.getMaximumFlingVelocity()); 
      if (mScrollState == SCROLL_STATE_ALLOW 
          && Math.abs(mVelocityTracker.getXVelocity()) > 200) { 
        return true; 
      } 
      break; 
 
    case MotionEvent.ACTION_UP: 
      releaseVelocityTracker(); 
      if (mScrollState == SCROLL_STATE_ALLOW 
          && mScreenState == SCREEN_STATE_OPEN) { 
        return true; 
      } 
      break; 
    } 
    return super.onInterceptTouchEvent(ev); 
  } 
 
  public boolean onTouchEvent(MotionEvent event) { 
    obtainVelocityTracker(event); 
    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
      mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESTART 
          : TOUCH_STATE_SCROLLING; 
      if (mTouchState == TOUCH_STATE_SCROLLING) { 
        return false; 
      } 
      break; 
 
    case MotionEvent.ACTION_MOVE: 
      mVelocityTracker.computeCurrentVelocity(1000, 
          ViewConfiguration.getMaximumFlingVelocity()); 
      mVelocityValue = (int) mVelocityTracker.getXVelocity(); 
      getChildAt(1).scrollTo(-(int) event.getX(), 0); 
      break; 
 
    case MotionEvent.ACTION_UP: 
      if (mScrollState == SCROLL_STATE_ALLOW) { 
        if (mVelocityValue > 2000) { 
          mScreenState = SCREEN_STATE_OPEN; 
          mScroller 
              .startScroll( 
                  getChildAt(1).getScrollX(), 
                  0, 
                  -(getWidth() 
                      - Math.abs(getChildAt(1) 
                          .getScrollX()) - 
 
                  mWidth), 0, 250); 
          invalidate(); 
 
        } else if (mVelocityValue < -2000) { 
          mScreenState = SCREEN_STATE_CLOSE; 
          mScroller.startScroll(getChildAt(1).getScrollX(), 0, 
              -getChildAt(1).getScrollX(), 0, 250); 
          invalidate(); 
        } else if (event.getX() < getWidth() / 2) { 
          mScreenState = SCREEN_STATE_CLOSE; 
          mScroller.startScroll(getChildAt(1).getScrollX(), 0, 
              -getChildAt(1).getScrollX(), 0, 800); 
          invalidate(); 
        } else { 
          mScreenState = SCREEN_STATE_OPEN; 
          mScroller 
              .startScroll( 
                  getChildAt(1).getScrollX(), 
                  0, 
                  -(getWidth() 
                      - Math.abs(getChildAt(1) 
                          .getScrollX()) - 
 
                  mWidth), 0, 800); 
          invalidate(); 
        } 
      } 
      break; 
    } 
    return super.onTouchEvent(event); 
  } 
 
  public void open() { 
    mTouchState = mScroller.isFinished() ? TOUCH_STATE_RESTART 
        : TOUCH_STATE_SCROLLING; 
    if (mTouchState == TOUCH_STATE_RESTART) { 
      mScreenState = SCREEN_STATE_OPEN; 
      mScroller.startScroll(getChildAt(1).getScrollX(), 0, -(getWidth() 
          - Math.abs(getChildAt(1).getScrollX()) - 
 
      mWidth), 0, 800); 
      invalidate(); 
    } 
  } 
 
  //关闭当前的侧滑菜单,用view打开点击事件的页面 
  public void close(View view) { 
    mScreenState = SCREEN_STATE_CLOSE; 
    mScroller.startScroll(getChildAt(1).getScrollX(), 0, -getChildAt(1) 
        .getScrollX(), 0, 800); 
    invalidate(); 
    setContentView(view); 
  } 
 
  public void computeScroll() { 
    super.computeScroll(); 
    if (mScroller.computeScrollOffset()) { 
      getChildAt(1).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
      postInvalidate(); 
    } else { 
      if (mScreenState == SCREEN_STATE_OPEN) { 
        if (mOnUgcDismissListener != null) { 
          mOnUgcDismissListener.dismiss(); 
        } 
      } else if (mScreenState == SCREEN_STATE_CLOSE) { 
        if (mOnUgcShowListener != null) { 
          mOnUgcShowListener.show(); 
        } 
      } 
    } 
  } 
  private void obtainVelocityTracker(MotionEvent event) { 
    if (mVelocityTracker == null) { 
      mVelocityTracker = VelocityTracker.obtain(); 
    } 
    mVelocityTracker.addMovement(event); 
  } 
  private void releaseVelocityTracker() { 
    if (mVelocityTracker != null) { 
      mVelocityTracker.recycle(); 
      mVelocityTracker = null; 
    } 
  } 
  public int getScreenState() { 
    return mScreenState; 
  } 
  public void setContentView(View view) { 
    removeViewAt(1); 
    addView(view, 1, getLayoutParams()); 
  } 
  public interface OnOpenListener { 
    public abstract void open(); 
  } 
  public interface OnCloseListener { 
    public abstract void close(); 
  } 
  public interface onUgcDismissListener { 
    public abstract void dismiss(); 
  } 
  public interface onUgcShowListener { 
    public abstract void show(); 
  } 
  public void setOnUgcDismissListener( 
      onUgcDismissListener onUgcDismissListener) { 
    mOnUgcDismissListener = onUgcDismissListener; 
  } 
  public void setOnUgcShowListener(onUgcShowListener onUgcShowListener) { 
    mOnUgcShowListener = onUgcShowListener; 
  } 
} 

4、主界面MainActivity

package com.mmsx.activity;  
import com.mmsx.activity.SideslipMenu.onChangeViewListener; 
import com.mmsx.activity.SideslipOther.onDataListener; 
import com.mmsx.base.FlipperLayout; 
import com.mmsx.base.FlipperLayout.OnOpenListener; 
import com.mmsx.base.ViewUtil; 
import android.os.Bundle; 
import android.app.Activity; 
import android.view.ViewGroup.LayoutParams; 
import android.widget.Toast; 
 
public class MainActivity extends Activity implements OnOpenListener{ 
 
  //侧滑主要控制类,设置跟布局 
  private FlipperLayout mRoot; 
  //侧滑的默认界面,主界面 
  private SideslipHome mHome; 
  //侧滑的菜单,进行选择的 
  private SideslipMenu mSideslipMenu; 
  //其他菜单列表选择的效果 
  private SideslipOther mOther; 
  //退出时间间隔变量 
  private long mExitTime; 
  //时间间隔2s 
  private static final int INTERVAL = 2000; 
  //侧滑菜单选中的item 
  private int mViewPosition; 
  //侧滑传递的标题 
  private String mstrTitle; 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
     //创建容器,并设置全屏大小 
    mRoot = new FlipperLayout(this); 
    //布局的参数 
    LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, 
        LayoutParams.FILL_PARENT); 
    mRoot.setLayoutParams(params); 
    //创建菜单界面和内容首页界面,并添加到容器中,用于初始显示 
    mHome = new SideslipHome(this, this); 
    mSideslipMenu = new SideslipMenu(this); 
    mRoot.addView(mSideslipMenu.getView(), params); 
    mRoot.addView(mHome.getView(), params); 
    //设置跟布局 
    setContentView(mRoot); 
    //设置监听 
    setListener(); 
  } 
 
  //设置监听 
  private void setListener() { 
    mHome.setOnOpenListener(this); 
     //监听菜单界面切换显示内容(onChangeViewListener接口在SideslipMenu中定义) 
    mSideslipMenu.setOnChangeViewListener(new onChangeViewListener() { 
       
      public void onChangeView(int arg0) { 
        mViewPosition = arg0; 
        mOther = new SideslipOther(MainActivity.this); 
        switch (arg0) { 
        case ViewUtil.HOME: 
          mRoot.close(mHome.getView()); 
          break; 
        case ViewUtil.MESSAGE: 
          mstrTitle = "消息"; 
          //设置数据接口监听 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.FRIENDS: 
          mstrTitle = "好友"; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.PHOTO: 
          mstrTitle = "照片"; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.VIEWED: 
          mstrTitle = "转帖"; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.GIFTS: 
          mstrTitle = "礼物"; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.RECOMMEND: 
          mstrTitle = "游戏"; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        case ViewUtil.LBS: 
          mstrTitle = "附近 "; 
          mOther.setDataTitle(new DataTitle()); 
          mRoot.close(mOther.getView()); 
          break; 
        default: 
          break; 
        } 
      } 
    }); 
  } 
   
  //传递数据到侧滑选中的页面 
  private class DataTitle implements onDataListener{ 
    @Override 
    public String getDataTitle() { 
      return mstrTitle; 
    } 
     
  } 
 
  @Override 
  public void open() { 
    if (mRoot.getScreenState() == FlipperLayout.SCREEN_STATE_CLOSE) { 
      mRoot.open(); 
    } 
  } 
   
  /** 
   * 返回键监听 
   */ 
  public void onBackPressed() { 
    /** 
     * 如果界面的path菜单没有关闭时,先将path菜单关闭,否则则判断两次返回时间间隔,小于两秒则退出程序 
     */ 
    if (mRoot.getScreenState() == FlipperLayout.SCREEN_STATE_OPEN) { 
      if (mSideslipMenu.getUgcIsShowing()) { 
        mSideslipMenu.closeUgc(); 
      } else { 
        exit(); 
      } 
    } else { 
      switch (mViewPosition) { 
      case ViewUtil.HOME: 
        if (mHome.getUgcIsShowing()) { 
          mHome.closeUgc(); 
        } else { 
          exit(); 
        } 
        break; 
      default: 
        exit(); 
        break; 
      } 
 
    } 
 
  }   
  /** 
   * 判断两次返回时间间隔,小于两秒则退出程序 
   */ 
  private void exit() { 
    if (System.currentTimeMillis() - mExitTime > INTERVAL) { 
      Toast.makeText(this, "再按一次返回键,可直接退出程序", Toast.LENGTH_SHORT).show(); 
      mExitTime = System.currentTimeMillis(); 
    } else { 
      finish(); 
      android.os.Process.killProcess(android.os.Process.myPid()); 
      System.exit(0); 
    } 
  } 
}

5、SideslipHome

package com.mmsx.activity; 
import com.mmsx.base.FlipperLayout.OnOpenListener; 
 
import android.app.Activity; 
import android.content.Context; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.widget.TextView; 
 
public class SideslipHome { 
  private Context mContext; 
  private Activity mActivity; 
  private View mHomeView; 
  private boolean mUgcIsShowing = false;  
  private OnOpenListener mOnOpenListener; 
  public SideslipHome(Context context, Activity activity) { 
    mContext = context; 
    mActivity = activity; 
    mHomeView = LayoutInflater.from(context).inflate(R.layout.sideslip_home, null); 
     
    initUI(); 
  } 
 
  private void initUI() { 
    TextView ivTitleName = (TextView)mHomeView.findViewById(R.id.ivTitleName);  
    ivTitleName.setText("主页动态"); 
     
  } 
   
  public void setOnOpenListener(OnOpenListener onOpenListener) { 
    mOnOpenListener = onOpenListener; 
  } 
 
  public View getView() { 
    return mHomeView; 
  } 
   
  /** 
   * 获取Path菜单显示状态 
   */ 
  public boolean getUgcIsShowing() { 
    return mUgcIsShowing; 
  } 
 
  /** 
   * 关闭Path菜单 
   */ 
  public void closeUgc() { 
    mUgcIsShowing = false; 
  } 
 
} 

好了,以上就是本文的全部叙述,希望大家喜欢。