Android View體系(二)實現View滑動的六種方法
1.View的滑動簡介
View的滑動是Android實現自定義控制元件的基礎,同時在開發中我們也難免會遇到View的滑動的處理。其實不管是那種滑動的方式基本思想都是類似的:當觸控事件傳到View時,系統記下觸控點的座標,手指移動時系統記下移動後的觸控的座標並算出偏移量,並通過偏移量來修改View的座標。
實現View滑動有很多種方法,這篇文章主要講解六種滑動的方法,分別是:layout()、offsetLeftAndRight()與offsetTopAndBottom()、LayoutParams、動畫、scollTo與scollBy和Scroller;在下一篇文章我們會詳細介紹屬性動畫。
2.實現View滑動的六種方法
layout()
view進行繪製的時候會呼叫onLayout()方法來設定顯示的位置,因此我們同樣也可以通過修改View的left、top、right、bottom這四種屬性來控制View的座標。首先我們要自定義一個View,在onTouchEvent()方法中獲取觸控點的座標:
public boolean onTouchEvent(MotionEvent event) {
//獲取到手指處的橫座標和縱座標
int x = (int) event.getX();
int y = (int ) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
...
接下來我們在ACTION_MOVE事件中計算偏移量,再呼叫layout()方法重新放置這個自定義View的位置就好了:
case MotionEvent.ACTION_MOVE:
//計算移動的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//呼叫layout方法來重新放置它的位置
layout(getLeft()+offsetX, getTop()+offsetY,
getRight()+offsetX , getBottom()+offsetY);
break;
當我們每次移動時都會呼叫layout()方法來對自己重新佈局,從而達到移動View的效果。
自定義View的全部程式碼(CustomView.java):
package com.example.liuwangshu.moonviewslide;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
private int lastX;
private int lastY;
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context) {
super(context);
}
public boolean onTouchEvent(MotionEvent event) {
//獲取到手指處的橫座標和縱座標
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//計算移動的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//呼叫layout方法來重新放置它的位置
layout(getLeft()+offsetX, getTop()+offsetY,
getRight()+offsetX , getBottom()+offsetY);
break;
}
return true;
}
}
佈局中引用自定義View:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.liuwangshu.moonviewslide.CustomView
android:id="@+id/customview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="50dp"
android:background="@android:color/holo_red_light" />
</LinearLayout>
offsetLeftAndRight()與offsetTopAndBottom()
這兩種方法和layout()方法效果方法差不多,使用也差不多,我們將ACTION_MOVE中的程式碼替換成如下程式碼:
case MotionEvent.ACTION_MOVE:
//計算移動的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//對left和right進行偏移
offsetLeftAndRight(offsetX);
//對top和bottom進行偏移
offsetTopAndBottom(offsetY);
break;
LayoutParams(改變佈局引數)
LayoutParams主要儲存了一個View的佈局引數,因此我們可以通過LayoutParams來改變View的佈局的引數從而達到了改變View的位置的效果。同樣的我們將ACTION_MOVE中的程式碼替換成如下程式碼:
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
因為父控制元件是LinearLayout,所以我們用了LinearLayout.LayoutParams,如果父控制元件是RelativeLayout則要使用RelativeLayout.LayoutParams。除了使用佈局的LayoutParams外,我們還可以用ViewGroup.MarginLayoutParams來實現:
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
動畫
可以採用View動畫來移動,在res目錄新建anim資料夾並建立translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="300" android:duration="1000"/>
</set>
在Java程式碼中引用:
mCustomView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));
當然使用屬性動畫移動那就更簡單了,我們讓CustomView在1000毫秒內沿著X軸像右平移300畫素:
ObjectAnimator.ofFloat(mCustomView,"translationX",0,300).setDuration(1000).start();
scollTo與scollBy
scollTo(x,y)表示移動到一個具體的座標點,而scollBy(dx,dy)則表示移動的增量為dx、dy。其中scollBy最終也是要呼叫scollTo的。scollTo、scollBy移動的是View的內容,如果在ViewGroup中使用則是移動他所有的子View。我們將ACTION_MOVE中的程式碼替換成如下程式碼:
((View)getParent()).scrollBy(-offsetX,-offsetY);
這裡要實現CustomView隨著我們手指移動的效果的話,我們就需要將偏移量設定為負值。
Scroller
我們用scollTo/scollBy方法來進行滑動時,這個過程是瞬間完成的,所以使用者體驗不大好。這裡我們可以使用Scroller來實現有過度效果的滑動,這個過程不是瞬間完成的,而是在一定的時間間隔完成的。Scroller本身是不能實現View的滑動的,它需要配合View的computeScroll()方法才能彈性滑動的效果。
在這裡我們實現CustomView平滑的向右移動。
- 首先我們要初始化Scroller:
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
- 接下來重寫computeScroll()方法,系統會在繪製View的時候在draw()方法中呼叫該方法,這個方法中我們呼叫父類的scrollTo()方法並通過Scroller來不斷獲取當前的滾動值,每滑動一小段距離我們就呼叫invalidate()方法不斷的進行重繪,重繪就會呼叫computeScroll()方法,這樣我們就通過不斷的移動一個小的距離並連貫起來就實現了平滑移動的效果:
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
//通過不斷的重繪不斷的呼叫computeScroll方法
invalidate();
}
}
- 呼叫Scroller.startScroll()方法。我們在CustomView中寫一個smoothScrollTo()方法,呼叫Scroller.startScroll()方法,在2000毫秒內沿X軸平移delta畫素:
public void smoothScrollTo(int destX,int destY){
int scrollX=getScrollX();
int delta=destX-scrollX;
//1000秒內滑向destX
mScroller.startScroll(scrollX,0,delta,0,2000);
invalidate();
}
- 最後我們在ViewSlideActivity.java中呼叫CustomView的smoothScrollTo()方法:
//使用Scroll來進行平滑移動
mCustomView.smoothScrollTo(-400,0);
這裡我們是設定CustomView沿著X軸向右平移400畫素。