1. 程式人生 > >你知道android中的視差特效嗎

你知道android中的視差特效嗎

阻尼效果(視差特效)

空間,微博很多地方都有這種下拉出現的”阻尼“效果,這種效果最早在ios上出現,如今android上這種功能也是很常見了。

先看效果圖:
image

該功能可以分為兩個點:

  1. 當ListView下拉的時候,頂部的HeaderView會有一個拉長的效果;
  2. 當下拉一段距離後,ListView會復位,執行一個簡單的回彈動畫。

這個功能實現起來挺簡單的,下面來介紹如何實現:

第一個功能:(阻尼效果)

來看下關鍵程式碼:

	@Override
	protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
			int scrollY, int scrollRangeX, int scrollRangeY,
			int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
		Log.d("TAG", "deltaY: " +deltaY + " scrollY: " + scrollY + " scrollRangeY: " + scrollRangeY
				+ " maxOverScrollY: " + maxOverScrollY + " isTouchEvent: " + isTouchEvent);
		
		// 手指拉動 並且 是下拉
		if(isTouchEvent && deltaY < 0) {
			// 把拉動的瞬時變化量的絕對值交給Header, 就可以實現放大效果
			if(mImage.getHeight() <= drawableHeight ) {
			    //①將deltaY除以3使產生阻尼效果
				int newHeight = mImage.getHeight() + Math.abs(deltaY/3.0f);
				
				mImage.getLayoutParams().height = newHeight;
				使View重繪
				mImage.requestLayout();
			}
			
		}
		
		return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
				scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
	}

看到上面的程式碼,該段邏輯主要針對第一個功能,要重寫overScrollBy方法,通過監聽下拉的移動的高度賦給ListView的HeaderView,使View重繪來實現這種效果。

注意上面的①處,將下拉的幅度按比例的縮小來賦值給圖片的高度時,就會產生很難下拉的效果,即“阻尼”效果。

第二個功能:(回彈動畫)

當手指鬆開時,需要執行回彈動畫。手指鬆開,即ACTION_UP,重寫onTouchEvent來實現相關邏輯。

看如下程式碼:

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		
		switch (ev.getAction()) {
			case MotionEvent.ACTION_UP:
				// 執行回彈動畫, 方式一: 屬性動畫\值動畫
				// 從當前高度mImage.getHeight(), 執行動畫到原始高度mOriginalHeight
				final int startHeight = mImage.getHeight();
				final int endHeight = mOriginalHeight;
				
//				valueAnimator(startHeight, endHeight);

				// 執行回彈動畫, 方式二: 自定義Animation
				ResetAnimation animation = new ResetAnimation(mImage, startHeight, endHeight);
				startAnimation(animation);
				
				break;
		}
		return super.onTouchEvent(ev);
	}

	private void valueAnimator(final int startHeight, final int endHeight) {
		ValueAnimator mValueAnim = ValueAnimator.ofInt(1);
		mValueAnim.addUpdateListener(new AnimatorUpdateListener() {
			
			@Override
			public void onAnimationUpdate(ValueAnimator mAnim) {
			    //①獲取動畫的百分比
				float fraction = mAnim.getAnimatedFraction();
				// percent 0.0 -> 1.0
				Log.d(TAG, "fraction: " +fraction);
				Integer newHeight = evaluate(fraction, startHeight, endHeight);

				mImage.getLayoutParams().height = newHeight;
				mImage.requestLayout();
			}
		});
		//設定動畫的插值器,OvershootInterpolator向前甩一定值後再回到原來位置
		mValueAnim.setInterpolator(new OvershootInterpolator());
		mValueAnim.setDuration(500);
		mValueAnim.start();
	}
	
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }

看到上面的邏輯,在onTouchEvent監聽到ACTION_UP後,需要執行回彈動畫,這部分既可以用屬性動畫來實現,也可以自定義動畫的方式來實現。

這兩種方式都是需要將ListView重置為初始狀態,不過是以動畫的方式來實現。
注意上面①處的程式碼,float fraction = mAnim.getAnimatedFraction();getAnimatedFraction的API解釋如下:

Returns the current animation fraction, which is the elapsed/interpolated 
fraction used in the most recent frame update on the animation.

返回當前動畫的百分比,這個百分比能夠在動畫執行中用於更新當前的片段。

自定義動畫的方式

上面除了採用屬性動畫外,還可以使用自定義動畫的方式。

看下自定義動畫的邏輯:

package com.wind.parallax.ui;

import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
import android.view.animation.Transformation;
import android.widget.ImageView;

public class ResetAnimation extends Animation {

	private final ImageView mImage;
	private final int startHeight;
	private final int endHeight;

	public ResetAnimation(ImageView mImage, int startHeight, int endHeight) {
		this.mImage = mImage;
		this.startHeight = startHeight;
		this.endHeight = endHeight;
		
		setInterpolator(new OvershootInterpolator());
		//設定動畫執行時長
		setDuration(500);
	}

	@Override
	protected void applyTransformation(float interpolatedTime, Transformation t) {
		// interpolatedTime 0.0f -> 1.0f
		

		Integer newHeight = evaluate(interpolatedTime, startHeight, endHeight);

		mImage.getLayoutParams().height = newHeight;
		mImage.requestLayout();
		
		super.applyTransformation(interpolatedTime, t);
	}
	
	
    /**
     * 型別估值器
     * @param fraction
     * @param startValue
     * @param endValue
     * @return
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
	
}

該自定義動畫就是將最上面的屬性動畫改寫下,這部分的邏輯挺簡單的,就不再重複解釋了。

補充

最後看下MainActivity和佈局中的邏輯。

MainActivity.java

package com.wind.parallax;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;

import com.wind.parallax.ui.MyListView;
import com.wind.parallax.utils.Cheeses;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		final MyListView mListView = (MyListView) findViewById(R.id.lv);
		
		final View mHeaderView = View.inflate(MainActivity.this, R.layout.view_header, null);
		final ImageView mImage = (ImageView) mHeaderView.findViewById(R.id.iv);
		
		mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			
			@SuppressLint("NewApi")
			@Override
			public void onGlobalLayout() {
				// 當佈局填充結束之後, 此方法會被呼叫
				mListView.setParallaxImage(mImage);
				
				mHeaderView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
			}
		});
		mListView.addHeaderView(mHeaderView);
		
		
		mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1,Cheeses.NAMES));
	}
}

view_header.xml

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:scaleType="centerCrop"
        android:src="@drawable/parallax" />

</LinearLayout>

Activity中的邏輯比較簡單,初始化下資料。

另外,注意在view_header.xml中設定圖片採用的拉伸方式是centerCrop,scaleType有很多屬性,這裡必須採用這種方式處理,採用其他的會使圖片拉伸的很不美觀,關於scaleType的多種屬性這裡就不詳細說明了,感興趣的童鞋可以自己研究下,後面如果有時間也會有這方面的筆記貼出來,O(∩_∩)O。

歡迎關注我的公眾號 ,不定期會有優質技術文章推送 。

微信掃一掃下方二維碼即可關注
在這裡插入圖片描述