文章出处:http://blog.csdn.net/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进行填充,滑动操作等等。
奉上运行效果图: