Android实现滑动方式汇总
2015-11-15 23:40
471 查看
一、Layout方法
在view绘制的时候会调用onLayout()方法来设置显示的位置,同样可以通过修改View的left、top、right、bottom四个属性来控制View的坐标。在每次回调onTouchEvent的时候,我们都获取一下触摸点的坐标,首先在ACTION_DOWN事件中记录触摸点的坐标,然后可以在ACTION_MOVE事件中计算偏移量,并将偏移量作用到layout 方法中,这样在每次移动后,View都会调用layout方法来对自己重新布局,从而达到移动View的效果。@Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; case MotionEvent.ACTION_MOVE: int offX = x - lastX; int offY = y - lastY; layout(getLeft() + offX, getTop() + offY, getRight() + offX, getBottom() + offY); lastX = x; lastY = y; break; case MotionEvent.ACTION_CANCEL: break; } return true; }
二、LayoutParams
LayoutParams保存了一个View的布局参数,可以通过LayoutParams来动态的修改一个布局参数,从而达到改变View位置的效果。使用getLayoutParams()来获取一个View的LayoutParams,当获取到偏移量之后就可以通过setLayoutParams来改变View的位置@Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; case MotionEvent.ACTION_MOVE: int offX = x - lastX; int offY = y - lastY; ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); layoutParams.leftMargin += offX; layoutParams.topMargin += offY; setLayoutParams(layoutParams); lastX = x; lastY = y; break; case MotionEvent.ACTION_CANCEL: break; } return true; }
三、scrollTo与scrollBy
View中提供了scrollTo、scrollBy两个方法来改变一个View的位置。这两个方法的区别非常好理解,scrollTo(x,y)表示移动到一个坐标点(x,y),而scrollBy(x,y)表示移动的增量为x,y。scrollTo、scrollBy方法移动的是View的content,即让view的内容移动,不妨想象手机屏幕时一个中空的盖板,盖板下面是一个巨大的画布,也就是我们想要显示的内容。当把这个盖板在画布上移动到某一处的时候,透过中间空的矩形,我们可以看见手机屏幕上显示的视图,而画布上其他地方的视图,则被盖板盖住了无法看到。我们的视图与这个例子非常的相似,我们没有看到的视图,并不代表他不存在,有可能只是在屏幕外面而已
@Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; case MotionEvent.ACTION_MOVE: int offX = lastX - x; int offY = lastY - y; ((View) getParent()).scrollBy(offX, offY); lastX = x; lastY = y; break; case MotionEvent.ACTION_CANCEL: break; } return true; }
四、Scroller与OverScroller
Scroller和OverScroller,这两个是AndroidUI框架下实现滚动效果的最关键的类,ScrollView内部的实现也是使用的OverScroller,所以熟练的使用这两个类的相关API,可以让我们满足大部分的开发需求。scrollTo()和scrollBy。这两个方法可以实现View内容的移动,注意,是内容,不是位置!是移动,不是滚动!什么叫做内容呢?比如说一个TextView,如果使用scrollTo(),那么移动的是里面的文字,而不是位置,scrollBy()也是一样的。那么为什么是移动,不是滚动呢?这是因为这两个方法完成的都是瞬间完成,即瞬移,而不是带有滚动过程的滚动,所以说,如果要实现效果比较好的滚动,光靠View自带的方法还是不行滴,还是要Scrollers出马
mScroller.getCurrX() //获取mScroller当前水平滚动的位置 mScroller.getCurrY() //获取mScroller当前竖直滚动的位置 mScroller.getFinalX() //获取mScroller最终停止的水平位置 mScroller.getFinalY() //获取mScroller最终停止的竖直位置 mScroller.setFinalX(int newX) //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置 mScroller.setFinalY(int newY) //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置 //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间 mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms mScroller.startScroll(int startX, int startY, int dx, int dy, int duration) mScroller.computeScrollOffset() //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。自定义一个CustomView,使用Scroller实现滚动:
import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.LinearLayout; import android.widget.Scroller; public class CustomView extends LinearLayout { private static final String TAG = "Scroller"; private OverScroller mScroller; public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mScroller = new OverScroller(context); } //调用此方法滚动到目标位置 public void smoothScrollTo(int fx, int fy) { int dx = fx - mScroller.getFinalX(); int dy = fy - mScroller.getFinalY(); smoothScrollBy(dx, dy); } //调用此方法设置滚动的相对偏移 public void smoothScrollBy(int dx, int dy) { //设置mScroller的滚动偏移量 mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy); invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果 } @Override public void computeScroll() { //先判断mScroller滚动是否完成 if (mScroller.computeScrollOffset()) { //这里调用View的scrollTo()完成实际的滚动 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //必须调用该方法,否则不一定能看到滚动效果 postInvalidate(); } super.computeScroll(); } }
五、ViewDragHelper
ViewDragHelper.Callback是连接ViewDragHelper与view之间的桥梁(这个view一般是指拥子view的容器即parentView);ViewDragHelper的实例是通过静态工厂方法创建的;
ViewDragHelper可以检测到是否触及到边缘;
ViewDragHelper并不是直接作用于要被拖动的View,而是使其控制的视图容器中的子View可以被拖动,如果要指定某个子view的行为,需要在Callback中想办法;
ViewDragHelper的本质其实是分析onInterceptTouchEvent和onTouchEvent的MotionEvent参数,然后根据分析的结果去改变一个容器中被拖动子View的位置( 通过offsetTopAndBottom(int offset)和offsetLeftAndRight(int offset)方法 )
能在触摸的时候判断当前拖动的是哪个子View;
1.ViewDragHelper的初始化
ViewDragHelper一般用在一个自定义ViewGroup的内部,比如下面自定义了一个继承于RelativeLayout的DragLayout:
public class DragLayout extends RelativeLayout { private ViewDragHelper mDragHelper; public DragLayout(Context context) { super(context, null); init(); } public DragLayout(Context context, AttributeSet attrs) { super(context, attrs, 0); init(); } private void init() { /** * @params ViewGroup forParent 必须是一个ViewGroup * @params float sensitivity 灵敏度 * @params Callback cb 回调 */ mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mDragHelper.cancel(); return false; } return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { mDragHelper.processTouchEvent(ev); return true; } private class DragHelperCallback extends ViewDragHelper.Callback { /** * 尝试捕获子view,一定要返回true * @param View child 尝试捕获的view * @param int pointerId 指示器id? * 这里可以决定哪个子view可以拖动 */ @Override public boolean tryCaptureView(View child, int pointerId) { return true; } @Override public void onViewCaptured(View capturedChild, int activePointerId) { Toast.makeText(getContext(), "onViewCaptured", Toast.LENGTH_SHORT).show(); } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { Toast.makeText(getContext(), "onViewReleased", Toast.LENGTH_SHORT).show(); } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); switch (state) { case ViewDragHelper.STATE_DRAGGING: // 正在被拖动 break; case ViewDragHelper.STATE_IDLE: // view没有被拖拽或者 正在进行fling/snap break; case ViewDragHelper.STATE_SETTLING: // fling完毕后被放置到一个位置 break; } } @Override public void onEdgeTouched(int edgeFlags, int pointerId) { Toast.makeText(getContext(), "edgeTouched", Toast.LENGTH_SHORT).show(); } /** * 当拖拽到状态改变时回调 */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); } /** * 处理水平方向上的拖动 * @param View child 被拖动的view * @param int left 移动到达的x轴的距离 * @param int dx 建议的移动的x距离 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { Log.d("DragLayout", "clampViewPositionHorizontal " + left + "," + dx); final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - child.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } /** * 处理竖直方向上的拖动 * @param View child 被拖动的view * @param int top 移动到达的y轴的距离 * @param int dy 建议的移动的y距离 */ @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); final int bottomBound = getHeight() - child.getHeight(); final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories