[原創]實現android知乎、一覽等的開場動畫圖片放大效果

代碼下載地址:android

https://github.com/Carbs0126/AutoZoomInImageViewgit

知乎等app的開場動畫爲:一張圖片被顯示到屏幕的正中央,並充滿整個屏幕,過一小段時間後,開始慢慢方法,且圖片的正中央始終處於屏幕的正中央,也就是「鏡頭緩慢放大」的效果github

難點1.android手機屏幕碎片化。因爲是全屏顯示,所以一張圖片須要放到不一樣大小的ImageView中,且圖片中央須要放到ImageView中央;數組

難點2.放大時須要保證圖片中央與ImageView中央處於一點;app

難點3.實現緩慢放大效果(這個能夠利用ValueAnimator實現)less

實現原理是:採用調整ImageView的matrix的方式來實現此效果。ide

我將其實現過程拆分爲兩部:
1.調整圖片,使圖片位於屏幕的正中間。因爲android手機屏幕尺寸多種多樣,而圖片的大小也不甚相同,爲了靈活的使用此效果,須要將任意尺寸比例的圖片顯示在任意尺寸比例的手機屏幕的正中間,同時不使圖片扭曲變形。
2更改float[]的值,而後更新Matrix並應用到ImageView中,從而達到圖片zoomin的效果

本篇文章抽象出一個新的view,以便於使用及修改,同時完善了上篇文章因爲行文倉促而留下的多個未實現的需求。佈局

實現效果以下:post

 

快速應用到工程:動畫

首先,添加依賴:

compile 'cn.carbs.android:AutoZoomInImageView:1.0.0'

其次,xml佈局文件中聲明

    <cn.carbs.android.library.AutoZoomInImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/horse" />

最後,在Activity中使用代碼:(注意,要在view徹底顯示出來以後使用,所以這裏我用了post(runnable)的方式)

iv.post(new Runnable() {//iv即AutoZoomInImageView

            @Override
            public void run() {
                //簡單方式啓動放大動畫
//                iv.init()
//                  .startZoomInByScaleDeltaAndDuration(0.3f, 1000, 1000);//放大增量是0.3,放大時間是1000毫秒,放大開始時間是1000毫秒之後
                //使用較爲具體的方式啓動放大動畫
                iv.init()
                        .setScaleDelta(0.2f)//放大的係數是原來的(1 + 0.2)倍
                        .setDurationMillis(1500)//動畫的執行時間爲1500毫秒
                        .setOnZoomListener(new AutoZoomInImageView.OnZoomListener(){
                            @Override
                            public void onStart(View view) {
                                //放大動畫開始時的回調
                            }
                            @Override
                            public void onUpdate(View view, float progress) {
                                //放大動畫進行過程當中的回調 progress取值範圍是[0,1]
                            }
                            @Override
                            public void onEnd(View view) {
                                //放大動畫結束時的回調
                            }
                        })
                        .start(1000);//延遲1000毫秒啓動
            }
        });

 

主要的代碼以下:

1.AutoZoomInImageView的代碼爲:

  1 package cn.carbs.android.library;
  2 
  3 import android.animation.Animator;
  4 import android.animation.ValueAnimator;
  5 import android.annotation.SuppressLint;
  6 import android.content.Context;
  7 import android.graphics.Matrix;
  8 import android.graphics.drawable.Drawable;
  9 import android.util.AttributeSet;
 10 import android.view.View;
 11 import android.widget.ImageView;
 12 
 13 @SuppressLint("NewApi")
 14 public class AutoZoomInImageView extends ImageView{
 15     
 16     private Drawable mDrawable;
 17     private int mDrawableW;
 18     private int mDrawableH;
 19     
 20     private int mImageViewW;
 21     private int mImageViewH;
 22     
 23     private Matrix mMatrix;
 24     private float[] mValues = new float[9];
 25 
 26     private float mScaleDelta = 0.2f;
 27     private long mDurationMillis = 700;
 28 
 29     public AutoZoomInImageView(Context context) {
 30         super(context);
 31         this.setScaleType(ScaleType.MATRIX);
 32     }
 33     
 34     public AutoZoomInImageView(Context context, AttributeSet attrs) {
 35         super(context, attrs);
 36         this.setScaleType(ScaleType.MATRIX);
 37     }
 38     
 39     public AutoZoomInImageView(Context context, AttributeSet attrs, int defStyle) {
 40         super(context, attrs, defStyle);
 41         this.setScaleType(ScaleType.MATRIX);
 42     }
 43     
 44     public AutoZoomInImageView init(){
 45         initInternalValues();
 46         initPicturePosition();
 47         return this;
 48     }
 49     
 50     public void init(Drawable drawable){
 51         initInternalValues(drawable);
 52         initPicturePosition();
 53     }
 54 
 55     private void initInternalValues(){
 56         mDrawable = getDrawable();
 57         
 58         if(mDrawable == null){
 59             throw new IllegalArgumentException("please set the source of AutoZoomInImageView");
 60         }
 61         
 62         mDrawableW = mDrawable.getIntrinsicWidth();
 63         mDrawableH = mDrawable.getIntrinsicHeight();
 64         
 65         mImageViewW = getMeasuredWidth();
 66         mImageViewH = getMeasuredHeight();
 67     
 68         mMatrix = getImageMatrix();
 69         mMatrix.getValues(mValues);
 70     }
 71     
 72     private void initInternalValues(Drawable drawable){
 73         mDrawable = drawable;
 74         
 75         if(mDrawable == null){
 76             throw new IllegalArgumentException("please set the source of AutoZoomInImageView");
 77         }
 78         
 79         mDrawableW = mDrawable.getIntrinsicWidth();
 80         mDrawableH = mDrawable.getIntrinsicHeight();
 81         
 82         mImageViewW = getMeasuredWidth();
 83         mImageViewH = getMeasuredHeight();
 84     
 85         mMatrix = getImageMatrix();
 86         mMatrix.getValues(mValues);
 87     }
 88     
 89     private void initPicturePosition(){
 90         updateMatrixValuesOrigin(mMatrix, mValues, mDrawableW, mDrawableH, mImageViewW, mImageViewH);
 91         setImageMatrix(mMatrix);
 92     }
 93     
 94     private void startZoomInByScaleDelta(final float scaleDelta, long duration){
 95         
 96         final float oriScaleX = mValues[0];
 97         final float oriScaleY = mValues[4];
 98         
 99         ValueAnimator va = ValueAnimator.ofFloat(0, scaleDelta);
100         va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
101             
102             @Override
103             public void onAnimationUpdate(ValueAnimator animation) {
104                 float value = (Float)animation.getAnimatedValue();
105                 if(mOnZoomListener != null) mOnZoomListener.onUpdate(AutoZoomInImageView.this, value / scaleDelta);
106                 updateMatrixValuesSpan(mValues, mDrawableW, mDrawableH, mImageViewW, mImageViewH,
107                         oriScaleX, oriScaleY, value);
108                 mMatrix.setValues(mValues);
109                 setImageMatrix(mMatrix);
110             }
111         });
112         va.addListener(new Animator.AnimatorListener() {
113             @Override
114             public void onAnimationStart(Animator animation) {
115                 if(mOnZoomListener != null) mOnZoomListener.onStart(AutoZoomInImageView.this);
116             }
117 
118             @Override
119             public void onAnimationEnd(Animator animation) {
120                 if(mOnZoomListener != null) mOnZoomListener.onEnd(AutoZoomInImageView.this);
121             }
122             @Override
123             public void onAnimationCancel(Animator animation) {}
124             @Override
125             public void onAnimationRepeat(Animator animation) {}
126         });
127         va.setDuration(duration);
128         va.start();
129     }
130 
131     /**
132      * 開始放大動畫
133      * start zooming in
134      * @param scaleDelta        放大的增大倍數,若是是0.2,那麼最後大小放大至1.2倍。
135      *                          the scale that the image will add to original scale
136      * @param durationMillis    放大效果的持續時間,單位毫秒。
137      *                          the duration of zoomin animation, in millisecond.
138      * @param delayMillis       開始放大效果的延遲時間,單位毫秒。delayed毫秒後開始放大動畫效果。
139      *                          the delayed time of starting zoomin animation, in millisecond.
140      */
141     public void startZoomInByScaleDeltaAndDuration(final float scaleDelta, final long durationMillis, long delayMillis){
142         if(scaleDelta < 0){
143             throw new IllegalArgumentException("scaleDelta should be larger than 0, now scaleDelta is " + scaleDelta);
144         }
145         if(durationMillis < 0){
146             throw new IllegalArgumentException("durationMillis should not be less than 0, now durationMillis is " + durationMillis);
147         }
148         if(delayMillis < 0){
149             throw new IllegalArgumentException("delayMillis should not be less than 0, now delayMillis is " + delayMillis);
150         }
151 
152         postDelayed(new Runnable() {
153             @Override
154             public void run() {
155                 startZoomInByScaleDelta(scaleDelta, durationMillis);
156             }
157         }, delayMillis);
158     }
159 
160     /**
161      * 放大的增大倍數,若是是0.2,那麼最後大小放大至1.2倍。
162      * the scale that the image will add to original scale
163      * @param scaleDelta
164      * @return
165      */
166     public AutoZoomInImageView setScaleDelta(float scaleDelta){
167         mScaleDelta = scaleDelta;
168         return this;
169     }
170 
171     /**
172      * 放大效果的持續時間,單位毫秒。
173      * the duration of zoomin animation, in millisecond.
174      * @param durationMillis
175      * @return
176      */
177     public AutoZoomInImageView setDurationMillis(long durationMillis){
178         mDurationMillis = durationMillis;
179         return this;
180     }
181 
182     /**
183      * 動畫結束的回調
184      * callback when zoomin animation finished
185      * @param onZoomListener
186      * @return
187      */
188     public AutoZoomInImageView setOnZoomListener(OnZoomListener onZoomListener){
189         mOnZoomListener = onZoomListener;
190         return this;
191     }
192 
193     /**
194      * 開始放大效果
195      * start animation of zoomin
196      * @param delayMillis       開始放大效果的延遲時間,單位毫秒。delayed毫秒後開始放大動畫效果
197      *                          the delayed time of starting zoomin animation, in millisecond.
198      */
199     public void start(long delayMillis){
200         postDelayed(new Runnable() {
201             @Override
202             public void run() {
203                 startZoomInByScaleDelta(mScaleDelta, mDurationMillis);
204             }
205         }, delayMillis);
206     }
207 
208     private void updateMatrixValuesOrigin(Matrix outMatrix, float[] outValues, float drawW, float drawH, float imageW, float imageH){
209         
210         if(outMatrix == null || outValues == null){
211             throw new IllegalArgumentException("please set the source of AutoZoomInImageView's matrix and values");
212         }
213         
214         outMatrix.reset();
215         
216         if((imageH * drawW > drawH * imageW)){
217             float scale1 = (imageH)/(drawH);
218             float offset1 = (drawW * scale1 - imageW)/2;
219             
220             outMatrix.postScale(scale1, scale1);
221             outMatrix.postTranslate(-offset1, 0);
222                     
223         }else{
224             float scale2 = (imageW)/(drawW);
225             float offset2 = (drawH * scale2 - imageH)/2;
226 
227             outMatrix.postScale(scale2, scale2);
228             outMatrix.postTranslate(0, -offset2);
229         }
230         outMatrix.getValues(outValues);
231     }
232     
233     private void updateMatrixValuesSpan(float[] outValues,
234                                         float drawW, float drawH,
235                                         float imageW, float imageH,
236                                         float oriScaleX, float oriScaleY,
237                                         float scaleDelta){
238         //根據四個參數:圖片的寬高、控件的寬高,動態的計算出輸出的矩陣(float數組)的值
239         outValues[0] = oriScaleX * (1 + scaleDelta);
240         outValues[4] = oriScaleY * (1 + scaleDelta);
241         float offsetwidth = (drawW * outValues[0] - imageW)/2;
242         outValues[2] = - offsetwidth;
243         float offsetHeight = (drawH * outValues[0] - imageH)/2;
244         outValues[5] = - offsetHeight;
245     }
246 
247     private OnZoomListener mOnZoomListener;
248     public interface OnZoomListener{
249         /**
250          * 動畫更新時執行的回調
251          * @param view      返回此AutoZoomInImageView
252          * @param progress  返回動畫進行過程,範圍是[0,1]
253          */
254         void onUpdate(View view, float progress);
255         void onEnd(View view);
256         void onStart(View view);
257     }
258 
259 
260     //function for log
261     public String printMyMatrix(Matrix m){
262         float[] valueFloat = new float[9];
263         m.getValues(valueFloat);
264         
265         String s = "";
266         for(int i = 0; i < 9; i++){
267             s = s + " [ " + valueFloat[i] + " ] ";
268         }
269         return s;
270     }
271         
272     //function for log
273     public String printMyValue(float[] valueFloat){
274         String s = "";
275         for(int i = 0; i < 9; i++){
276             s = s + " [ " + valueFloat[i] + " ] ";
277         }
278         return s;
279     }
280     
281 }

2.代碼中調用AutoZoomInImageView啓用動畫的方法:

 1 iv.post(new Runnable() {//iv即AutoZoomInImageView
 2 
 3             @Override
 4             public void run() {
 5                 //簡單方式啓動放大動畫
 6 //                iv.init()
 7 //                  .startZoomInByScaleDeltaAndDuration(0.3f, 1000, 1000);//放大增量是0.3,放大時間是1000毫秒,放大開始時間是1000毫秒之後
 8                 //使用較爲具體的方式啓動放大動畫
 9                 iv.init()
10                         .setScaleDelta(0.2f)//放大的係數是原來的(1 + 0.3)倍
11                         .setDurationMillis(1500)//動畫的執行時間爲1000毫秒
12                         .setOnZoomListener(new AutoZoomInImageView.OnZoomListener(){
13                             @Override
14                             public void onStart(View view) {
15                                 //放大動畫開始時的回調
16                             }
17                             @Override
18                             public void onUpdate(View view, float progress) {
19                                 //放大動畫進行過程當中的回調 progress取值範圍是[0,1]
20                             }
21                             @Override
22                             public void onEnd(View view) {
23                                 //放大動畫結束時的回調
24                             }
25                         })
26                         .start(1000);//延遲1000毫秒啓動
27             }
28         });

3.xml文件中聲明此view:

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

    <cn.carbs.android.library.AutoZoomInImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/horse" />

</LinearLayout>

 

 

 

代碼下載地址:

https://github.com/Carbs0126/AutoZoomInImageView

相關文章
相關標籤/搜索