代碼下載地址:android
https://github.com/Carbs0126/AutoZoomInImageViewgit
知乎等app的開場動畫爲:一張圖片被顯示到屏幕的正中央,並充滿整個屏幕,過一小段時間後,開始慢慢方法,且圖片的正中央始終處於屏幕的正中央,也就是「鏡頭緩慢放大」的效果github
難點1.android手機屏幕碎片化。因爲是全屏顯示,所以一張圖片須要放到不一樣大小的ImageView中,且圖片中央須要放到ImageView中央;數組
難點2.放大時須要保證圖片中央與ImageView中央處於一點;app
難點3.實現緩慢放大效果(這個能夠利用ValueAnimator實現)less
實現原理是:採用調整ImageView的matrix的方式來實現此效果。ide
本篇文章抽象出一個新的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>
代碼下載地址: