DEMO地址:https://github.com/zhaopingfu/GifPlayerDemo
Gif 格式图片详细解析:http://blog.csdn.net/wzy198852/article/details/17266507
Android中Gif播放一般有三种方式
自定义View
这里用了一个开源库提供的自定义gifview
https://code.google.com/archive/p/gifview/downloads
NDK方式
这里以android-gif-drawable为例,但是不是用的这个库,是用的系统的代码
系统代码在\android-6.0.0_r1\external\giflib中
先将gif放在外置卡的根目录下
final File file = newFile(Environment.getExternalStorageDirectory(), “demo.gif”);
还需要自己写一些
#include <jni.h>
#include <string>
#include "gif_lib.h"
#include <android/log.h>
#include <android/bitmap.h>
#include <malloc.h>
#define LOG_TAG "pf"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define argb(a, r, g, b)(((a) & 0xff) << 24) | ((r) & 0xff)| (((g) & 0xff) << 8)| (((b) & 0xff) << 16)
typedef struct GifBean {
int current_frame;
int total_frame;
int *delayed;
} GifBean;
extern "C"
{
JNIEXPORT jlong JNICALL
Java_com_pf_gifplayerdemo_ndk_GifHandler_loadPath(JNIEnv *env, jobject instance, jstring path_) {
const char *path = env->GetStringUTFChars(path_, NULL);
int err;
//用系统函数打开一个gif文件,返回一个结构体,这个结构体为句柄
GifFileType *gifFileType = DGifOpenFileName(path, &err);
DGifSlurp(gifFileType);
//封装成自己想要的
GifBean *gifBean = (GifBean *) malloc(sizeof(struct GifBean));
//清空内存地址
memset(gifBean, 0, sizeof(struct GifBean));
//相当于view.setTag
gifFileType->UserData = gifBean;
gifBean->delayed = (int *) malloc(sizeof(int) * gifFileType->ImageCount);
memset(gifBean->delayed, 0, (sizeof(int) * gifFileType->ImageCount));
gifBean->total_frame = gifFileType->ImageCount;
ExtensionBlock *ext;
int i, j;
for (i = 0; i < gifFileType->ImageCount; i++) {
SavedImage frame = gifFileType->SavedImages[i];
for (j = 0; j < frame.ExtensionBlockCount; j++) {
if (frame.ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) {
ext = &frame.ExtensionBlocks[j];
break;
}
}
if (ext) {
int frame_delay = 10 * (ext->Bytes[2] << 8 | ext->Bytes[1]);
LOGE("时间:%d", frame_delay);
gifBean->delayed[i] = frame_delay;
}
}
LOGE("gif长度大小:%d", gifFileType->ImageCount);
env->ReleaseStringUTFChars(path_, path);
return (jlong) gifFileType;
}
JNIEXPORT jint JNICALL
Java_com_pf_gifplayerdemo_ndk_GifHandler_getWidth(JNIEnv *env, jobject instance, jlong ndkGif) {
return ((GifFileType *) ndkGif)->SWidth;
}
JNIEXPORT jint JNICALL
Java_com_pf_gifplayerdemo_ndk_GifHandler_getHeight(JNIEnv *env, jobject instance, jlong ndkGif) {
return ((GifFileType *) ndkGif)->SHeight;
}
//绘制一张图片
void drawFrame(GifFileType *gifFileType, GifBean *gifBean, AndroidBitmapInfo androidBitmapInfo,
void *pixels) {
//拿到当前帧
SavedImage savedImage = gifFileType->SavedImages[gifBean->current_frame];
GifImageDesc gifImageDesc = savedImage.ImageDesc;
//整幅图片的首地址
int *px = (int *) pixels;
//每一行的首地址
int *line;
//其中一个像素的位置,不是指针,在颜色表中的索引
int pointPixel;
GifByteType gifByteType;
GifColorType gifColorType;
ColorMapObject *colorMapObject = gifImageDesc.ColorMap;
px = (int *) ((char *) px + androidBitmapInfo.stride * gifImageDesc.Top);
int x, y;
for (y = gifImageDesc.Top; y < gifImageDesc.Top + gifImageDesc.Height; ++y) {
line = px;
for (x = gifImageDesc.Left; x < gifImageDesc.Left + gifImageDesc.Width; ++x) {
pointPixel = (y - gifImageDesc.Top) * gifImageDesc.Width + (x - gifImageDesc.Left);
gifByteType = savedImage.RasterBits[pointPixel];
gifColorType = colorMapObject->Colors[gifByteType];
line[x] = argb(255, gifColorType.Red, gifColorType.Green, gifColorType.Blue);
}
px = (int *) ((char *) px + androidBitmapInfo.stride);
}
}
JNIEXPORT jint JNICALL
Java_com_pf_gifplayerdemo_ndk_GifHandler_updateFrame(JNIEnv *env, jobject instance, jobject bitmap,
jlong ndkGif) {
GifFileType *gifFileType = (GifFileType *) ndkGif;
GifBean *gifBean = (GifBean *) gifFileType->UserData;
AndroidBitmapInfo androidBitmapInfo;
//代表一幅图片的像素数组
void *pixels;
AndroidBitmap_getInfo(env, bitmap, &androidBitmapInfo);
//锁定bitmap 一幅图片->二维数组
AndroidBitmap_lockPixels(env, bitmap, &pixels);
//TODO
drawFrame(gifFileType, gifBean, androidBitmapInfo, pixels);
//播放完之后,循环到下一帧
gifBean->current_frame += 1;
LOGE("当前帧:%d", gifBean->current_frame);
if (gifBean->current_frame >= gifBean->total_frame - 1) {
gifBean->current_frame = 0;
LOGE("重新来过,当前帧:%d", gifBean->current_frame);
}
AndroidBitmap_unlockPixels(env, bitmap);
return gifBean->delayed[gifBean->current_frame];
}
}
WebView方式
直接将gif放在assets路径下
webview = (WebView) findViewById(R.id.webview);
String url = "file:///android_asset/demo.gif";
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
// User settings
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setUseWideViewPort(true);//关键点
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webSettings.setDisplayZoomControls(false);
webSettings.setJavaScriptEnabled(true); // 设置支持javascript脚本
webSettings.setAllowFileAccess(true); // 允许访问文件
webSettings.setBuiltInZoomControls(true); // 设置显示缩放按钮
webSettings.setSupportZoom(true); // 支持缩放
webSettings.setLoadWithOverviewMode(true);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int mDensity = metrics.densityDpi;
Log.d("maomao", "densityDpi = " + mDensity);
if (mDensity == 240) {
webSettings.setDefaultZoom(WebSettings.ZoomDensity.FAR);
} else if (mDensity == 160) {
webSettings.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
} else if(mDensity == 120) {
webSettings.setDefaultZoom(WebSettings.ZoomDensity.CLOSE);
}else if(mDensity == DisplayMetrics.DENSITY_XHIGH){
webSettings.setDefaultZoom(WebSettings.ZoomDensity.FAR);
}else if (mDensity == DisplayMetrics.DENSITY_TV){
webSettings.setDefaultZoom(WebSettings.ZoomDensity.FAR);
}else{
webSettings.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
}
/**
* 用WebView显示图片,可使用这个参数 设置网页布局类型:
* 1、LayoutAlgorithm.NARROW_COLUMNS 适应内容大小
* 2、LayoutAlgorithm.SINGLE_COLUMN:适应屏幕,内容将自动缩放
*/
//webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webview.loadUrl(url);