Android实现自定义滑动式抽屉效果菜单

在Andoird使用Android自带的那些组件,像SlidingDrawer和DrawerLayout都是抽屉效果的菜单,但是在项目很多要实现的功能都收到Android这些自带组件的限制,导致很难完成项目的需求,自定义的组件,各方面都在自己的控制之下,从而根据需求做出调整。想要实现好的效果,基本上都的基于Android的OnTouch事件自己实现响应的功能。
首先,给大家先看一下整体的效果:

滑动的加速度效果都是有的,具体的体验,只能安装后才能查看。
接下来,看代码:
代码从MainActivity延伸出了2个类:MainController和MainView,MainController来处理控制层、MainView来操作展示层。
主要代码:
MainActivity的代码:

package com.example.wz;

import com.example.wz.controller.MainController;
import com.example.wz.util.MyLog;
import com.example.wz.view.MainView;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;

public class MainActivity extends Activity {

 public MyLog log = new MyLog(this, true);

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  log.e("欢迎你加入测试项目.");
  link();
 }

 public MainController mainController;
 public MainView mainView;

 private void link() {
  this.mainController = new MainController(this);
  this.mainView = new MainView(this);

  this.mainController.thisView = this.mainView;
  this.mainView.thisController = this.mainController;

  this.mainView.initViews();
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  super.onTouchEvent(event);
  return mainController.onTouchEvent(event);
 }
}

MainController的代码:

package com.example.wz.controller;

import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;

import com.example.wz.MainActivity;
import com.example.wz.util.MyLog;
import com.example.wz.util.OpenLooper;
import com.example.wz.util.OpenLooper.LoopCallback;
import com.example.wz.view.MainView;

public class MainController {

 public MyLog log = new MyLog(this, true);

 public MainActivity mainActivity;
 public MainController thisController;
 public MainView thisView;

 public GestureDetector mGesture;

 public MainController(MainActivity mainActivity) {
  this.mainActivity = mainActivity;
  this.thisController = this;

  mGesture = new GestureDetector(mainActivity, new GestureListener());
  openLooper = new OpenLooper();
  openLooper.createOpenLooper();
  loopCallback = new ListLoopCallback(openLooper);
  openLooper.loopCallback = loopCallback;
 }

 public class TouchStatus {
  public int None = 4, Down = 1, Horizontal = 2, Vertical = 3, Up = 4;// LongPress = 5
  public int state = None;
 }

 public TouchStatus touchStatus = new TouchStatus();

 public class BodyStatus {
  public int Fixed = 0, Dragging = 1, Homing = 2, FlingHoming = 3, BoundaryHoming = 4;
  public int state = Fixed;
 }

 public BodyStatus bodyStatus = new BodyStatus();

 public class DrawStatus {
  public int Closed = 0, Open = 1, GoClosing = 2, GoOpening = 3;
  public int state = Closed;
 }

 public DrawStatus drawStatus = new DrawStatus();

 public class AreaStatus {
  public int A = 0, B = 1;
  public int state = A;
 }

 public AreaStatus areaStatus = new AreaStatus();

 public float touch_pre_x;
 public float touch_pre_y;

 public float currentTranslateX;

 public boolean onTouchEvent(MotionEvent event) {
  int action = event.getAction();

  float x = event.getX();
  float y = event.getY();

  if (action == MotionEvent.ACTION_DOWN) {
   this.touch_pre_x = x;
   this.touch_pre_y = y;

   if (touchStatus.state == touchStatus.None) {
    touchStatus.state = touchStatus.Down;
    log.e("Down ");
    if (x > thisView.maxTranslateX) {
     areaStatus.state = areaStatus.B;
    } else if (x <= thisView.maxTranslateX) {
     areaStatus.state = areaStatus.A;
    }
   }
  } else if (action == MotionEvent.ACTION_MOVE) {
   float Δy = (y - touch_pre_y);
   float Δx = (x - touch_pre_x);
   if (touchStatus.state == touchStatus.Down) {
    if (Δx * Δx + Δy * Δy > 400) {
     if (Δx * Δx > Δy * Δy) {
      touchStatus.state = touchStatus.Horizontal;
     } else {
      touchStatus.state = touchStatus.Vertical;
     }
     touch_pre_x = x;
     touch_pre_y = y;
     log.e("ACTION_MOVE ");
    }
   } else if (touchStatus.state == touchStatus.Horizontal) {
    currentTranslateX += Δx;
    this.touch_pre_x = x;
    this.touch_pre_y = y;
    if (currentTranslateX - thisView.maxTranslateX <= 0 && currentTranslateX >= 0) {
     setPosition();
    }
    log.e("Horizontal");
    bodyStatus.state = bodyStatus.Dragging;
   } else if (touchStatus.state == touchStatus.Vertical) {
    log.e("Vertical");
    bodyStatus.state = bodyStatus.Dragging;
   }
  } else if (action == MotionEvent.ACTION_UP) {
   log.e("ACTION_UP");
   if (bodyStatus.state == bodyStatus.Dragging) {
    if (touchStatus.state == touchStatus.Horizontal) {
     bodyStatus.state = bodyStatus.Homing;
     openLooper.start();
    } else if (touchStatus.state == touchStatus.Vertical) {
     if (drawStatus.state == drawStatus.Open && areaStatus.state == areaStatus.B) {
      bodyStatus.state = bodyStatus.Homing;
      drawStatus.state = drawStatus.GoClosing;
      openLooper.start();
     }
    }
   } else if (touchStatus.state == touchStatus.Down && areaStatus.state == areaStatus.B) {
    bodyStatus.state = bodyStatus.Homing;
    drawStatus.state = drawStatus.GoClosing;
    openLooper.start();
   }
   touchStatus.state = touchStatus.Up;
  }
  mGesture.onTouchEvent(event);
  return true;
 }

 class GestureListener extends SimpleOnGestureListener {

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   if (velocityX * velocityX + velocityY * velocityY > 250000) {
    if (velocityX * velocityX > velocityY * velocityY) {
     log.e("velocityX--" + velocityX);
     if (drawStatus.state == drawStatus.Closed && velocityX < 0) {
     } else if (drawStatus.state == drawStatus.Open && velocityX > 0) {
     } else {
      dxSpeed = velocityX;
      bodyStatus.state = bodyStatus.FlingHoming;
      openLooper.start();
     }
    } else {
     log.e("velocityY");
    }
   }
   return true;
  }

  public void onLongPress(MotionEvent event) {
  }

  public boolean onDoubleTap(MotionEvent event) {
   return false;
  }

  public boolean onDoubleTapEvent(MotionEvent event) {
   return false;
  }

  public boolean onSingleTapUp(MotionEvent event) {
   return false;
  }

  @Override
  public boolean onSingleTapConfirmed(MotionEvent event) {
   return false;
  }

  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
   return false;
  }
 }

 public void setPosition() {
  thisView.v1.setTranslationX(currentTranslateX - thisView.maxTranslateX);
  thisView.v2.setTranslationX(Math.abs(currentTranslateX));
 }

 float transleteSpeed = 3f;
 OpenLooper openLooper = null;
 LoopCallback loopCallback = null;

 public class ListLoopCallback extends LoopCallback {
  public ListLoopCallback(OpenLooper openLooper) {
   openLooper.super();
  }

  @Override
  public void loop(double ellapsedMillis) {
   if (bodyStatus.state == bodyStatus.Homing) {
    hommingView((float) ellapsedMillis);
   } else if (bodyStatus.state == bodyStatus.FlingHoming) {
    flingHomingView((float) ellapsedMillis);
   }
  }
 }

 public float ratio = 0.0008f;

 public void flingHomingView(float ellapsedMillis) {
  float distance = (float) ellapsedMillis * transleteSpeed;
  boolean isStop = false;
  if (drawStatus.state == drawStatus.Closed) {
   drawStatus.state = drawStatus.GoOpening;
  } else if (drawStatus.state == drawStatus.Open) {
   drawStatus.state = drawStatus.GoClosing;
  }
  if (drawStatus.state == drawStatus.GoClosing) {
   this.currentTranslateX -= distance;
   if (this.currentTranslateX <= 0) {
    this.currentTranslateX = 0;
    drawStatus.state = drawStatus.Closed;
    isStop = true;
    log.e("-------------1");
   }
  } else if (drawStatus.state == drawStatus.GoOpening) {
   this.currentTranslateX += distance;
   if (this.currentTranslateX >= thisView.maxTranslateX) {
    this.currentTranslateX = thisView.maxTranslateX;
    drawStatus.state = drawStatus.Open;
    isStop = true;
    log.e("-------------2");
   }
  }
  setPosition();
  if (isStop) {
   openLooper.stop();
  }
 }

 public float dxSpeed;

 public void dampenSpeed(long deltaMillis) {

  if (this.dxSpeed != 0.0f) {
   this.dxSpeed *= (1.0f - 0.002f * deltaMillis);
   if (Math.abs(this.dxSpeed) < 50f)
    this.dxSpeed = 0.0f;
  }
 }

 public void hommingView(float ellapsedMillis) {
  float distance = (float) ellapsedMillis * transleteSpeed;
  boolean isStop = false;
  if (drawStatus.state == drawStatus.Closed && this.currentTranslateX < thisView.maxTranslateX / 5) {
   this.currentTranslateX -= distance;
   if (this.currentTranslateX <= 0) {
    this.currentTranslateX = 0;
    drawStatus.state = drawStatus.Closed;
    isStop = true;
   }
  } else if (drawStatus.state == drawStatus.Closed && this.currentTranslateX >= thisView.maxTranslateX / 5) {
   this.currentTranslateX += distance;
   if (this.currentTranslateX >= thisView.maxTranslateX) {
    this.currentTranslateX = thisView.maxTranslateX;
    drawStatus.state = drawStatus.Open;
    isStop = true;
   }
  } else if (drawStatus.state == drawStatus.Open && this.currentTranslateX < thisView.maxTranslateX / 5 * 4) {
   this.currentTranslateX -= distance;
   if (this.currentTranslateX <= 0) {
    this.currentTranslateX = 0;
    drawStatus.state = drawStatus.Closed;
    isStop = true;
   }
  } else if (drawStatus.state == drawStatus.Open && this.currentTranslateX >= thisView.maxTranslateX / 5 * 4) {
   this.currentTranslateX += distance;
   if (this.currentTranslateX >= thisView.maxTranslateX) {
    this.currentTranslateX = thisView.maxTranslateX;
    drawStatus.state = drawStatus.Open;
    isStop = true;
   }
  } else if (drawStatus.state == drawStatus.GoClosing) {
   this.currentTranslateX -= distance;
   if (this.currentTranslateX <= 0) {
    this.currentTranslateX = 0;
    drawStatus.state = drawStatus.Closed;
    isStop = true;
   }
  }
  setPosition();
  if (isStop) {
   openLooper.stop();
   log.e("looper stop...");
  }
 }

}

MainView的代码:

package com.example.wz.view;

import android.graphics.Color;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.wz.MainActivity;
import com.example.wz.R;
import com.example.wz.controller.MainController;
import com.example.wz.util.MyLog;

public class MainView {

 public MyLog log = new MyLog(this, true);

 public MainActivity mainActivity;
 public MainController thisController;
 public MainView thisView;

 public MainView(MainActivity mainActivity) {
  this.mainActivity = mainActivity;
  this.thisView = this;
 }

 public DisplayMetrics displayMetrics;

 public float screenWidth;
 public float screenHeight;
 public float density;

 public float maxTranslateX;

 public RelativeLayout maxView;
 public RelativeLayout v1;
 public RelativeLayout v2;

 public void initViews() {
  this.displayMetrics = new DisplayMetrics();
  this.mainActivity.getWindowManager().getDefaultDisplay().getMetrics(this.displayMetrics);
  this.screenHeight = this.displayMetrics.heightPixels;
  this.screenWidth = this.displayMetrics.widthPixels;
  this.density = this.displayMetrics.density;
  this.maxTranslateX = this.screenWidth * 0.8f;
  this.mainActivity.setContentView(R.layout.activity_main);
  this.maxView = (RelativeLayout) this.mainActivity.findViewById(R.id.maxView);
  v1 = new RelativeLayout(mainActivity);
  v1.setBackgroundColor(Color.RED);
  RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) this.maxTranslateX, LayoutParams.MATCH_PARENT);
  this.maxView.addView(v1, params1);
  TextView t1 = new TextView(mainActivity);
  t1.setText("left menu bar");
  t1.setTextColor(Color.WHITE);
  v1.addView(t1);
  v1.setTranslationX(0 - this.maxTranslateX);
  v2 = new RelativeLayout(mainActivity);
  v2.setBackgroundColor(Color.parseColor("#0099cd"));
  RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams((int) this.screenWidth, LayoutParams.MATCH_PARENT);
  this.maxView.addView(v2, params2);
  v2.setTranslationX(0);
  TextView t2 = new TextView(mainActivity);
  t2.setText("body content");
  t2.setTextColor(Color.WHITE);
  v2.addView(t2);
 }
}

日志管理类MyLog:

package com.example.wz.util;

import android.util.Log;

public class MyLog {

 public static boolean isGlobalTurnOn = true;

 public boolean isTurnOn = true;
 public String tag = null;

 public MyLog(String tag, boolean isTurnOn) {
  this.tag = tag;
  this.isTurnOn = isTurnOn;
 }

 public MyLog(Object clazz, boolean isTurnOn) {
  this.tag = clazz.getClass().getSimpleName();
  this.isTurnOn = isTurnOn;
 }

 public void v(String message) {
  this.v(this.tag, message);
 }

 public void d(String message) {
  this.d(this.tag, message);
 }

 public void i(String message) {
  this.i(this.tag, message);
 }

 public void w(String message) {
  this.w(this.tag, message);
 }

 public void e(String message) {
  this.e(this.tag, message);
 }

 public void v(String tag, String message) {
  if (isTurnOn && isGlobalTurnOn) {
   Log.v(tag, message);
  }
 }

 public void d(String tag, String message) {
  if (isTurnOn && isGlobalTurnOn) {
   Log.d(tag, message);
  }
 }

 public void i(String tag, String message) {
  if (isTurnOn && isGlobalTurnOn) {
   Log.i(tag, message);
  }
 }

 public void w(String tag, String message) {
  if (isTurnOn && isGlobalTurnOn) {
   Log.w(tag, message);
  }
 }

 public void e(String tag, String message) {
  if (isTurnOn && isGlobalTurnOn) {
   Log.e(tag, message);
  }
 }

}

实现动画效果的核心类OpenLooper:

package com.example.wz.util;

import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.view.Choreographer;

public class OpenLooper {

 public LegacyAndroidSpringLooper legacyAndroidSpringLooper = null;
 public ChoreographerAndroidSpringLooper choreographerAndroidSpringLooper = null;
 public LoopCallback loopCallback = null;

 public void createOpenLooper() {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
   choreographerAndroidSpringLooper = new ChoreographerAndroidSpringLooper();
  } else {
   legacyAndroidSpringLooper = new LegacyAndroidSpringLooper();
  }
 }

 public void start() {
  if (choreographerAndroidSpringLooper != null) {
   choreographerAndroidSpringLooper.start();
  } else if (legacyAndroidSpringLooper != null) {
   legacyAndroidSpringLooper.start();
  }
 }

 public void stop() {
  if (choreographerAndroidSpringLooper != null) {
   choreographerAndroidSpringLooper.stop();
  } else if (legacyAndroidSpringLooper != null) {
   legacyAndroidSpringLooper.stop();
  }
 }

 public class LoopCallback {

  public void loop(double ellapsedMillis) {

  }
 }

 public void loop(double ellapsedMillis) {
  if (this.loopCallback != null) {
   this.loopCallback.loop(ellapsedMillis);
  }
 }

 public class LegacyAndroidSpringLooper {

  public Handler mHandler;
  public Runnable mLooperRunnable;
  public boolean mStarted;
  public long mLastTime;

  public LegacyAndroidSpringLooper() {
   initialize(new Handler());
  }

  public void initialize(Handler handler) {
   mHandler = handler;
   mLooperRunnable = new Runnable() {
    @Override
    public void run() {
     if (!mStarted) {
      return;
     }
     long currentTime = SystemClock.uptimeMillis();
     loop(currentTime - mLastTime);
     mHandler.post(mLooperRunnable);
    }
   };
  }

  public void start() {
   if (mStarted) {
    return;
   }
   mStarted = true;
   mLastTime = SystemClock.uptimeMillis();
   mHandler.removeCallbacks(mLooperRunnable);
   mHandler.post(mLooperRunnable);
  }

  public void stop() {
   mStarted = false;
   mHandler.removeCallbacks(mLooperRunnable);
  }
 }

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
 public class ChoreographerAndroidSpringLooper {

  public Choreographer mChoreographer;
  public Choreographer.FrameCallback mFrameCallback;
  public boolean mStarted;
  public long mLastTime;

  public ChoreographerAndroidSpringLooper() {
   initialize(Choreographer.getInstance());
  }

  public void initialize(Choreographer choreographer) {
   mChoreographer = choreographer;
   mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
     if (!mStarted) {
      return;
     }
     long currentTime = SystemClock.uptimeMillis();
     loop(currentTime - mLastTime);
     mLastTime = currentTime;
     mChoreographer.postFrameCallback(mFrameCallback);
    }
   };
  }

  public void start() {
   if (mStarted) {
    return;
   }
   mStarted = true;
   mLastTime = SystemClock.uptimeMillis();
   mChoreographer.removeFrameCallback(mFrameCallback);
   mChoreographer.postFrameCallback(mFrameCallback);
  }

  public void stop() {
   mStarted = false;
   mChoreographer.removeFrameCallback(mFrameCallback);
  }
 }
}

转载来自:http://blog.csdn.net/qxs965266509

源码下载:抽屉效果

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • Android App中DrawerLayout抽屉效果的菜单编写实例

    抽屉效果的导航菜单 看了很多应用,觉得这种侧滑的抽屉效果的菜单很好. 不用切换到另一个页面,也不用去按菜单的硬件按钮,直接在界面上一个按钮点击,菜单就滑出来,而且感觉能放很多东西. 库的引用: 首先, DrawerLayout这个类是在Support Library里的,需要加上android-support-v4.jar这个包. 然后程序中用时在前面导入import android.support.v4.widget.DrawerLayout; 如果找不到这个类,首先用SDK Manager更

  • Android编程实现仿美团或淘宝的多级分类菜单效果示例【附demo源码下载】

    本文实例讲述了Android编程实现仿美团或淘宝的多级分类菜单效果.分享给大家供大家参考,具体如下: 这里要实现的是诸如美团/淘宝/百度糯米 多级分类菜单效果.当分类数量非常多时可以考虑采用两级分类,而诸如美团这种表现方式是一个不错的选择. 首先上效果图:   主要代码: 1. PopupWindow初始化过程: popupWindow = new PopupWindow(this); View view = LayoutInflater.from(this).inflate(R.layout.

  • Android编程实现仿优酷旋转菜单效果(附demo源码)

    本文实例讲述了Android编程实现仿优酷旋转菜单效果.分享给大家供大家参考,具体如下: 首先,看下效果: 不好意思,不会制作动态图片,只好上传静态的了,如果谁会,请教教我吧. 首先,看下xml文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" a

  • Android仿优酷圆形菜单学习笔记分享

    先来看看效果: 首先来分析一下: 这个菜单可以分成三个菜单: 1.一级菜单(即最内圈那个菜单) 2.二级菜单(即中间圈那个菜单) 3.三级菜单(即最外圈那个菜单) 首先,可以将这三个菜单使用相对布局 一级菜单只有一个按钮(即home),可以控制二级和三级菜单 二级菜单有三个按钮(即menu),中间那个按钮可以控制三级菜单 三级菜单有七个按钮 那先把布局文件先写出来,采用三个相对布局(即每个菜单采用一个相对布局) <RelativeLayout xmlns:android="http://s

  • Android实现顶部导航菜单左右滑动效果

    本文给大家介绍在Android中如何实现顶部导航菜单左右滑动效果,具体内容如下 第一种解决方案: 实现原理是使用android-support-v4.jar包中ViewPager控件,在ViewPager控件中设置流布局,再在流布局中设置几项TextView,给每一个TextView设置相关参数,事件等.关于ViewPager控件可以设置全屏幕滑动效果,当然也可以实现局部滑动效果,下面介绍导航菜单. 关于导航菜单,相信大家对它并不陌生,比如在新闻客户端中就经常使用左右滑动菜单来显示不同类别的新闻

  • Android编程实现仿优酷圆盘旋转菜单效果的方法详解【附demo源码下载】

    本文实例讲述了Android编程实现仿优酷圆盘旋转菜单效果的方法.分享给大家供大家参考,具体如下: 目前,用户对安卓应用程序的UI设计要求越来越高,因此,掌握一些新颖的设计很有必要. 比如菜单,传统的菜单已经不能满足用户的需求. 其中优酷中圆盘旋转菜单的实现就比较优秀,这里我提供下我的思路及实现,仅供参考. 该菜单共分里外三层导航菜单.可以依次从外向里关闭三层菜单,也可以反向打开,并且伴有圆盘旋转的动画效果 首先,看下效果: 以下是具体的代码及解释: 1. 菜单布局文件: 大家看到主要有三个Ra

  • Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能

    如何为不同的list item呈现不同的菜单,本文实例就为大家介绍了Android仿微信或QQ滑动弹出编辑.删除菜单效果.增加下拉刷新等功能的实现,分享给大家供大家参考,具体内容如下 效果图: 1. 下载开源项目,并将其中的liberary导入到自己的项目中: 2. 使用SwipeMenuListView代替ListView,在页面中布局: <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh

  • Android自定义控件之仿优酷菜单

    去年的优酷HD版有过这样一种菜单,如下图: 应用打开之后,先是三个弧形的三级菜单,点击实体键menu之后,这三个菜单依次旋转退出,再点击实体键menu之后,一级菜单会旋转进入,点击一级菜单,二级菜单旋转进入,点击二级菜单的menu键,三级菜单旋转进入,再次点击二级菜单的旋转键,三级菜单又会旋转退出,这时再点击一级菜单,二级菜单退出,最后点击实体menu键,一级菜单退出. 总体来说实现这样的功能: (1)点击实体menu键时,如果界面上有菜单显示,不管有几个,全部依次退出,如果界面上没有菜单显示,

  • Android仿微信顶/底部菜单栏效果

    本文要实现仿微信微信底部菜单栏+顶部菜单栏,采用ViewPage来做,每一个page对应一个XML,当手指在ViewPage左右滑动时,就相应显示不同的page(其实就是xml)并且同时改变底部菜单按钮的图片变暗或变亮,同时如果点击底部菜单按钮,左右滑动page(其实就是xml)并且改变相应按钮的亮度. 一.布局 1.顶部菜单布局,命名为top_layout.xml <?xml version="1.0" encoding="utf-8"?> <R

  • Android实现下拉菜单Spinner效果

    Android 中下拉菜单,即如html中的<select>,关键在于调用setDropDownViewResource方法,以XML的方式定义下拉菜单要显示的模样 1.1.activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android

  • Android仿今日头条APP实现下拉导航选择菜单效果

    本文实例为大家分享了在Android中如何实现下拉导航选择菜单效果的全过程,供大家参考,具体内容如下 关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左右滑动或进行切换的导航菜单,也可以为了增强用户体验在应用中添加这样的下拉导航选择菜单效果. 关于它的实现原理,其实也是挺简单的,就是使用PopupWindow来进行展现,在显示时控制其高度并配置以相应的动画效果.在PopupWindow中我使用GridView

随机推荐