一 需求:實現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;
}
}
}