您的位置:首页 > 移动开发 > Android开发

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;
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android