android中gif图实现的方案

  gif图的实现原理就是把gif的每一帧图片抽取出来,在view里循环播放,在效果上就达到了gif 图的要求。原理上简单,但是实现起来有需要考虑图片加载、图片切换间隔时间、以及图片回收的问题。

   我的第一个方案是使用surfaceview,但是surfaceview有一个很大的弊端,就是遮挡背景,比如我的gif有一部分是透明的,如果使用surfaceview,看起来背景就是黑色的,当然也可以在canvas里面设置背景色,但是如果后面是另外的图片或者不确定的话,就没法解决,所以,至少放弃使用surfaceview的方案

   所以只好使用重写view的ondraw方法,采用ArrayBlockingQueue来加载图片,启用一个专门的线程来加载图片,然后再ondraw中取图片并持续更新,就可以达到gif图的效果

下面是代码

package com.example.gifdemo;


import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.util.Log;
import android.view.View;
/**
 * gif图
 * @author zhaoq
 *
 */
public class MarsorGifImage extends View {

public MarsorGifImage(Context context) {
super(context);
}
//图片队列的默认size,大小可以根据设备的内存和cpu效率来确定,如果内存很少,可以考虑将这个设置的小一点,如果设备计算效率很快这个可以设置的大一点
private static int QUEUESIZE = 10;
//bitmap队列,会有一个单独的线程将新建的bitmap放入,而ui线程会从其中获取,这个队列是同步的,所以不需要我们考虑线程同步的问题
private ArrayBlockingQueue<Bitmap> bitmapQueue = new ArrayBlockingQueue<Bitmap>(QUEUESIZE);
//图片全路径的集合
private ArrayList<String> mResourceList;
//加载图片顺序编号
private int loadIndex = 0;
/**
* gif图的大小,请根据视图的大小进行设置
*/
public int mWidth = 100;
public int mHeight = 100;
/**
* 当前已经绘制的bitmap
*/
private Bitmap currentBitmap = null;
/**
* 图片的绘制区域
*/
private RectF rectf = null;
/**
* 设置图片的数据集合

* @param resourceList 所有图片全路径集合
*/
public void setResourceList(ArrayList<String> resourceList){
mResourceList = resourceList;
}
/**
* 提供load方法,这样就可以提前加载bitmap了,
* 如果加载bitmap的速度很慢,那么就需要提前调用这个方法,这样gif的播放会更加流畅
*/
public void load(){
//启动加载线程
new Thread(loadRunnable).start();
//绘制到画布上,但是一定要,设定大小
rectf = new RectF(0,0,mWidth,mHeight);
}


@Override
protected void onDraw(Canvas canvas) {
//回去当前的bitmap,不确定当前的bimap是否可用,如果不可用就去取一个
if(currentBitmap == null){
currentBitmap = bitmapQueue.poll();
}
//将图片画上去
canvas.drawBitmap(currentBitmap, null, rectf, null);
//如果已经停止播放,那么在这里既可以直接返回了
if(isRunning)return;
//开始准备下一次绘制
currentBitmap.recycle();
currentBitmap = null;
//生成下一帧需要的bitmap
currentBitmap = bitmapQueue.poll();
//启动绘制
postInvalidate();
//如果太快可以调用
//postInvalidateDelayed(100);
}
//当前视图的运行标志,如果这个为false,那就说明当前的视图已经被隐藏或者被销毁,这个时候就不需要加载新的bitmap了
private boolean isRunning = false;
/**
* 加载bitmap的线程
* 一会会加载到bitmapQueue充满为止
* 如果bitmapQueue中的bitmap被取出,则会被唤醒,接着加载
* 除非被外界中断
*/
private Runnable loadRunnable = new Runnable(){


@Override
public void run() {

// if(!mRunning)return;
while(isRunning){
//如果bitmap路径尚未被设置,那我们就先wait,直到被唤醒
if(mResourceList == null)
try {
wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//如果需要循环播放gif图,则需要在这个时候设置0,如果不需要循环播放,那么直接返回就可以了
if(loadIndex>=mResourceList.size())loadIndex=0;

String bitMapResource = mResourceList.get(loadIndex);
Bitmap bitmap = getBitmapFromSD(bitMapResource, mWidth, mHeight);
//如果加载失败,那就跳过这一帧
if(bitmap ==null)continue;
try {
bitmapQueue.put(bitmap);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
loadIndex++;
}

}
};

/**
* 创建bitmap
* @param fileName  文件全路径
* @param width  输出的宽度
* @param height  输出的高度
* @return
*/
public static Bitmap getBitmapFromSD(String fileName, int width, int height) {
Bitmap bitmap = null;
//1先取得bitmap的size
BitmapFactory.Options _option = new BitmapFactory.Options();
_option.inJustDecodeBounds = true;
   // 通过这个bitmap获取图片的宽和高&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
BitmapFactory.decodeFile(fileName, _option);
       

        float realWidth = _option.outWidth;
        float realHeight = _option.outHeight;
if(realWidth>width){
_option.inSampleSize = (int) (realWidth/width);
}
_option.inDither = false; // Disable Dithering mode
_option.inPurgeable = true; // Tell to gc that whether it needs free
// memory, the Bitmap can be cleared
_option.inInputShareable = true; // Which kind of reference will be used
// to recover the Bitmap data after
// being clear, when it will be used
// in the future
_option.inJustDecodeBounds = false;
_option.inTempStorage = new byte[16 * 1024];
_option.outHeight = height;
_option.outWidth = width;

try {
bitmap = BitmapFactory.decodeFile(fileName, _option);
if(bitmap==null){
_option.inSampleSize = 10;
bitmap = BitmapFactory.decodeFile(fileName, _option);
}
return bitmap;
} catch (Exception e) {
Log.e("mouee","load bitmap Exception");
e.printStackTrace();
} catch (OutOfMemoryError e) {
Log.e("mouee","load bitmap OutOfMemoryError");
e.printStackTrace();
_option.inSampleSize = 10;
bitmap = BitmapFactory.decodeFile(fileName, _option);
return bitmap;
} finally {
}
return null;


}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值