文章出处:http://blog.youkuaiyun.com/knlnzhao/article/details/7920766
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=183314
最近发现有好多软件都采用了滑动式菜单的效果,例如人人,云中书城等等。这种效果给人以耳目一新的感觉,所以自己也特别想实现一个。由于鄙人才疏学浅,属于菜鸟级的人物,第一次想到的当然是在网上找相关的demo。百度过来谷歌过去,发现相关的demo少的可怜,只找到一个某位大牛jfeinstein10写的SlidingMenu库。欣喜之余,下载下来看看,发现真心麻烦啊,于是下定决心自己写一个。
实现SlidingMenu的方法如下:
自定义ViewGroup类ViewFlipper如下:
package knlnzhao.slidingmenu;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
public class FlipperView extends ViewGroup {
private int distance;// 完全显示菜单需要移动的距离
private View menu;
private View main;
private Scroller scroller;
private boolean menuVisible = false;
public FlipperView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
scroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
distance = getWidth() * 4 / 5;// 获得平滑移动的距离,也是菜单的宽度
// 布局菜单和主页面视图
menu = getChildAt(0);// 获得菜单视图
menu.setVisibility(VISIBLE);
menu.measure(
MeasureSpec.makeMeasureSpec(distance, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY));
menu.layout(-distance, 0, 0, getHeight());
main = getChildAt(1);// 获得主页面视图
main.setVisibility(VISIBLE);
// 相当于fill_parent
main.measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
main.layout(0, 0, getWidth(), getHeight());
}
public void showMenu() {
if (!menuVisible) {
scroller.startScroll(getScrollX(), 0, -distance, 0, 300);
invalidate();
menuVisible = true;
}
}
public void hideMenu() {
if (menuVisible) {
scroller.startScroll(getScrollX(), 0, distance, 0, 300);
invalidate();
menuVisible = false;
}
}
@Override
public void computeScroll() {
// TODO Auto-generated method stub
// 当滚动没有完成
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
postInvalidate();
}
}
}
ViewFlipper中包含有两个子布局,一个子布局是menu布局,另一个子布局是主要显示内容布局。在ViewFlipper的onLayout函数中,对这两个子布局进行布局。显示菜单和隐藏菜单是有两个函数实现,分别是
public void showMenu() {
if (!menuVisible) {
scroller.startScroll(getScrollX(), 0, -distance, 0, 300);
invalidate();
menuVisible = true;
}
}
public void hideMenu() {
if (menuVisible) {
scroller.startScroll(getScrollX(), 0, distance, 0, 300);
invalidate();
menuVisible = false;
}
}
这两个函数都用到了ViewGroup中负责移动子视图的Scroller类。通过调用函数
startScroll(int startX, int startY, int dx, int dy, int duration)
来实现菜单移动的动画效果。这个函数里面的四个参数比较令人费解,仔细琢磨解释一下,如果理解不对,也请各位大牛指正。
startX:在水平方向上,已经滚动的距离。
startY:在竖直方向上,已经滚动的距离。
dx:在水平方向需要滚动的总距离,注意是总距离,包括已经滚动的距离。
dy:在竖直方向上需要滚动的总距离。
这样,自定义的ViewGroup类ViewFlipper就完成了。接下来就要填写内容,包括菜单和主页面了。
菜单menu.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200.0dip" android:layout_height="fill_parent" android:orientation="vertical" android:background="@color/dark_blue">
<button android:id="@+id/close" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="关闭菜单"/>
<textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是菜单" android:textsize="20.0sp" android:textcolor="#ff000000" android:layout_gravity="center"/>
</linearlayout>
主页面content.xml如下
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200.0dip" android:layout_height="fill_parent" android:orientation="vertical" android:background="@color/dark_blue">
<button android:id="@+id/close" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="打开菜单"/>
<textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是主页面" android:textsize="20.0sp" android:textcolor="#ff000000" android:layout_gravity="center"/>
</linearlayout>
在SlidingMenuActivity的内容描述文件main.xml中做如下定义:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">
<knlnzhao slidingmenu="" flipperview="" android:id="@+id/flipper" android:layout_width="fill_parent" android:layout_height="fill_parent">
</knlnzhao></linearlayout>
在启动的Activity中做如下代码:
package knlnzhao.slidingmenu;
import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.GestureDetector.OnGestureListener;
import android.widget.Button;
public class SlidingMenuActivity extends Activity {
private FlipperView flipper;
private Button open;
private Button close;
private GestureDetector detector;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
flipper = (FlipperView) findViewById(R.id.flipper);
View menu = LayoutInflater.from(this).inflate(R.layout.menu, null);
View content = LayoutInflater.from(this)
.inflate(R.layout.content, null);
flipper.addView(menu);
flipper.addView(content);
open = (Button) content.findViewById(R.id.open);
close = (Button) menu.findViewById(R.id.close);
open.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
flipper.showMenu();
}
});
close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
flipper.hideMenu();
}
});
detector = new GestureDetector(new OnGestureListener() {
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// TODO Auto-generated method stub
return false;
}
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
// TODO Auto-generated method stub
// 判断是否达到最小滑动速度,取绝对值
if (Math.abs(velocityX) > ViewConfiguration.get(
SlidingMenuActivity.this).getScaledMinimumFlingVelocity()) {
if (velocityX > 0 ) {//向右滑动
flipper.showMenu();
} else if (velocityX < 0) {//向左滑动
flipper.hideMenu();
}
}
return true;
}
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
detector.onTouchEvent(event);
return true;
}
}
主要包括对FlipperView进行填充,滑动操作等等。
奉上运行效果图: