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,对于这两种方法,大家可以查阅网上资料,就不详细介绍了。
 
(以上内容来源于看《Android群英传》的总结)

 

posted @ 2016-11-28 14:28  天涯海角路  阅读(120)  评论(0)    收藏  举报