android开源项目(一) AndroidSideMenu
SlidingMenu实现的效果如下:
最近直接拿来用!很火的android开源项目真的很牛,作为菜鸟我也要学习大神们的经验,牛顿曾说过:“如果说我看的远,那是因为站在巨人的肩膀上”
AndroidSideMenu的来源是github 代码库中的开源项目:路径如下:git://github.com/dmitry-zaitsev/AndroidSideMenu.git
那么怎样把github代码仓库中的项目导入到我们常用的Eclipse中呢?详细步骤请看下面:
1.首先安装EGit插件,如果已有可以略过。
在eclipse的help中选择Eclipse Marketplace 在find中输入egit就会出现如下界面
点击install, 等待安装完毕
然后接受安装协议
然后eclipse就开始安装EGit
片刻后,EGit就可以安装好了,
2.克隆Git仓库上的代码到本地电脑,并在Eclipse中查看Git中的工程
1.步骤如下:file—Import---Git--ProjectFromGit--URI
2.从git://github.com/dmitry-zaitsev/AndroidSideMenu.git 地址中clone androidslidingMenu项目,
默认next 后出现以下,
继续默认next,选择克隆代码存放的位置
clon完毕后会出现以下选择界面
这样就可以导入从Git上下载的工程了
在eclipse中的布局如下
当我们导入从Git中下载好后的东西,我们却发现运行不了,我在网上也收了很多方法,可惜可有收到怎样解决的方法,如果谁有好的方法可以给我说说,
我的解决方法如下:
3.创建android工程
新建一个android工程,然后将布局,实现的代码等复制进去,修改修改最后终于给弄好了,不过呢,这个android工程要求建立的版本的在android 3.0之上的才可以运行,我试着建立常用的2.3.3 或则3.0以下的版本,结果发现有点不兼容...不知道那些牛逼的应用中怎样解决这个兼容性的问题的...
我创建好的工程源码下载地址:点击打开链接
实现的效果如下:
实现的布局如下:
<com.nyist.net.SlideHolder xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/slideHolder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".Test3Activity" >
<ScrollView
android:layout_width="280dp"
android:layout_height="fill_parent"
android:background="@drawable/navigation_background" >
<LinearLayout
android:layout_width="280dp"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:padding="1dp"
android:orientation="vertical" >
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="我的音乐" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="推荐" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="歌单" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="搜索" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="排行榜" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="歌手" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/btn_right"
android:text="淘歌" />
</LinearLayout>
</ScrollView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/swipe"
android:background="@drawable/guider_03"
android:textSize="25sp" />
</RelativeLayout>
</com.nyist.net.SlideHolder>
布局引用的代码如下:
package com.nyist.net;
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<Runnable> mWhenReady = new LinkedList<Runnable>();
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;
}
/**
*
* @param direction - direction in which SlideHolder opens. Can be: DIRECTION_LEFT, DIRECTION_RIGHT
*/
public void setDirection(int direction) {
closeImmediately();
mDirection = direction;
}
/**
*
* @param allow - if false, SlideHolder won't react to swiping gestures (but still will be able to work by manually invoking mathods)
*/
public void setAllowInterceptTouch(boolean allow) {
mInterceptTouch = allow;
}
public boolean isAllowedInterceptTouch() {
return mInterceptTouch;
}
/**
*
* @param dispatch - if true, in open state SlideHolder will dispatch touch events to main layout (in other words - it will be clickable)
*/
public void setDispatchTouchWhenOpened(boolean dispatch) {
mDispatchWhenOpened = dispatch;
}
public boolean isDispatchTouchWhenOpened() {
return mDispatchWhenOpened;
}
/**
*
* @param opened - if true, SlideHolder will always be in opened state (which means that swiping won't work)
*/
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) {
/*
* On new versions we redrawing main layout only
* if it's marked as dirty
*/
if(main.isDirty()) {
mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
main.draw(mCachedCanvas);
}
} else {
/*
* On older versions we just redrawing our cache
* every 5th frame
*/
if(++mFrame % 5 == 0) {
mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
main.draw(mCachedCanvas);
}
}
/*
* Draw only visible part of menu
*/
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) {
/*
* Possibility of crashes on some devices (especially on Samsung).
* Usually, when ListView is empty.
*/
}
}
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);
}
}
ps:这个工程要求是在android 3.0之上的版本
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!