用Movie显示gif(2)GifView
1,类
1 import android.annotation.SuppressLint; 2 import android.content.Context; 3 import android.content.res.TypedArray; 4 import android.graphics.Canvas; 5 import android.graphics.Movie; 6 import android.os.Build; 7 import android.util.AttributeSet; 8 import android.view.View; 9 10 import com.e.weixin.R; 11 12 public class GifView extends View { 13 14 /** 15 * 默认为1秒 16 */ 17 private static final int DEFAULT_MOVIE_DURATION = 1000; 18 19 private int mMovieResourceId; //要显示的gif的id 20 21 private Movie mMovie; //用于播放gif等 22 23 private long mMovieStart; // 24 25 private int mCurrentAnimationTime = 0; 26 27 private float mLeft; 28 29 private float mTop; 30 31 private float mScale; 32 33 private int mMeasuredMovieWidth; 34 35 private int mMeasuredMovieHeight; 36 37 private boolean mVisible = true; 38 39 private volatile boolean mPaused = false; 40 41 public GifView(Context context) { 42 this(context, null); 43 } 44 45 public GifView(Context context, AttributeSet attrs) { 46 this(context, attrs, R.styleable.CustomTheme_gifViewStyle); 47 // super(context, attrs); 48 } 49 50 public GifView(Context context, AttributeSet attrs, int defStyle) { 51 super(context, attrs, defStyle); 52 setViewAttributes(context, attrs, defStyle); 53 } 54 55 @SuppressLint("NewApi") 56 private void setViewAttributes(Context context, AttributeSet attrs,int defStyle) { 57 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 58 setLayerType(View.LAYER_TYPE_SOFTWARE, null); 59 } 60 // 从描述文件中读出gif的值,创建出Movie实例 61 final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GifView, defStyle, 62 R.style.Theme_GifView); 63 mMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1); 64 mPaused = array.getBoolean(R.styleable.GifView_paused, false); 65 array.recycle(); 66 if (mMovieResourceId != -1) { 67 mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId)); 68 } 69 } 70 71 /** 72 * 设置gif图资源 73 * 74 * @param movieResId 75 */ 76 public void setMovieResource(int movieResId) { 77 this.mMovieResourceId = movieResId; 78 mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId)); 79 requestLayout(); 80 } 81 82 public void setMovie(Movie movie) { 83 this.mMovie = movie; 84 requestLayout(); 85 } 86 87 public Movie getMovie() { 88 return mMovie; 89 } 90 91 public void setMovieTime(int time) { 92 mCurrentAnimationTime = time; 93 invalidate(); 94 } 95 96 /** 97 * 设置暂停 98 * 99 * @param paused 100 */ 101 public void setPaused(boolean paused) { 102 this.mPaused = paused; 103 if (!paused) { 104 mMovieStart = android.os.SystemClock.uptimeMillis() - mCurrentAnimationTime; 105 } 106 invalidate(); 107 } 108 109 /** 110 * 判断gif图是否停止了 111 * 112 * @return 113 */ 114 public boolean isPaused() { 115 return this.mPaused; 116 } 117 118 @Override 119 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 120 if (mMovie != null) { 121 int movieWidth = mMovie.width(); 122 int movieHeight = mMovie.height(); 123 int maximumWidth = MeasureSpec.getSize(widthMeasureSpec); 124 float scaleW = (float) movieWidth / (float) maximumWidth; 125 mScale = 1f / scaleW; 126 mMeasuredMovieWidth = maximumWidth; 127 mMeasuredMovieHeight = (int) (movieHeight * mScale); 128 setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight); 129 } else { 130 setMeasuredDimension(getSuggestedMinimumWidth(),getSuggestedMinimumHeight()); 131 } 132 } 133 134 @Override 135 protected void onLayout(boolean changed, int l, int t, int r, int b) { 136 super.onLayout(changed, l, t, r, b); 137 mLeft = (getWidth() - mMeasuredMovieWidth) / 2f; 138 mTop = (getHeight() - mMeasuredMovieHeight) / 2f; 139 mVisible = getVisibility() == View.VISIBLE; 140 } 141 142 @Override 143 protected void onDraw(Canvas canvas) { 144 if (mMovie != null) { 145 if (!mPaused) { 146 updateAnimationTime(); 147 drawMovieFrame(canvas); 148 invalidateView(); 149 } else { 150 drawMovieFrame(canvas); 151 } 152 } 153 } 154 155 @SuppressLint("NewApi") 156 private void invalidateView() { 157 if (mVisible) { 158 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 159 postInvalidateOnAnimation(); 160 } else { 161 invalidate(); 162 } 163 } 164 } 165 166 private void updateAnimationTime() { 167 long now = android.os.SystemClock.uptimeMillis(); 168 // 如果第一帧,记录起始时间 169 if (mMovieStart == 0) { 170 mMovieStart = now; 171 } 172 // 取出动画的时长 173 int dur = mMovie.duration(); 174 if (dur == 0) { 175 dur = DEFAULT_MOVIE_DURATION; 176 } 177 // 算出需要显示第几帧 178 mCurrentAnimationTime = (int) ((now - mMovieStart) % dur); 179 } 180 181 private void drawMovieFrame(Canvas canvas) { 182 // 设置要显示的帧,绘制即可 183 mMovie.setTime(mCurrentAnimationTime); 184 canvas.save(Canvas.MATRIX_SAVE_FLAG); 185 canvas.scale(mScale, mScale); 186 mMovie.draw(canvas, mLeft / mScale, mTop / mScale); 187 canvas.restore(); 188 } 189 190 @SuppressLint("NewApi") 191 @Override 192 public void onScreenStateChanged(int screenState) { 193 super.onScreenStateChanged(screenState); 194 mVisible = screenState == SCREEN_STATE_ON; 195 invalidateView(); 196 } 197 198 @SuppressLint("NewApi") 199 @Override 200 protected void onVisibilityChanged(View changedView, int visibility) { 201 super.onVisibilityChanged(changedView, visibility); 202 mVisible = visibility == View.VISIBLE; 203 invalidateView(); 204 } 205 206 @Override 207 protected void onWindowVisibilityChanged(int visibility) { 208 super.onWindowVisibilityChanged(visibility); 209 mVisible = visibility == View.VISIBLE; 210 invalidateView(); 211 } 212 }
2,自定义view的属性
1 <resources> 2 3 <declare-styleable name="GifView"> 4 <attr name="gif" format="reference" /> 5 <attr name="paused" format="boolean" /> 6 </declare-styleable> 7 <declare-styleable name="CustomTheme"> 8 <attr name="gifViewStyle" format="reference" /> 9 </declare-styleable> 10 11 </resources>
3,使用到的主题
1 <style name="Theme.GifView" parent="@android:style/Theme.Dialog"> 2 <!--...--> 3 </style>
不用非继承 @android:style/Theme.Dialog 不可,其它主题也可以。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?