Android之仿美拍主要菜单滑动反弹效果
来源:互联网 发布:java web角色权限设计 编辑:程序博客网 时间:2024/05/02 01:52
本文主要记录一些零碎的东西
先说说要实现的效果:
菜单滑动最左边,还可以拖动一定距离,松开手后,view自动反弹会原位置
主要的坑:控制滑动的view响应touch事件,里面的子view无法响应click事件
左右滑动很多可以实现,最简单是 HorizontalScrollView,下面这个布局就可以实现上下左右滑动
<!-- 上下左右滑动 --> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <View android:layout_width="wrap_content" android:layout_height="wrap_content"/> </HorizontalScrollView> </ScrollView>
我一开始是直接在activity里监听view的touch事件,然后,遇到了上面的坑
自定义一个view,处理touch事件,View的移动使用
import android.content.Context;import android.os.Build;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.HorizontalScrollView;import android.widget.Scroller;/** * <p>Description: 滑动到边界,反弹效果的 ScrollView </p> * Created by slack on 2016/10/18 18:37 . */public class BoundScrollView extends HorizontalScrollView { private static final String TAG = "BoundScrollView"; private static final int DEFAULT_MOVE = 30; // 每次移动距离 private static final long DELAY_DURATION = 10L; // 默认延时时间 private View innerView;// 子View private int downX, tempX, moveX; private boolean isFirstTouch; public BoundScrollView(Context context) { this(context, null); } public BoundScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BoundScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); isFirstTouch = true; // 取消滑动到顶部或底部时边缘的黄色或蓝色底纹 if (Build.VERSION.SDK_INT >= 9) { this.setOverScrollMode(View.OVER_SCROLL_NEVER); } } @Override protected void onFinishInflate() { super.onFinishInflate(); if (getChildCount() > 0) { innerView = getChildAt(0); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (innerView != null) { handleTouchEvent(ev); } return super.onTouchEvent(ev); } /* * 这是因为ACTION_DOWN和子View的OnClick有冲突,如果touch点在有click事件的view上ACTION_DOWN进入不了 * */ private void handleTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN:// Log.i(TAG, "ACTION_DOWN..."); break; case MotionEvent.ACTION_MOVE: if (isFirstTouch) { downX = (int) event.getX(); moveX = downX; isFirstTouch = false; } if ((tempX = (int) event.getX() - moveX) != 0) { innerView.scrollBy(-tempX / 2, 0);// innerView.offsetLeftAndRight(tempX); // 都可以实现随手指滑动效果 } moveX = (int) event.getX();// Log.i(TAG, "ACTION_MOVE..." + tempX+ " , " + innerView.getScrollX()); break; case MotionEvent.ACTION_UP:// Log.i(TAG, "ACTION_UP..." + innerView.getScrollX()); isFirstTouch = true; // 这里需要处理反弹回去 if(innerView.getScrollX() != 0) { innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION); }// innerView.scrollBy( -innerView.getScrollX() , 0);// innerView.offsetLeftAndRight(downX - (int)event.getX() ); break; default: break; } } // 按滑动长度 反弹 private class BoundTask implements Runnable{ private int distanceX; public BoundTask(int distance) { this.distanceX = distance; } @Override public void run() {// Log.i(TAG, "BoundTask..." + innerView.getScrollX() + " " + distanceX); if(distanceX > 0){ if(distanceX > DEFAULT_MOVE) { innerView.scrollBy(DEFAULT_MOVE, 0); distanceX -= DEFAULT_MOVE; }else { innerView.scrollBy(distanceX, 0); distanceX = 0; } innerView.postDelayed(this,DELAY_DURATION); }else if(distanceX < 0){ if(distanceX < -DEFAULT_MOVE) { innerView.scrollBy(-DEFAULT_MOVE, 0); distanceX += DEFAULT_MOVE; }else { innerView.scrollBy(distanceX, 0); distanceX = 0; } innerView.postDelayed(this,DELAY_DURATION); } } }}
上面的代码有些问题,反弹动画是自己写的,感觉有些死板,调整移动距离和时间也感觉怪怪的
,觉得还是使用Google提供好的吧 Scroller,再自定义一个布局
FrameLayoutView
import android.content.Context;import android.util.AttributeSet;import android.widget.FrameLayout;import android.widget.Scroller;/** * <p>Description: innerView 纯粹为实现反弹效果 </p> * Created by slack on 2016/10/19 14:45 . * * Scroller的基本用法其实还是比较简单的,主要可以分为以下几个步骤: * 1. 创建Scroller的实例 * 2. 调用startScroll()方法来初始化滚动数据并刷新界面 * 3. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 */public class FrameLayoutView extends FrameLayout { private Scroller mScroller; public FrameLayoutView(Context context) { this(context,null); } public FrameLayoutView(Context context, AttributeSet attrs) { this(context, attrs,0); } public FrameLayoutView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller = new Scroller(context); } @Override public void computeScroll() { // 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 if ( mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } } public void startScroll(int startX, int startY, int dx, int dy, int duration){ mScroller.startScroll(startX, startY, dx, dy, duration); invalidate(); }}
BoundScrollView
handleTouchEvent 里 case MotionEvent.ACTION_UP:修改
case MotionEvent.ACTION_UP:// Log.i(TAG, "ACTION_UP..." + innerView.getScrollX()); isFirstTouch = true; // 这里需要处理反弹回去 if(innerView.getScrollX() != 0) { //这里使用了 viewGroup.getScrollX() 和 viewGroup.getScrollY() 作为起始坐标, // ScrollY 和 ScrollX 记录了使用 scrollBy 进行偏移的量 //所以使用他们就等于是使用了现在的坐标作为起始坐标, // 目的坐标为他们的负数,就是偏移量为0的位置,也是view在没有移动之前的位置 innerView.startScroll(innerView.getScrollX(), innerView.getScrollY(), -innerView.getScrollX(), -innerView.getScrollY(), 800);// innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION); }// innerView.scrollBy( -innerView.getScrollX() , 0);// innerView.offsetLeftAndRight(downX - (int)event.getX() ); break;完整的
import android.content.Context;import android.os.Build;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.HorizontalScrollView;/** * <p>Description: 滑动到边界,反弹效果的 ScrollView </p> * Created by slack on 2016/10/18 18:37 . */public class BoundScrollView extends HorizontalScrollView { private static final String TAG = "BoundScrollView"; private static final int DEFAULT_MOVE = 30; // 每次移动距离 private static final long DELAY_DURATION = 15L; // 默认延时时间 private FrameLayoutView innerView;// 子View private int downX, tempX, moveX; private boolean isFirstTouch; public BoundScrollView(Context context) { this(context, null); } public BoundScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BoundScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); isFirstTouch = true; // 取消滑动到顶部或底部时边缘的黄色或蓝色底纹 if (Build.VERSION.SDK_INT >= 9) { this.setOverScrollMode(View.OVER_SCROLL_NEVER); } } @Override protected void onFinishInflate() { super.onFinishInflate(); if (getChildCount() > 0) { innerView = (FrameLayoutView)getChildAt(0); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (innerView != null) { handleTouchEvent(ev); } return super.onTouchEvent(ev); } /* * 这是因为ACTION_DOWN和子View的OnClick有冲突,如果touch点在有click事件的view上ACTION_DOWN进入不了 * */ private void handleTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN:// Log.i(TAG, "ACTION_DOWN..."); break; case MotionEvent.ACTION_MOVE: if (isFirstTouch) { downX = (int) event.getRawX(); moveX = downX; isFirstTouch = false; } if ((tempX = (int) event.getRawX() - moveX) != 0) { innerView.scrollBy(-tempX / 2, 0);// innerView.offsetLeftAndRight(tempX); // 都可以实现随手指滑动效果 } moveX = (int) event.getX();// Log.i(TAG, "ACTION_MOVE..." + tempX+ " , " + innerView.getScrollX()); break; case MotionEvent.ACTION_UP:// Log.i(TAG, "ACTION_UP..." + innerView.getScrollX()); isFirstTouch = true; // 这里需要处理反弹回去 if(innerView.getScrollX() != 0) { //这里使用了 viewGroup.getScrollX() 和 viewGroup.getScrollY() 作为起始坐标, // ScrollY 和 ScrollX 记录了使用 scrollBy 进行偏移的量 //所以使用他们就等于是使用了现在的坐标作为起始坐标, // 目的坐标为他们的负数,就是偏移量为0的位置,也是view在没有移动之前的位置 innerView.startScroll(innerView.getScrollX(), innerView.getScrollY(), -innerView.getScrollX(), -innerView.getScrollY(), 800);// innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION); }// innerView.scrollBy( -innerView.getScrollX() , 0);// innerView.offsetLeftAndRight(downX - (int)event.getX() ); break; default: break; } } // 按滑动长度 反弹 , 自己处理的感觉有些死板 private class BoundTask implements Runnable{ private int distanceX; public BoundTask(int distance) { this.distanceX = distance; } @Override public void run() {// Log.i(TAG, "BoundTask..." + innerView.getScrollX() + " " + distanceX); if(distanceX > 0){ if(distanceX > DEFAULT_MOVE) { innerView.scrollBy(DEFAULT_MOVE, 0); distanceX -= DEFAULT_MOVE; }else { innerView.scrollBy(distanceX, 0); distanceX = 0; } innerView.postDelayed(this,DELAY_DURATION); }else if(distanceX < 0){ if(distanceX < -DEFAULT_MOVE) { innerView.scrollBy(-DEFAULT_MOVE, 0); distanceX += DEFAULT_MOVE; }else { innerView.scrollBy(distanceX, 0); distanceX = 0; } innerView.postDelayed(this,DELAY_DURATION); } } }}
然后界面的布局就变成
<com.benqu.wuta.views.BoundScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" > <com.benqu.wuta.views.FrameLayoutView android:layout_width="match_parent" android:layout_height="match_parent"> // your view.... </com.benqu.wuta.views.FrameLayoutView> </com.benqu.wuta.views.BoundScrollView>这样修改之后,反弹动画果然舒服多了。
0 0
- Android之仿美拍主要菜单滑动反弹效果
- Android高仿QQ左右滑动菜单的效果实现方式之安卓菜单左右滑动效果实现方式
- android listview 反弹效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Android实现导航菜单左右滑动效果
- Unity打开网页
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 有用的链接
- Ethereal 过滤规则
- C#索引器
- Android之仿美拍主要菜单滑动反弹效果
- 【转】____前端构建工具gulpjs的使用介绍及技巧
- DWR服务器推技术(实现消息实时推送功能)
- Qlik Sense Desktop 初步体验
- socket简单实例
- 举例说明enum(枚举类型)的使用
- opencv视频基础操作
- (示例1)创建视频 AR 应用
- SSO实现单点登录的原理