代码下载地址:
https://github.com/Carbs0126/AutoZoomInImageView
知乎等app的开场动画为:一张图片被显示到屏幕的正*,并充满整个屏幕,过一小段时间后,开始慢慢方法,且图片的正*始终处于屏幕的正*,也就是“镜头缓慢放大”的效果
难点1.android手机屏幕碎片化。由于是全屏显示,因此一张图片需要放到不同大小的ImageView中,且图片*需要放到ImageView*;
难点2.放大时需要保证图片*与ImageView*处于一点;
难点3.实现缓慢放大效果(这个可以利用ValueAnimator实现)
实现原理是:采用调整ImageView的matrix的方式来实现此效果。
我将其实现过程拆分为两部: 1.调整图片,使图片位于屏幕的正中间。由于android手机屏幕尺寸多种多样,而图片的大小也不甚相同,为了灵活的使用此效果,需要将任意尺寸比例的图片显示在任意尺寸比例的手机屏幕的正中间,同时不使图片扭曲变形。 2更改float[]的值,然后更新Matrix并应用到ImageView中,从而达到图片zoomin的效果本篇文章抽象出一个新的view,以便于使用及修改,同时完善了上篇文章由于行文仓促而留下的多个未实现的需求。
实现效果如下:
快速应用到工程:
首先,添加依赖:
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>
代码下载地址: