android movie实现gif (包含sdcard)加载
首先先说说使用movie加载gif的弊端:
1.需要关闭硬件加速,这就意味着对CPU的 消耗要明显变大,之前用的CPU全志T501(很低端,性能不行),CPU消耗可以占到25%,当然,对于现在 的主流 手机来说,小菜一碟。
2.只能加载gif,不能加载其他类型的图片;
优点:
1.相比较于glide(glide加载gif很鸡肋),gif图运行流畅;
下面直接上代码:
/**
* @author : wengliuhu
* @version : 0.1
* @since : 2020/7/20
* Describe
*/
public class GifView extends View
{
private final int DEFAULT_MOVIE_VIEW_DURATION = 1000;
final int buffersize = 16*1024;
private int movieMovieResourceId;
private String movieFilePath = "";
private Movie movie;
private long movieStart;
private int currentAnimationTime;
private float movieLeft;
private float movieTop;
private float movieScale;
private int movieMeasuredMovieWidth;
private int movieMeasuredMovieHeight;
private volatile boolean isPaused;
private Context context;
private boolean isVisible = true;
public GifView(Context context)
{
super(context);
this.context = context;
}
public GifView(Context context, @Nullable AttributeSet attrs)
{
super(context, attrs);
this.context = context;
// GifView(context, attrs, -1);
init(context, attrs, -1);
}
public GifView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
this.context = context;
init(context, attrs, defStyleAttr);
}
public int getMovieMovieResourceId()
{
return movieMovieResourceId;
}
public void setMovieMovieResourceId(int movieMovieResourceId)
{
this.movieMovieResourceId = movieMovieResourceId;
movie = Movie.decodeStream(context.getResources().openRawResource(movieMovieResourceId));
requestLayout();
}
public String getMovieFilePath()
{
return movieFilePath;
}
/**
*从SD卡中加载gif
*/
public void setMovieFilePath(String movieFilePath)
{
File file = new File(movieFilePath);
if (!file.exists()) return;
this.movieFilePath = movieFilePath;
InputStream inputStream = null;
try
{
inputStream = new BufferedInputStream(new FileInputStream(file), buffersize);
inputStream.mark(buffersize);
movie = Movie.decodeStream(inputStream);
requestLayout();
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
}
public boolean isPaused()
{
return isPaused;
}
public void setPaused(boolean paused)
{
isPaused = paused;
}
private void init(Context context, AttributeSet attributeSet, int defstyle){
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
TypedArray array = context.obtainStyledAttributes(attributeSet,
R.styleable.GifView, defstyle, R.style.Widget_GifView);
//-1 is default value
movieMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1);
isPaused = array.getBoolean(R.styleable.GifView_paused, false);
array.recycle();
if (movieMovieResourceId != -1) {
movie = Movie.decodeStream(context.getResources().openRawResource(movieMovieResourceId));
}
}
public void play() {
if (this.isPaused) {
this.isPaused = false;
/**
* Calculate new movie start time, so that it resumes from the same
* frame.
*/
movieStart = android.os.SystemClock.uptimeMillis() - currentAnimationTime;
invalidate();
}
}
public void pause() {
if (!this.isPaused) {
this.isPaused = true;
invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (movie != null) {
int movieWidth = movie.width();
int movieHeight = movie.height();
/*
* Calculate horizontal scaling
*/
float scaleH = 1f;
int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (measureModeWidth != MeasureSpec.UNSPECIFIED) {
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
if (movieWidth > maximumWidth) {
scaleH = 1.0f * movieWidth / maximumWidth;
}
}
/*
* calculate vertical scaling
*/
float scaleW = 1f;
int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (measureModeHeight != MeasureSpec.UNSPECIFIED) {
int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);
if (movieHeight > maximumHeight) {
scaleW = 1.0f * movieHeight / maximumHeight;
}
}
/*
* calculate overall scale
*/
movieScale = 1f / Math.max(scaleH, scaleW);
movieMeasuredMovieWidth = (int) (movieWidth * movieScale);
movieMeasuredMovieHeight = (int) (movieHeight * movieScale);
setMeasuredDimension(movieMeasuredMovieWidth, movieMeasuredMovieHeight);
} else {
/*
* No movie set, just set minimum available size.
*/
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
super.onLayout(changed, left, top, right, bottom);
/*
* Calculate movieLeft / movieTop for drawing in center
*/
movieLeft = (getWidth() - movieMeasuredMovieWidth) / 2f;
movieTop = (getHeight() - movieMeasuredMovieHeight) / 2f;
isVisible = getVisibility() == View.VISIBLE;
}
@Override
protected void onDraw(Canvas canvas)
{
if (movie != null) {
if (!isPaused) {
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
} else {
drawMovieFrame(canvas);
}
}
}
/**
* Invalidates view only if it is isVisible.
* <br></br>
* [.postInvalidateOnAnimation] is used for Jelly Bean and higher.
*/
private void invalidateView() {
if (isVisible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
invalidate();
}
}
}
/**
* Calculate current animation time
*/
private void updateAnimationTime() {
long now = android.os.SystemClock.uptimeMillis();
if (movieStart == 0L) {
movieStart = now;
}
int duration = movie == null ? 0 :movie.duration();
if (duration == 0) {
duration = DEFAULT_MOVIE_VIEW_DURATION;
}
currentAnimationTime = (int) ((now - movieStart) % duration);
}
/**
* Draw current GIF frame
*/
private void drawMovieFrame(Canvas canvas) {
if (movie != null){
movie.setTime(currentAnimationTime);
}
canvas.save();
canvas.scale(movieScale, movieScale);
if (movie != null){
movie.draw(canvas, movieLeft / movieScale, movieTop / movieScale);
}
canvas.restore();
}
@Override
public void onScreenStateChanged(int screenState)
{
super.onScreenStateChanged(screenState);
isVisible = screenState == View.SCREEN_STATE_ON;
invalidateView();
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility)
{
super.onVisibilityChanged(changedView, visibility);
isVisible = visibility == View.VISIBLE;
invalidateView();
}
@Override
protected void onWindowVisibilityChanged(int visibility)
{
super.onWindowVisibilityChanged(visibility);
isVisible = visibility == View.VISIBLE;
invalidateView();
}
}
接下来styles.xml:
<style name="Widget.GifView" parent="@android:style/Widget"/> <
下面是attrs.xml:
<declare-styleable name="GifView">
<attr name="gif" format="reference" />
<attr name="paused" format="boolean" />
</declare-styleable>
使用中遇到的问题:
1.如何从sdcard中加载gif,这一部分资料比较好,movie本身的decodeFile()方法执行会报错,需要转换另一种方式,通过IO流来做
public void setMovieFilePath(String movieFilePath)
{
File file = new File(movieFilePath);
if (!file.exists()) return;
this.movieFilePath = movieFilePath;
InputStream inputStream = null;
try
{
inputStream = new BufferedInputStream(new FileInputStream(file), buffersize);
inputStream.mark(buffersize);
movie = Movie.decodeStream(inputStream);
requestLayout();
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
}