Android真正仿攜程首頁view實現

一 需求:實現view的點擊縮放效果,類似於攜程首頁。

二 需求分析:對於單純的view的縮放還是比較簡單的,我們使用單純的android縮放動畫就可以實現。但是按照攜程首頁的view來做的話,裏面是有很多細節是我們需要處理的。  

     1 對於一張圖片,當我們按下然後左右滑動時它應該仍然處於縮放狀態,直到我們的手指脫離開view的邊界纔回到初始狀態。

     2 如果說我們的view的父控件是scrollview的話,這裏就需要用到android中事件分發的相關知識了。

        當我們按下view時,需要view來處理接下來的事件,但如果我們是向上或者向下滑動的話,當滑出一小段距離時,我們需要把view重置爲

        初始狀態,同時把事件交給scrollview來處理使其可以進行上下滑動。

三 說了這麼多,相信大家還是有點摸不着頭腦的,沒關係,我們先來張屏幕截圖,然後我們直接貼出源碼。

運行效果:


縮放view源碼:

package com.example.asiatravel.ctriphomescaleview.view;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;

/**
 * Created by kuangxiaoguo on 16/8/30.
 */
public class CTripHomeScaleView extends ImageView {

    /**
     * 動畫持續時長
     */
    private static final int DURATION = 100;
    /**
     * 快速點擊的時間間隔
     */
    private static final int TIME_DELAY = 500;
    /**
     * 滑動最小距離
     */
    private static final int MIN_MOVE_DPI = 10;
    /**
     * 縮放的scale
     */
    private static final float SMALL_SCALE = 0.95f;
    /**
     * 初始scale
     */
    private static final float ONE_SCALE = 1f;
    /**
     * 判斷縮小動畫有沒有執行過
     */
    private boolean hasDoneAnimation;
    /**
     * 點擊事件監聽
     */
    private OnClickListener listener;
    /**
     * 開始動畫
     */
    private AnimatorSet beginAnimatorSet;
    /**
     * scale返回動畫
     */
    private AnimatorSet backAnimatorSet;
    private int downX;
    private int downY;
    private long lastClickTime;

    public CTripHomeScaleView(Context context) {
        this(context, null);
    }

    public CTripHomeScaleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CTripHomeScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAnimation();
    }

    /**
     * Init scale animation
     */
    private void initAnimation() {
        ObjectAnimator beginXAnimation = ObjectAnimator.ofFloat(this, "scaleX", ONE_SCALE, SMALL_SCALE).setDuration(DURATION);
        beginXAnimation.setInterpolator(new LinearInterpolator());
        ObjectAnimator beginYAnimation = ObjectAnimator.ofFloat(this, "scaleY", ONE_SCALE, SMALL_SCALE).setDuration(DURATION);
        beginYAnimation.setInterpolator(new LinearInterpolator());
        ObjectAnimator backXAnimation = ObjectAnimator.ofFloat(this, "scaleX", SMALL_SCALE, ONE_SCALE).setDuration(DURATION);
        backXAnimation.setInterpolator(new LinearInterpolator());
        ObjectAnimator backYAnimation = ObjectAnimator.ofFloat(this, "scaleY", SMALL_SCALE, ONE_SCALE).setDuration(DURATION);
        backYAnimation.setInterpolator(new LinearInterpolator());
        beginAnimatorSet = new AnimatorSet();
        beginAnimatorSet.play(beginXAnimation).with(beginYAnimation);
        backAnimatorSet = new AnimatorSet();
        backAnimatorSet.play(backXAnimation).with(backYAnimation);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                /**
                 * 判斷是不是快速點擊
                 */
                if (isFastClick()) {
                    return true;
                }
                /**
                 * 請求父類不要攔截我的事件,意思是讓我來處理接下來的滑動或別的事件
                 */
                getParent().requestDisallowInterceptTouchEvent(true);
                downX = (int) event.getX();
                downY = (int) event.getY();
                hasDoneAnimation = false;
                post(new Runnable() {
                    @Override
                    public void run() {
                        beginAnimatorSet.start();
                    }
                });
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();
                int moveY = (int) event.getY();
                int moveDistanceX = Math.abs(moveX) - downX;
                int moveDistanceY = Math.abs(moveY) - downY;
                /**
                 * 這裏是判斷是向左還是向右滑動,然後用view的寬度計算出一個距離compareWidth,當滑動距離超出compareWidth時,需要執行返回動畫.
                 */
                int compareWidth = moveDistanceX > 0 ? getWidth() - downX : downX;
                /**
                 * 第一個條件:判斷向上或向下滑動距離大於滑動最小距離
                 * 第二個條件:判斷向左或向右的滑動距離是否超出(compareWidth-最小距離)
                 * 第三個條件:判斷有沒有執行過返回動畫並在執行過一次後置爲true.
                 */
                if ((Math.abs(moveDistanceY) > dip2px(MIN_MOVE_DPI) || Math.abs(moveDistanceX) >= compareWidth - dip2px(MIN_MOVE_DPI)) && !hasDoneAnimation) {
                    /**
                     * 一 只要滿足上述條件,就代表用戶不是點擊view,而是執行了滑動操作,這個時候我們就需要父類以及我們的最上層的控件來
                     * 攔截我們的事件,讓最外層控件處理接下來的事件,比如scrollview的滑動.
                     * 二 因爲我們執行了滑動操作,所以要執行view的返回動畫
                     */
                    getParent().requestDisallowInterceptTouchEvent(false);
                    hasDoneAnimation = true;
                    post(new Runnable() {
                        @Override
                        public void run() {
                            backAnimatorSet.start();
                        }
                    });
                }
                break;
            case MotionEvent.ACTION_UP:
                /**
                 * 這裏如果我們是單純的點擊事件就會執行
                 */
                if (!hasDoneAnimation) {
                    hasDoneAnimation = true;
                    post(new Runnable() {
                        @Override
                        public void run() {
                            backAnimatorSet.start();
                        }
                    });
                    post(new Runnable() {
                        @Override
                        public void run() {
                            /**
                             * 接口回調點擊事件
                             */
                            if (listener != null) {
                                listener.onClick(CTripHomeScaleView.this);
                            }
                        }
                    });
                }
                break;
        }
        return true;
    }

    public void setOnClickListener(OnClickListener l) {
        listener = l;
    }

    public interface OnClickListener {
        void onClick(View v);
    }

    /**
     * Make dp to px
     *
     * @param dipValue dp you need to change
     * @return Get px according to your dp value.
     */
    public int dip2px(float dipValue) {
        final float scale = getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    /**
     * Judge is fast click event.
     *
     * @return Is fast click or not.
     */
    public boolean isFastClick() {
        long time = System.currentTimeMillis();
        long timeD = time - lastClickTime;
        if (timeD < TIME_DELAY) {
            return true;
        } else {
            lastClickTime = time;
            return false;
        }
    }
}
總結:代碼量不多,主要的邏輯是在處理view的onTouch事件時,註釋在代碼裏寫的還算清晰,所以這裏就不在做贅述了,不懂的童鞋可以在下面留言,或者可以發郵件:[email protected]
希望我的文章可以幫到大家,最後附上github源碼地址:https://github.com/kuangxiaoguo0123/ctripHomeScaleView
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章