admin
6 天以前 7f0825f8195a522ed7e8bcdb6347f3a719e06c74
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package com.weikou.beibeivideo.util;
 
import java.io.InputStream;
import java.lang.reflect.Field;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
 
import com.weikou.beibeivideo.R;
 
public class PowerImageView extends androidx.appcompat.widget.AppCompatImageView implements OnClickListener {
    /**
     * 播放GIF动画的关键类
     */
    private Movie mMovie;
 
    /**
     * 开始播放按钮图片
     */
    private Bitmap mStartButton;
 
    /**
     * 记录动画开始的时间
     */
    private long mMovieStart;
 
    /**
     * GIF图片的宽度
     */
    private int mImageWidth;
 
    /**
     * GIF图片的高度
     */
    private int mImageHeight;
 
    /**
     * 图片是否正在播放
     */
    private boolean isPlaying;
 
    /**
     * 是否允许自动播放
     */
    private boolean isAutoPlay;
 
    /**
     * PowerImageView构造函数。
     * 
     * @param context
     */
    public PowerImageView(Context context) {
        super(context);
    }
 
    /**
     * PowerImageView构造函数。
     * 
     * @param context
     */
    public PowerImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    /**
     * PowerImageView构造函数,在这里完成所有必要的初始化操作。
     * 
     * @param context
     */
    public PowerImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.PowerImageView);
        int resourceId = getResourceId(a, context, attrs);
        if (resourceId != 0) {
            // 当资源id不等于0时,就去获取该资源的流
            InputStream is = getResources().openRawResource(resourceId);
            // 使用Movie类对流进行解码
            mMovie = Movie.decodeStream(is);
            if (mMovie != null) {
                // 如果返回值不等于null,就说明这是一个GIF图片,下面获取是否自动播放的属性
                isAutoPlay = a.getBoolean(R.styleable.PowerImageView_auto_play,
                        false);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                mImageWidth = bitmap.getWidth();
                mImageHeight = bitmap.getHeight();
                bitmap.recycle();
                if (!isAutoPlay) {
                    // 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
                    mStartButton = BitmapFactory.decodeResource(getResources(),
                            R.drawable.ic_full_media_play);
                    setOnClickListener(this);
                }
            }
        }
    }
 
    @Override
    public void onClick(View v) {
        if (v.getId() == getId()) {
            // 当用户点击图片时,开始播放GIF动画
            isPlaying = true;
            invalidate();
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        if (mMovie == null) {
            // mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法
            super.onDraw(canvas);
        } else {
            // mMovie不等于null,说明是张GIF图片
            if (isAutoPlay) {
                // 如果允许自动播放,就调用playMovie()方法播放GIF动画
                playMovie(canvas);
                invalidate();
            } else {
                // 不允许自动播放时,判断当前图片是否正在播放
                if (isPlaying) {
                    // 正在播放就继续调用playMovie()方法,一直到动画播放结束为止
                    if (playMovie(canvas)) {
                        isPlaying = false;
                    }
                    invalidate();
                } else {
                    // 还没开始播放就只绘制GIF图片的第一帧,并绘制一个开始按钮
                    mMovie.setTime(0);
                    mMovie.draw(canvas, 0, 0);
                    int offsetW = (mImageWidth - mStartButton.getWidth()) / 2;
                    int offsetH = (mImageHeight - mStartButton.getHeight()) / 2;
                    canvas.drawBitmap(mStartButton, offsetW, offsetH, null);
                }
            }
        }
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mMovie != null) {
            // 如果是GIF图片则重写设定PowerImageView的大小
            setMeasuredDimension(mImageWidth, mImageHeight);
        }
    }
 
    /**
     * 开始播放GIF动画,播放完成返回true,未完成返回false。
     * 
     * @param canvas
     * @return 播放完成返回true,未完成返回false。
     */
    private boolean playMovie(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mMovieStart == 0) {
            mMovieStart = now;
        }
        int duration = mMovie.duration();
        if (duration == 0) {
            duration = 1000;
        }
        int relTime = (int) ((now - mMovieStart) % duration);
        mMovie.setTime(relTime);
        mMovie.draw(canvas, 0, 0);
        if ((now - mMovieStart) >= duration) {
            mMovieStart = 0;
            return true;
        }
        return false;
    }
 
    /**
     * 通过Java反射,获取到src指定图片资源所对应的id。
     * 
     * @param a
     * @param context
     * @param attrs
     * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
     */
    private int getResourceId(TypedArray a, Context context, AttributeSet attrs) {
        try {
            Field field = TypedArray.class.getDeclaredField("mValue");
            field.setAccessible(true);
            TypedValue typedValueObject = (TypedValue) field.get(a);
            return typedValueObject.resourceId;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (a != null) {
                a.recycle();
            }
        }
        return 0;
    }
}