android完美slidingmenu滑动按钮
来源:互联网 发布:网络主播经纪公司合同 编辑:程序博客网 时间:2024/06/05 15:52
想实现类似网易新闻的右滑出menu的那种效果(youTube也有那种效果),网上找了种种方法和代码例子,发现都达不到想要的效果。发现他们大部分方法,不是重写HorizontalScrollView, 就是重写SlidingDrawer。
其实想像一下那个交互的直实情况,完全不用绕圏子,原理就可以是将上面的一个View拨开,将下面的View显示出来而以。 不如自己重写一个基本布局.
demo下载
先看一下做出来的效果吧,有人说没图说真相, 这个自己多嵌套了一层。(每滑完一层,右边就变灰,表示不可操作了)
右滑---------->再右滑---------->
最大的特点是如果你要优化自己的代码成这个效果,代码改动量非常小,只需要调整一下布局,改改View的位置就可以了。甚至代码可以不用修改。
下面看看实现过程
首先,两个View是可以重叠的。我们使用FrameLayout作为继承类。命名为ScrollDrawerView
这里要保证FrameLayout里有两个子View, 做很多事情先都要先检测一下。
/** * 检查设置top, bottom * @return */private boolean checkTopBottomOk() {if (mBottomView != null && mTopView != null) {return mBottomView != mTopView;}int count = getChildCount();if (mBottomView == null && mTopView == null) {if (count != 2) {return false;} else {mBottomView = getChildAt(0);mTopView = getChildAt(1);return mBottomView != mTopView;}} else {if (count != 1) {return false;} else {View v = getChildAt(0);if (mBottomView != null) {mTopView = v;} else {mBottomView = v;}return mBottomView != mTopView;}}} /** * 设置(替换)抽屉的下层 * @param v */ public void setBottomView(View v) { this.addView(v, 0); mBottomView = v; } /** * 设置(替换)抽屉的上层 * @param v */ public void setTopView(View v) { this.addView(v, 1); mTopView = v; }
需要灵活一点的话,就是继可以在layout文件中去配置View的位置,也可以在代码中进行设置。见上面的代码。
2, 要写View的话,onInterceptTouchEvent和onTouchEvent是必须要处理的。因为之前对这些只是一知半解, 也懒得去细想。于是用一个偷懒的办法,从ViewPager那里“借”点东西喽。因为ViewPager跟我们的事件拦截非常相似。如果子View里面内容可以滑动,则不会触发父View的onTouchEvent事件。干脆将onInterceptTouchEvent抄过来,稍作修改,嘿嘿大功告成。注意canScroll方法是核心
private boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v instanceof ViewGroup) { final ViewGroup group = (ViewGroup) v; final int scrollX = v.getScrollX(); final int scrollY = v.getScrollY(); final int count = group.getChildCount(); // Count backwards - let topmost views consume scroll distance first. for (int i = count - 1; i >= 0; i--) { // TODO: Add versioned support here for transformed views. // This will not work for transformed views in Honeycomb+ final View child = group.getChildAt(i); if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && canScroll(child, true, dx, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) { return true; } } } return checkV && v.canScrollHorizontally(-dx) && v.isEnabled(); }另外拓展性想稍强一点, 可以自嵌套的话。也要重写canScrollHorizontally
@Overridepublic boolean canScrollHorizontally(int direction) {if (checkTopBottomOk()) {int postion = mTopView.getLeft() - direction;return postion >= 0 && postion <= mBottomView.getWidth(); } else {return false;}}上面的意思就是mTopView的位置只能在0 到mBottomView.getWidth()的范围,不能越界。
看onTouchEvent的处理。ACTION_DOWN只是一个初始化的处理。而且很多情况不会运行到,原因是事件的起源是从onInterceptTouchEvent的ACTION_MOVE那里才决定到要去拦截处理这个事件。因此onTouchEvent的很大机率是从ACTION_MOVE开始接收的。
final int activePointerIndex = event.findPointerIndex(mActivePointerId);final int x = (int) event.getX(activePointerIndex);int deltaX = mLastMotionX - x;if (!mIsBeingDragged) {final int y = (int) event.getY(activePointerIndex);int yDiff = Math.abs(y - mLastMotionY);if (Math.abs(deltaX) > mTouchSlop && Math.abs(deltaX) > yDiff) {final ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}mIsBeingDragged = true;if (deltaX > 0) {deltaX -= mTouchSlop;} else {deltaX += mTouchSlop;}}}if (mIsBeingDragged) {mLastMotionX = x;int bottomWidth = mBottomView.getWidth();int left = mTopView.getLeft() - deltaX;if (left < 0) {left = 0;} else if (left > bottomWidth) {left = bottomWidth;}if (left == 0 || left == bottomWidth) {mVelocityTracker.clear();}//位置随手指变动mTopView.setLeft(left);}}上面是ACTION_MOVE的处理,其分为两部分,if(!mIsBeingDragged){}里面是去决定是否真的去执行拖动事件。 if(mIsBeingDragged){} 里面是让mTopView的位置随手的移动而变动。
看下ACTION_UP的处理。这里决定了滑动过后的开关状态。
final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(500, mMaximumVelocity);int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);final int activePointerIndex = event.findPointerIndex(mActivePointerId);final int x = (int) event.getX(activePointerIndex);int xDiff = Math.abs(mInitialMotionX - x );if ((Math.abs(initialVelocity) > mDirectVelocity)) {//速度足够大,执行切换opener(initialVelocity > 0);} else if ((Math.abs(initialVelocity) > mMinimumVelocity) && (xDiff > mBottomView.getWidth() / 4)) {//速度不太大,但移动距离够长,也执行切换opener(initialVelocity > 0);}else {//按现在位置,不到一半就返回,到了一半就过去opener(mTopView.getLeft() >= mBottomView.getWidth()/2);}
根据速度,移动位置,最终位置三个条件来决定状态的开关, 这里很好理解了。
然后再用mScroller处理一下滑动的动画(关联函数opener, computeScroll)。
再处理一下滑开后使mTopView无效(关联函数setViewEnabled)。
再处理一下Layout后mTopView的复问问题(onLayout).再修理一下边边角角。
OK,搞定了,效果不错。还可以嵌套使用。可以延伸出很多有意思的效果来。
我比较懒很多东西也说不清楚。。上面只是一个大概思路。详细的看代码就OK了
- android完美slidingmenu滑动按钮
- android滑动菜单SlidingMenu
- android 滑动菜单SlidingMenu实现
- Android SlidingMenu demo(滑动菜单)
- android 滑动菜单之SlidingMenu
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- (转)android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- Android实现滑动菜单—SlidingMenu
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- android 滑动菜单SlidingMenu的实现
- .Net平台Bug汇总
- [Boost基础]并发编程——Thread多线程(二)
- java方法参数校验实践
- 字符串中数字统计解题报告
- 栈应用---计算表达式
- android完美slidingmenu滑动按钮
- unicode gb2312对应表
- linux 软阵列管理:mdadm howto
- line 1:syntax error: unexpected "("
- 从多方查找资源并测试终于解决了问题,特记录下。 其实如果在安装的时候,正确的安装,就不会出现下面的信息了. 如在Linux下安装,提示密码的时候,输入Root账号的密码就不会出现这个问题了!
- Android TCP/IP 发送接收16进制数据
- UVa 10763 - Foreign Exchange
- Android处理9.png文件流程
- PHP环境搭建:Windows 7下安装配置PHP+Apache+Mysql环境教程