【Android初級】如何實現一個比相簿更高大上的左右滑動特效(附原始碼)

snowyeti發表於2021-02-02

在Android裡面,想要實現一個類似相簿的左右滑動效果,我們除了可以用Gallery、HorizontalScrollView、ViewPager等控制元件,還可以用一個叫做 ViewFlipper 的類來代替實現,它繼承於 ViewAnimator。如見其名,這個類是跟動畫有關,會將新增到它裡面的兩個或者多個View做一個動畫,然後每次只顯示一個子View,通過在 View 之間切換時執行動畫,最終達到一個類似相簿能左右滑動的效果。

本次功能要實現的兩個基本效果

  1. 最基本的左右滑動效果
  2. 從螢幕的45度方向進入和退出的效果

實現思路

  1. 按照 ViewFlipper 的原始碼說明,它是將兩個或多個View用動畫展示出來。那麼我就在 ViewFlipper 內放入兩個佈局,每個佈局都包含一個 TextView 和 ImageView,分別用於顯示文字和圖片
  2. 既然要有動畫效果,我準備使用Android的位移動畫類 TranslateAnimation,設定起始的橫縱座標值
  3. 為了讓效果明顯,我會設定 ViewFlipper 的進入和退出螢幕的動畫,並且在左滑時呈現一個動畫、右滑時呈現另一個動畫(需要判斷是左滑還是右滑:重寫 onTouchEvent 方法,比較橫座標X的值的變化)

原始碼如下:

1、主Activity

// import語句省略
public class ViewFlipperDemo extends Activity {
    private static final String TAG = "ViewFlipperDemo";

    private ViewFlipper mViewFlipper;
    private float mOldTouchValue;

    @Override
    protected void onCreate(Bundle onSavedInstance) {
        super.onCreate(onSavedInstance);

        // 設定為全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.view_flipper_demo);
        mViewFlipper = findViewById(R.id.viewFlipper1);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mOldTouchValue = event.getX();
                break;

                case MotionEvent.ACTION_UP:
                    float currentX = event.getX();

                    // 手指向右滑動: 手指向右滑動時橫座標 X 的值會變大,因此 currentX 的值更大
                    if (mOldTouchValue < currentX) {

                        // 進入螢幕的動效
                        mViewFlipper.setInAnimation(AnimationHelper.inFromLeftAnimation());

                        // 退出螢幕的動效
                        mViewFlipper.setOutAnimation(AnimationHelper.outToRightAnimation());
                        mViewFlipper.showNext();
                    }

                    // 橫座標的值變小,說明是左滑
                    if (mOldTouchValue > currentX) {

                        // 進入螢幕的動效
                        mViewFlipper.setInAnimation(AnimationHelper.inFromRightAnimation());

                        // 退出螢幕的動效
                        mViewFlipper.setOutAnimation(AnimationHelper.outToLeftAnimation());
                        mViewFlipper.showPrevious();
                    }
                    break;

                default:
                    break;
        }
        return super.onTouchEvent(event);
    }
}

2、對應的佈局檔案 view_flipper_demo.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:textColor="@color/colorBlack"
              android:gravity="center"
              android:text="這是一個ViewFlipper樣例"
              android:paddingTop="20dp"/>

    <ViewFlipper android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:id="@+id/viewFlipper1">

        <LinearLayout android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:orientation="vertical"
                      android:gravity="center">

            <TextView android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:textColor="@color/colorBlue"
                      android:gravity="center"
                      android:text="這是第一個ViewFlipper頁面"/>

            <ImageView android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:src="@drawable/avasterdr"/>

        </LinearLayout>

        <LinearLayout android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:orientation="vertical"
                      android:gravity="center" >

            <TextView android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:textColor="@color/colorBlue"
                      android:gravity="center"
                      android:text="這是第二個ViewFlipper頁面"/>

            <ImageView android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:src="@drawable/avastertony"/>

        </LinearLayout>

    </ViewFlipper>

</LinearLayout>

3、動畫輔助類 AnimationHelper.java

public class AnimationHelper {

    // 左滑的進入動畫
    public static Animation inFromRightAnimation() {
        Animation inFromRight = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT,
                1.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f);
        inFromRight.setDuration(500);
        inFromRight.setInterpolator(new AccelerateInterpolator());
        return inFromRight;
    }

    // 左滑的退出動畫
    public static Animation outToLeftAnimation() {
        Animation outToLeft = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                -1.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f);
        outToLeft.setDuration(500);
        outToLeft.setInterpolator(new AccelerateInterpolator());
        return outToLeft;
    }

    // 右滑的進入動畫
    public static Animation inFromLeftAnimation() {
        Animation inFromLeft = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT,
                -1.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f);
        inFromLeft.setDuration(500);
        inFromLeft.setInterpolator(new AccelerateInterpolator());
        return inFromLeft;
    }

    // 右滑的退出動畫
    public static Animation outToRightAnimation() {
        Animation outToRight = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                1.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f,
                Animation.RELATIVE_TO_PARENT,
                0.0f);
        outToRight.setDuration(500);
        outToRight.setInterpolator(new AccelerateInterpolator());
        return outToRight;
    }
}

4、對應的效果圖如下

可以看到,這個左右滑動效果沒有任何酷炫的地方。我們不妨先來看看跟動畫相關的幾個重點地方:

(1)函式 setInAnimation:是指 View 進入螢幕的動效

(2)函式 setOutAnimation:是指 View 退出螢幕的動效

(3)TranslateAnimation的建構函式的引數解釋:

1、fromXType/toXType/fromYType/toYType,取值共有三個:

  • Animation.ABSOLUTE
  • Animation.RELATIVE_TO_SELF
  • Animation.RELATIVE_TO_PARENT

我這裡用的是 Animation.RELATIVE_TO_PARENT,當傳入該引數時,其餘幾個座標值需要傳入百分比引數(1.0表示100%);如果傳入 Animation.ABSOLUTE,座標值需要傳入螢幕上的絕對位置(比如1000,1000)

2、fromXValue:起點的橫座標值

3、toXValue:終點的橫座標值

4、fromYValue:起點的縱座標值

5、toYValue:終點的縱座標值

如果我們想讓這個效果變成45度從螢幕的四個角進入和退出,那程式碼就應該這麼寫(注意程式碼中傳入的 4 個橫縱座標值):

// 左滑的進入動畫
public static Animation inFromRightAnimation() {
    Animation inFromRight = new TranslateAnimation(
            Animation.RELATIVE_TO_PARENT,
            1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            -1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f);
    inFromRight.setDuration(500);
    inFromRight.setInterpolator(new AccelerateInterpolator());
    return inFromRight;
}

// 左滑的退出動畫
public static Animation outToLeftAnimation() {
    Animation outToLeft = new TranslateAnimation(
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            -1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            1.0f);
    outToLeft.setDuration(500);
    outToLeft.setInterpolator(new AccelerateInterpolator());
    return outToLeft;
}

// 右滑的進入動畫
public static Animation inFromLeftAnimation() {
    Animation inFromLeft = new TranslateAnimation(
            Animation.RELATIVE_TO_PARENT,
            -1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            -1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f);
    inFromLeft.setDuration(500);
    inFromLeft.setInterpolator(new AccelerateInterpolator());
    return inFromLeft;
}

// 右滑的退出動畫
public static Animation outToRightAnimation() {
    Animation outToRight = new TranslateAnimation(
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            1.0f,
            Animation.RELATIVE_TO_PARENT,
            0.0f,
            Animation.RELATIVE_TO_PARENT,
            1.0f);
    outToRight.setDuration(500);
    outToRight.setInterpolator(new AccelerateInterpolator());
    return outToRight;
}

對應的效果如下:

之所以有 -1.0f 這個值,是因為螢幕上的橫縱座標值的分佈可以用如下象限來表示:

ViewFlipper中的 View 就位於象限的中心位置。因此,如果動畫從左上角進入,那麼它的起始橫縱座標就是(-1,-1)。大家可以按照這個思路去實現自己想要的動效。

歡迎交流~

相關文章