AndroidSideMenu能够让你轻而易举地创建侧滑菜单。需要注意的是,该项目自身并不提供任何创建菜单的工具,因此,开发者可以自由创建内部菜单。
核心类如下:
package com.agimind.widget;
import java.util.linkedList;
import java.util.Queue;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
import android.widget.frameLayout;
public class SlideHolder extends frameLayout {
public final static int DIRECTION_LEFT = 1;
public final static int DIRECTION_RIGHT = -1;
protected final static int MODE_READY = 0;
protected final static int MODE_SLIDE = 1;
protected final static int MODE_FINISHED = 2;
private Bitmap mCachedBitmap;
private Canvas mCachedCanvas;
private Paint mCachedPaint;
private View mMenuView;
private int mMode = MODE_READY;
private int mDirection = DIRECTION_LEFT;
private int mOffset = 0;
private int mStartOffset;
private int mEndOffset;
private boolean mEnabled = true;
private boolean mInterceptTouch = true;
private boolean mAlwaysOpened = false;
private boolean mDispatchWhenOpened = false;
private Queue mWhenReady = new linkedList();
private onSlideListener mListener;
public SlideHolder(Context context) {
super(context);
initView();
}
public SlideHolder(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SlideHolder(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
mCachedPaint = new Paint(
Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG
| Paint.DITHER_FLAG
);
}
@Override
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
@Override
public boolean isEnabled() {
return mEnabled;
}
public void setDirection(int direction) {
closeImmediately();
mDirection = direction;
}
public void setAllowInterceptTouch(boolean allow) {
mInterceptTouch = allow;
}
public boolean isAllowedInterceptTouch() {
return mInterceptTouch;
}
public void setDispatchTouchWhenOpened(boolean dispatch) {
mDispatchWhenOpened = dispatch;
}
public boolean isDispatchTouchWhenOpened() {
return mDispatchWhenOpened;
}
public void setAlwaysOpened(boolean opened) {
mAlwaysOpened = opened;
requestLayout();
}
public int getMenuOffset() {
return mOffset;
}
public void setonSlideListener(onSlideListener lis) {
mListener = lis;
}
public boolean isOpened() {
return mAlwaysOpened || mMode == MODE_FINISHED;
}
public void toggle(boolean immediately) {
if(immediately) {
toggleImmediately();
} else {
toggle();
}
}
public void toggle() {
if(isOpened()) {
close();
} else {
open();
}
}
public void toggleImmediately() {
if(isOpened()) {
closeImmediately();
} else {
openImmediately();
}
}
public boolean open() {
if(isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
return false;
}
if(!isReadyForSlide()) {
mWhenReady.add(new Runnable() {
@Override
public void run() {
open();
}
});
return true;
}
initSlideMode();
Animation anim = new SlideAnimation(mOffset, mEndOffset);
anim.setAnimationListener(mOpenListener);
startAnimation(anim);
invalidate();
return true;
}
public boolean openImmediately() {
if(isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
return false;
}
if(!isReadyForSlide()) {
mWhenReady.add(new Runnable() {
@Override
public void run() {
openImmediately();
}
});
return true;
}
mMenuView.setVisibility(View.VISIBLE);
mMode = MODE_FINISHED;
requestLayout();
if(mListener != null) {
mListener.onSlideCompleted(true);
}
return true;
}
public boolean close() {
if(!isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
return false;
}
if(!isReadyForSlide()) {
mWhenReady.add(new Runnable() {
@Override
public void run() {
close();
}
});
return true;
}
initSlideMode();
Animation anim = new SlideAnimation(mOffset, mEndOffset);
anim.setAnimationListener(mCloseListener);
startAnimation(anim);
invalidate();
return true;
}
public boolean closeImmediately() {
if(!isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
return false;
}
if(!isReadyForSlide()) {
mWhenReady.add(new Runnable() {
@Override
public void run() {
closeImmediately();
}
});
return true;
}
mMenuView.setVisibility(View.GONE);
mMode = MODE_READY;
requestLayout();
if(mListener != null) {
mListener.onSlideCompleted(false);
}
return true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int parentLeft = 0;
final int parentTop = 0;
final int parentRight = r - l;
final int parentBottom = b - t;
View menu = getChildAt(0);
int menuWidth = menu.getMeasuredWidth();
if(mDirection == DIRECTION_LEFT) {
menu.layout(parentLeft, parentTop, parentLeft+menuWidth, parentBottom);
} else {
menu.layout(parentRight-menuWidth, parentTop, parentRight, parentBottom);
}
if(mAlwaysOpened) {
if(mDirection == DIRECTION_LEFT) {
mOffset = menuWidth;
} else {
mOffset = 0;
}
} else if(mMode == MODE_FINISHED) {
mOffset = mDirection*menuWidth;
} else if(mMode == MODE_READY) {
mOffset = 0;
}
View main = getChildAt(1);
main.layout(
parentLeft + mOffset,
parentTop,
parentLeft + mOffset + main.getMeasuredWidth(),
parentBottom
);
invalidate();
Runnable rn;
while((rn = mWhenReady.poll()) != null) {
rn.run();
}
}
private boolean isReadyForSlide() {
return (getWidth() > 0 && getHeight() > 0);
}
@Override
protected void onMeasure(int wSp, int hSp) {
mMenuView = getChildAt(0);
if(mAlwaysOpened) {
View main = getChildAt(1);
if(mMenuView != null && main != null) {
measureChild(mMenuView, wSp, hSp);
LayoutParams lp = (LayoutParams) main.getLayoutParams();
if(mDirection == DIRECTION_LEFT) {
lp.leftMargin = mMenuView.getMeasuredWidth();
} else {
lp.rightMargin = mMenuView.getMeasuredWidth();
}
}
}
super.onMeasure(wSp, hSp);
}
private byte mframe = 0;
@Override
protected void dispatchDraw(Canvas canvas) {
try {
if(mMode == MODE_SLIDE) {
View main = getChildAt(1);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if(main.isDirty()) {
mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
main.draw(mCachedCanvas);
}
} else {
if(++mframe % 5 == 0) {
mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
main.draw(mCachedCanvas);
}
}
View menu = getChildAt(0);
final int scrollX = menu.getScrollX();
final int scrollY = menu.getScrollY();
canvas.save();
if(mDirection == DIRECTION_LEFT) {
canvas.clipRect(0, 0, mOffset, menu.getHeight(), Op.REPLACe);
} else {
int menuWidth = menu.getWidth();
int menuLeft = menu.getLeft();
canvas.clipRect(menuLeft+menuWidth+mOffset, 0, menuLeft+menuWidth, menu.getHeight());
}
canvas.translate(menu.getLeft(), menu.getTop());
canvas.translate(-scrollX, -scrollY);
menu.draw(canvas);
canvas.restore();
canvas.drawBitmap(mCachedBitmap, mOffset, 0, mCachedPaint);
} else {
if(!mAlwaysOpened && mMode == MODE_READY) {
mMenuView.setVisibility(View.GONE);
}
super.dispatchDraw(canvas);
}
} catch(IndexOutOfBoundsException e) {
}
}
private int mHistoricalX = 0;
private boolean mCloseonRelease = false;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(((!mEnabled || !mInterceptTouch) && mMode == MODE_READY) || mAlwaysOpened) {
return super.dispatchTouchEvent(ev);
}
if(mMode != MODE_FINISHED) {
onTouchEvent(ev);
if(mMode != MODE_SLIDE) {
super.dispatchTouchEvent(ev);
} else {
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
super.dispatchTouchEvent(cancelEvent);
cancelEvent.recycle();
}
return true;
} else {
final int action = ev.getAction();
Rect rect = new Rect();
View menu = getChildAt(0);
menu.getHitRect(rect);
if(!rect.contains((int) ev.getX(), (int) ev.getY())) {
if (action == MotionEvent.ACTION_UP && mCloseonRelease && !mDispatchWhenOpened) {
close();
mCloseonRelease = false;
} else {
if(action == MotionEvent.ACTION_DOWN && !mDispatchWhenOpened) {
mCloseonRelease = true;
}
onTouchEvent(ev);
}
if(mDispatchWhenOpened) {
super.dispatchTouchEvent(ev);
}
return true;
} else {
onTouchEvent(ev);
ev.offsetLocation(-menu.getLeft(), -menu.getTop());
menu.dispatchTouchEvent(ev);
return true;
}
}
}
private boolean handleTouchEvent(MotionEvent ev) {
if(!mEnabled) {
return false;
}
float x = ev.getX();
if(ev.getAction() == MotionEvent.ACTION_DOWN) {
mHistoricalX = (int) x;
return true;
}
if(ev.getAction() == MotionEvent.ACTION_MOVE) {
float diff = x - mHistoricalX;
if((mDirection*diff > 50 && mMode == MODE_READY) || (mDirection*diff < -50 && mMode == MODE_FINISHED)) {
mHistoricalX = (int) x;
initSlideMode();
} else if(mMode == MODE_SLIDE) {
mOffset += diff;
mHistoricalX = (int) x;
if(!isSlideAllowed()) {
finishSlide();
}
} else {
return false;
}
}
if(ev.getAction() == MotionEvent.ACTION_UP) {
if(mMode == MODE_SLIDE) {
finishSlide();
}
mCloseonRelease = false;
return false;
}
return mMode == MODE_SLIDE;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = handleTouchEvent(ev);
invalidate();
return handled;
}
private void initSlideMode() {
mCloseonRelease = false;
View v = getChildAt(1);
if(mMode == MODE_READY) {
mStartOffset = 0;
mEndOffset = mDirection*getChildAt(0).getWidth();
} else {
mStartOffset = mDirection*getChildAt(0).getWidth();
mEndOffset = 0;
}
mOffset = mStartOffset;
if(mCachedBitmap == null || mCachedBitmap.isRecycled() || mCachedBitmap.getWidth() != v.getWidth()) {
mCachedBitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
mCachedCanvas = new Canvas(mCachedBitmap);
} else {
mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
}
v.setVisibility(View.VISIBLE);
mCachedCanvas.translate(-v.getScrollX(), -v.getScrollY());
v.draw(mCachedCanvas);
mMode = MODE_SLIDE;
mMenuView.setVisibility(View.VISIBLE);
}
private boolean isSlideAllowed() {
return (mDirection*mEndOffset > 0 && mDirection*mOffset < mDirection*mEndOffset && mDirection*mOffset >= mDirection*mStartOffset)
|| (mEndOffset == 0 && mDirection*mOffset > mDirection*mEndOffset && mDirection*mOffset <= mDirection*mStartOffset);
}
private void completeOpening() {
mOffset = mDirection*mMenuView.getWidth();
requestLayout();
post(new Runnable() {
@Override
public void run() {
mMode = MODE_FINISHED;
mMenuView.setVisibility(View.VISIBLE);
}
});
if(mListener != null) {
mListener.onSlideCompleted(true);
}
}
private Animation.AnimationListener mOpenListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
completeOpening();
}
};
private void completeClosing() {
mOffset = 0;
requestLayout();
post(new Runnable() {
@Override
public void run() {
mMode = MODE_READY;
mMenuView.setVisibility(View.GONE);
}
});
if(mListener != null) {
mListener.onSlideCompleted(false);
}
}
private Animation.AnimationListener mCloseListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
completeClosing();
}
};
private void finishSlide() {
if(mDirection*mEndOffset > 0) {
if(mDirection*mOffset > mDirection*mEndOffset/2) {
if(mDirection*mOffset > mDirection*mEndOffset) mOffset = mEndOffset;
Animation anim = new SlideAnimation(mOffset, mEndOffset);
anim.setAnimationListener(mOpenListener);
startAnimation(anim);
} else {
if(mDirection*mOffset < mDirection*mStartOffset) mOffset = mStartOffset;
Animation anim = new SlideAnimation(mOffset, mStartOffset);
anim.setAnimationListener(mCloseListener);
startAnimation(anim);
}
} else {
if(mDirection*mOffset < mDirection*mStartOffset/2) {
if(mDirection*mOffset < mDirection*mEndOffset) mOffset = mEndOffset;
Animation anim = new SlideAnimation(mOffset, mEndOffset);
anim.setAnimationListener(mCloseListener);
startAnimation(anim);
} else {
if(mDirection*mOffset > mDirection*mStartOffset) mOffset = mStartOffset;
Animation anim = new SlideAnimation(mOffset, mStartOffset);
anim.setAnimationListener(mOpenListener);
startAnimation(anim);
}
}
}
private class SlideAnimation extends Animation {
private static final float SPEED = 0.6f;
private float mStart;
private float mEnd;
public SlideAnimation(float fromX, float toX) {
mStart = fromX;
mEnd = toX;
setInterpolator(new DecelerateInterpolator());
float duration = Math.abs(mEnd - mStart) / SPEED;
setDuration((long) duration);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
float offset = (mEnd - mStart) * interpolatedTime + mStart;
mOffset = (int) offset;
postInvalidate();
}
}
public static interface onSlideListener {
public void onSlideCompleted(boolean opened);
}
}
使用:
package com.agimind.sidemenuexample;
import com.agimind.widget.SlideHolder;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.app.ActionBar;
import android.app.Activity;
public class MainActivity extends Activity {
private SlideHolder mSlideHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSlideHolder = (SlideHolder) findViewById(R.id.slideHolder);
// mSlideHolder.setAllowInterceptTouch(false);
// mSlideHolder.setAlwaysOpened(true);
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setHomeButtonEnabled(true);
View toggleView = findViewById(R.id.textView);
toggleView.setonClickListener(new View.onClickListener() {
@Override
public void onClick(View v) {
mSlideHolder.toggle();
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mSlideHolder.toggle();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
}
布局如下:
下载:AndroidSideMenu
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。



