《View的事件體系》(二)View的滑動實現
Android手機因為螢幕小所以為了呈現更多的內容就要選擇使用滑動來顯示和隱藏一些內容,而如果想要做出絢麗的自定義控制元件也是避不開實現View的滑動的。
實現View的滑動可以通過以下三種方式:
(一) 通過View本身提供的scrollTo和scrollBy方法實現滑動
這兩個方法是View提供專門用於實現滑動的,但上一篇博文說過它有一個缺點就是他的滑動是非彈性的。
scrollTo有一句關鍵程式碼是:onScrollChanged(mScrollX,mScrollY,oldX,oldY);這說明scrollTo實現的是基於所傳引數的絕對滑動。
scrollBy只有一句方法體:scrollTo(mScrollX+x,mScrollY+y);scrollBy呼叫了scrollTo方法,實現了基於當前位置的相對滑動
mScrollX和mScrollY是View的兩個內部屬性,下面要重點講一下他們是什麼以及改變規律。結合上一篇文章講的“檢視座標”,它不受物理螢幕限制,可以延伸到無限遠。我們也說了View有幾個座標屬性,比如left,top等他們是相對於父容器而言的,比如下圖所示,以父容器左上角建立座標系稱為”佈局座標”,他是有邊界的,超過了該顯示區域的子View將不能顯示到父檢視的區域中。所以說這個View在父容器中的移動也是有邊界的,我們稱之為View邊緣,把View稱作父容器的內容,而父容器也是一個View。所以就可以明白了一個View可以有若干內容,scrollTo和scrollBy改變的是某一個內容的位置,View在佈局中的位置是不變的
(二) 通過動畫給View施加平移效果來實現動畫
使用動畫來移動View,主要操作的是View的translationX和tranlationY,這裡既可以採取傳統的View動畫,也可以採用屬性動畫。但是要很.注意,使用屬性動畫的時候,為了相容Android3.0以下的版本,要採用開源動畫庫nineoldandroid。
採用View動畫需要定義在res下的anim資料夾下
<?xml version="1.0" encoding="utf-8"?>
<setxmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:zAdjustment="normal" >
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="100"
android:toYDelta="100" />
</set>
如果採用屬性動畫直接定義在java程式碼中,下面的程式碼實現了一個View在100ms內向右移平移100畫素:
ObjectAnimator.ofFloat(targetView,”translationX”,0,100).setDuration(100).start();
關於動畫無法三言兩語說清楚的,會在以後的博文中更新講解Android的動畫機制。
使用View動畫實現滑動時要注意兩點:
(1)如果希望動畫結束後View的狀態得到保留而不是自動回到原處,需要將fillAfter屬性設為true,否則動畫完成後View會自動恢復到動畫前的狀態。
(2)View動畫只是對View影像的移動,但View的實體還停留在原處。比如我們移動了Button後點擊新位置卻沒有響應,但是原來的位置雖然看不到Button但點選後依然正常響應。也就是說新位置上只是View的影像,所以我們不能簡單的給一個View做平移還需要他在新位置上能夠繼續被觸發一系列監聽。
Android3.0以後就可以使用屬性動畫解決上面的兩個問題了,除此之外還有一個解決辦法:我們可以預先在新位置建立一個和原View一模一樣的View,而且連監聽也一樣,當目標View完成平移動畫後就把目標View隱藏,然後把預先設定的View顯示出來。
(三)通過改變View的LayoutParams使得View重新佈局實現滑動
第三種方法是改變佈局引數,即改變LayoutParams。
比如我們要把一個button向右平移100px,我們可以把他的LayoutParams中的marginLeft增加100px即可;也可以採用下面的方式(假設Button的父容器是水平方向的LinearLayout),我們可以事先在Button左邊放置一個空View,預設寬度是0,當我們滑動結束的時候,重新設定View的寬度,當空View寬度增大時,Button自然被擠到了右邊。那麼如何設定一個View的LayoutParames呢?如下所示:
MarginLayoutParams params =(MarginLayoutParams)button.getLayoutParams();
params.width += 100;
params.leftMargin +=100;
button.requestLayout();
//或者button.setLayoutParams(params);
(四)各種滑動方式對比
先看scrollTo和scrollBy,前面已經說過,他們是View提供的原生方法,用它可以方便的實現滑動效果而且不影響內部元素的監聽事件。但他的缺點是隻能滑動View的內容,不能滑動View本身。所以他適用於對View內容的滑動。
在Android3.0以上使用屬性動畫沒有明顯的缺點。但是使用View動畫或者Android3.0以下使用屬性動畫,均不能改變View實體的屬性,這在前面說過,但是如果沒有在View上新增監聽,使用它也是不錯的。因為很多複雜的效果必須使用動畫才能實現。
改變佈局引數的方式沒有明顯的缺點,主要適用於一些具有互動性的View。
下面是一個實現跟手滑動的自定義View的關鍵程式碼,只需要在onTouchEvent中處理ACTION_MOVE事件即可。
public boolean onTouchEvent(MotionEventevent) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
Log.d(TAG, "move, deltaX:" +deltaX + " deltaY:" + deltaY);
int translationX =(int)ViewHelper.getTranslationX(this) + deltaX;
int translationY =(int)ViewHelper.getTranslationY(this) + deltaY;
ViewHelper.setTranslationX(this,translationX);
ViewHelper.setTranslationY(this,translationY);
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}
在上面的程式碼中先通過getRawX和getRawY獲取手指當前座標,其次得到兩次滑動之間的位移,移動的時候採用動畫相容庫nineoldandroids中ViewHelper提供的setTranslationX 和setTranslationY。View中也有這兩個方法,但是View的這兩個方法只能相容Android3.0及其以上的版本。此外還有一系列方法,不在一一列舉。