Android滑动的实现杂七杂八

下面通过一个例子来总结实现滑动的几种方式,例子的主要功能就是让我们的自定义View能够随着手指的移动而移动。
布局文件如下:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent">  
  6.     <com.scu.lly.dragviewtest.view.DragView  
  7.         android:layout_width="100dp"  
  8.         android:layout_height="100dp" />  
  9. </LinearLayout>  

方式一:layout方法

在View进行绘制时,会调用onLayout()方法来设置显示的位置,因此,我们可以通过修改View的left、top、right、bottom四个属性来控制View的坐标。要控制View随手指滑动,因此需要在onTouchEvent()事件中进行滑动控制。代码如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DragView extends View{  
  2.   
  3.     private int mLastX;  
  4.     private int mLastY;  
  5.   
  6.     public DragView(Context context) {  
  7.         super(context);  
  8.         init();  
  9.     }  
  10.   
  11.     public DragView(Context context, AttributeSet attrs) {  
  12.         super(context, attrs);  
  13.         init();  
  14.     }  
  15.   
  16.     public DragView(Context context, AttributeSet attrs, int defStyleAttr) {  
  17.         super(context, attrs, defStyleAttr);  
  18.         init();  
  19.     }  
  20.   
  21.     private void init(){  
  22.         setBackgroundColor(Color.BLUE);  
  23.     }  
  24.   
  25.     @Override  
  26.     public boolean onTouchEvent(MotionEvent ev) {  
  27.         int x = (int) ev.getX();  
  28.         int y = (int) ev.getY();  
  29.         switch (ev.getAction()){  
  30.             case MotionEvent.ACTION_DOWN:  
  31.                 mLastX = x;  
  32.                 mLastY = y;  
  33.                 break;  
  34.             case MotionEvent.ACTION_MOVE:  
  35.                 int offsetX = x - mLastX;  
  36.                 int offsetY = y - mLastY;  
  37.                 //调整layout的四个坐标  
  38.                 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);  
  39.                 break;  
  40.         }  
  41.         return true;  
  42.     }  
  43. }  

方式二:offsetLeftAndRight()和offsetTopAndBottom()

这两个方法其实是对上面那种layout设置方式的封装、简化,在layout中,左left、右right两个方向都是加上offsetX,上top、下bottom两个方向都是加上offsetY,为了简化设置四个方向,Android提供了offsetLeftAndRight()来代替左右方向的设置,用offsetTopAndBottom()来代替上下方向的设置。
我们只需要修改上面代码ACTION_MOVE的部分,如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="white-space:pre">    </span>case MotionEvent.ACTION_MOVE:  
  2.                 int offsetX = x - mLastX;  
  3.                 int offsetY = y - mLastY;  
  4.                 //调整layout的四个坐标  
  5.                 //layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);  
  6.                 //使用简写形式  
  7.                 offsetLeftAndRight(offsetX);  
  8.                 offsetTopAndBottom(offsetY);  
  9.                 break;  

方式三:LayoutParams

LayoutParams保存了一个View的布局参数,因此我们可以通过动态改变LayoutParams中的布局参数来达到改变View的位置效果。通过getLayoutParams()方法来获取View的LayoutParams,这里获取到的LayoutParams需要根据View所在父布局的类型来设置不同的类型,比如,我们这个自定义View是放在LinearLayout中的,那么通过getLayoutParams()获取到的就是LinearLayout.LayoutParams。因此,通过getLayoutParams()获取到LayoutParams的前提就是这个View需要有一个父布局。
同样,我们只需要修改上面代码ACTION_MOVE的部分,如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case MotionEvent.ACTION_MOVE:  
  2.     int offsetX = x - mLastX;  
  3.     int offsetY = y - mLastY;  
  4.     LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();  
  5.     lp.leftMargin = getLeft() + offsetX;  
  6.     lp.topMargin = getTop() + offsetY;  
  7.     setLayoutParams(lp);  
  8.     break;  
可以看到,通过LayoutParams改变一个View的位置时,改变的是这个View的Margin属性,这也是为什么这种方式一定要有父布局的原因,只有有了父布局,margin属性的设置才会起作用。
对于使用LayoutParams这种方式改变View位置,如果我们不想考虑父布局的类型,还可以使用ViewGroup.MarginLayoutParams来进行设置,这样也更加方便。如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case MotionEvent.ACTION_MOVE:  
  2.     int offsetX = x - mLastX;  
  3.     int offsetY = y - mLastY;  
  4.     //LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();  
  5.     ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();  
  6.     lp.leftMargin = getLeft() + offsetX;  
  7.     lp.topMargin = getTop() + offsetY;  
  8.     setLayoutParams(lp);  
  9.     break;  
效果是一样的。

方式四:scrollTo与scrollBy

关于scrollTo()和scrollBy()方法,我这篇文章《Android Scroller大揭秘》中有详细介绍。

使用scrollTo()和scrollBy()方法需要注意的一点是,scrollTo()和scrollBy()方法移动的是View的content,即让View的内容移动,如果在ViewGroup中使用scrollTo()和scrollBy()方法,那么移动的将是所有的子View,如果在View中使用,那么移动的将是View的内容。例如,TextView,content就是它的文本,ImageView,content就是它的drawable对象。

因此,上面例子中我们如果直接这样使用:
scrollBy(offsetX,offsetY);
发现View并没有移动,但其实是发生了移动的,只不过此时移动的是View中的内容,而我们例子中的content什么也没有。
所以,我们要想使这个View发生移动,我们就应该在View所在的ViewGroup中使用scrollBy或scrollTo方法来进行移动。同时,使用者两个方法进行移动的时候,注意此时的坐标方向与平常是相反的,具体在《Android Scroller大揭秘》有讲解。代码如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case MotionEvent.ACTION_MOVE:  
  2.     //int offsetX = x - mLastX;  
  3.     //int offsetY = y - mLastY;  
  4.     //此时,计算坐标是相反的  
  5.     int offsetX = mLastX - x;  
  6.     int offsetY = mLastY - y;  
  7.     //让View所在的ViewGroup进行移动  
  8.     ((View)getParent()).scrollBy(offsetX,offsetY);  
  9.      break;  

方式五:Scroller

通过Scroller这个辅助类,配合scrollTo和scrollBy可以实现一些更加高级的滑动效果,关于Scroller类的具体介绍,同样在这篇文章中有详解《Android Scroller大揭秘》

这里,我们只是结合上面这个例子实现一个简单的功能,当我们滑动完毕抬起手指后,View自动回弹到原来的位置。代码如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DragView extends View{  
  2.   
  3. private int mLastX;  
  4.     private int mLastY;  
  5.     private Scroller mScroller;  
  6.   
  7.     public DragView(Context context) {  
  8.         super(context);  
  9.         init(context);  
  10.     }  
  11.   
  12.     public DragView(Context context, AttributeSet attrs) {  
  13.         super(context, attrs);  
  14.         init(context);  
  15.     }  
  16.   
  17.     public DragView(Context context, AttributeSet attrs, int defStyleAttr) {  
  18.         super(context, attrs, defStyleAttr);  
  19.         init(context);  
  20.     }  
  21.   
  22.     private void init(Context context){  
  23.         setBackgroundColor(Color.BLUE);  
  24.         mScroller = new Scroller(context);  
  25.     }  
  26.   
  27.     @Override  
  28.     public boolean onTouchEvent(MotionEvent ev) {  
  29.         int x = (int) ev.getX();  
  30.         int y = (int) ev.getY();  
  31.         switch (ev.getAction()){  
  32.             case MotionEvent.ACTION_DOWN:  
  33.                 mLastX = x;  
  34.                 mLastY = y;  
  35.                 break;  
  36.             case MotionEvent.ACTION_MOVE:  
  37.                 //int offsetX = x - mLastX;  
  38.                 //int offsetY = y - mLastY;  
  39.                 //此时,计算坐标是相反的  
  40.                 int offsetX = mLastX - x;  
  41.                 int offsetY = mLastY - y;  
  42.                 //让View所在的ViewGroup进行移动  
  43.                 ((View)getParent()).scrollBy(offsetX,offsetY);  
  44.                 break;  
  45.             case MotionEvent.ACTION_UP:  
  46.                 View viewGroup = (View) getParent();  
  47.                 mScroller.startScroll(viewGroup.getScrollX(),viewGroup.getScrollY(),-viewGroup.getScrollX(),-viewGroup.getScrollY());  
  48.                 //记住需要invalidate  
  49.                 invalidate();  
  50.                 break;  
  51.         }  
  52.     return true;  
  53. }  
  54.   
  55.     @Override  
  56.     public void computeScroll() {  
  57.         super.computeScroll();  
  58.         if(mScroller.computeScrollOffset()){  
  59.             ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());  
  60.         //记住,需要不断调用invalidate进行重绘  
  61.         invalidate();  
  62.         }  
  63.     }  
  64. }  

以上五种方法就是常用的滑动View的方法。还有两种方式能够控制一个View的移动效果:属性动画和使用ViewDragHelper,对于这两种方法,大家可以查阅网上资料,就不详细介绍了。


以上内容,来源于网友的总结:http://blog.csdn.net/shakespeare001/article/details/51657795



View的位置参数 top left right bottom 相对于父容器而言,top是左上角纵坐标,left是左上角横坐标,right是右下角横坐标,bottom是右下角纵坐标,所以可得出View的宽高
width = right-left
height = bottom-top
left = getLeft() right = getRight() top = getTop Bottom = getBottom();



getX/getY    返回的是相对于当前View左上角的X和Y坐标
getRawX/getRawY    返回的是相对于手机屏幕左上角的x和y坐标   140页


TouchSlop 是系统所能识别出的被认为是滑动的最小距离,它是个常量,和设备有关,不同设备上,值可能不同。
可通过ViewConfiguration.get(getContext()).getScaledTouchSlop()获取这个值。


VelocityTracker 速度追踪  用于追踪手指在滑动过程的速度,水平方向和垂直方向。
使用方法如下:
在View的onTouchEvent方法中追踪当前单击事件的速度:
VelocityTracker  velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
//一段时间内手指滑过的像素数
VelocityTracker.computeCurrentVelocity(1000);
//x轴方向速度
int xVelocity = (int)velocityTracker.getXVelocity();
//y轴方向速度
int yVelocity = (int)velocityTracker.getYVelocity();
//当不需要使用时
velocityTracker.clear();
velocityTracker.recycle();


Scroller 弹性滑动对象,用于实现View的弹性滑动
使用View的scrollTo/scrollBy来进行滑动,其过程是瞬间完成的,用户体验不好,可以使用Scroller来完成。
Scroller本身无法让View弹性滑动,需要和View的computeScroll方法配合使用共同完成,典型代码是固定的。


Scroller scroller = new Scroller(mContext);
//缓慢滚动到指定位置
private void smoothScrollTo(int destX,int destY){


      int scrollX = getScrollX();
      int delta = destX - scrollX;
      //1000ms内滑向destX,效果就是慢慢滑动
      mScroller.startScroll(scrollX,0,delta,0,1000);
      invalidate();


}


@override
public void computeScroll(){


     if(mScroller.computeScrollOffset()){
        
        scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
        postInvalidate();


       }


}




View的滑动,滑动时应用的标配,例如下拉刷新,SlidingMemu,他们的基础都是滑动。
掌握滑动的方法是实现绚丽的自定义控件的基础,主要通过三种方式可以实现View的滑动。
1、通过View本身提供的scrollTo/scrollBy方法来实现滑动
2、通过动画给View施加平移效果来实现滑动
3、通过改变View的LayoutParams使得View重新布局从而实现滑动。


一一分析:
1、使用scrollTo/scrollBy
2、使用动画进行滑动,实现一个view平移,平移就是一种滑动。主要是操作View的translationX和translationY属性,既可以
使用传统的view动画,也可以采用属性动画。
3、改变布局参数是实现View滑动,即改变LayoutParams。比如想把一个Button向右平移100px,我们只需要将这个Button的LayoutParams
里的marginLeft参数的值增加100px即可.或者在Button的左边放置一个空的View,它的默认宽度为0,当需要右移动Button时,重新设置View的宽度即可。


MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
params.width += 100;
params.leftMargin +=100;
mButton.requestLayout();
//或者mButton.setLayoutParams(params);


以上三种方式对比:
1、scrollTo\srollBy方式,只能滑动View的内容,不能滑动View本身,不影响内部元素的单击事件。
2、动画,android3.0以上采用没有明显的缺点,3.0以下,均不能改变View本身的属性,如果需要用户的交互,动画做滑动合适,否则不太适合,但动画可以实现一些复杂的效果。
3、改变布局参数,操作稍微复杂,适用于有交互的View


如何实现弹性滑动呢?
共同思想:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成,例如通过Scroller、Handler#postDelayed以及Thread#sleep等

以上内容来源Android开发艺术探索一书。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值