[置顶] Android中使用Movie显示gif动态图
http://blog.csdn.net/u012975705/article/details/48717391
在看这篇博文之前对attr自定义属性还是不是很熟的童鞋可以先看看:Android中attr详解(http://blog.csdn.net/u012975705/article/details/48716797)
源码下载:https://github.com/noyo/GifTest
效果图
显示的事两张动态gif图
实现代码
1、布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:gif="http://schemas.android.com/apk/res-auto"
android:gravity="center"
android:orientation="vertical" >
<com.practice.noyet.giftest.MyGiftView
android:id="@+id/gif1"
gif:gif="@raw/gift1"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center_horizontal"
android:enabled="false" />
<com.practice.noyet.giftest.MyGiftView
android:layout_marginTop="10dp"
gif:gif="@raw/gift2"
android:id="@+id/gif2"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center_horizontal"
android:enabled="false" />
</LinearLayout>
2、自定义的com.practice.noyet.giftest.MyGiftView.Java代码(代码中都有注释,我就不废话了):
package com.practice.noyet.giftest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by noyet on 2015/9/24.
*/
public class MyGiftView extends View {
/** gift动态效果总时长,在未设置时长时默认为1秒 */
private static final int DEFAULT_MOVIE_DURATION = 1000;
/** gift图片资源ID */
private int mGiftId;
/** Movie实例,用来显示gift图片 */
private Movie mMovie;
/** 显示gift图片的动态效果的开始时间 */
private long mMovieStart;
/** 动态图当前显示第几帧 */
private int mCurrentAnimationTime = 0;
/** 图片离屏幕左边的距离 */
private float mLeft;
/** 图片离屏幕上边的距离 */
private float mTop;
/** 图片的缩放比例 */
private float mScale;
/** 图片在屏幕上显示的宽度 */
private int mMeasuredMovieWidth;
/** 图片在屏幕上显示的高度 */
private int mMeasuredMovieHeight;
/** 是否显示动画,为true表示显示,false表示不显示 */
private boolean mVisible = true;
/** 动画效果是否被暂停 */
private volatile boolean mPaused = false;
public MyGiftView(Context context) {
this(context, null);
}
public MyGiftView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_gifViewStyle);
}
public MyGiftView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setViewAttributes(context, attrs, defStyleAttr);
}
/**
*
* @param context 上下文
* @param attrs 自定义属性
* @param defStyle 默认风格
*/
@SuppressLint("NewApi")
private void setViewAttributes(Context context, AttributeSet attrs,
int defStyle) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
final TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.MyGiftView, defStyle, R.style.Widget_MyGiftView);
mGiftId = array.getResourceId(R.styleable.MyGiftView_gif, -1);
mPaused = array.getBoolean(R.styleable.MyGiftView_paused, false);
array.recycle();
if (mGiftId != -1) {
byte[] bytes = getGiftBytes();
mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);
}
}
/**
* 设置gif图资源
* @param giftResId
*/
public void setMovieResource(int giftResId) {
this.mGiftId = giftResId;
byte[] bytes = getGiftBytes();
mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);
requestLayout();
}
/**
* 手动设置 Movie对象
* @param movie Movie
*/
public void setMovie(Movie movie) {
this.mMovie = movie;
requestLayout();
}
/**
* 得到Movie对象
* @return Movie
*/
public Movie getMovie() {
return mMovie;
}
/**
* 设置要显示第几帧动画
* @param time
*/
public void setMovieTime(int time) {
mCurrentAnimationTime = time;
invalidate();
}
/**
* 设置暂停
*
* @param paused
*/
public void setPaused(boolean paused) {
this.mPaused = paused;
if (!paused) {
mMovieStart = android.os.SystemClock.uptimeMillis()
- mCurrentAnimationTime;
}
invalidate();
}
/**
* 判断gif图是否停止了
*
* @return
*/
public boolean isPaused() {
return this.mPaused;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMovie != null) {
int movieWidth = mMovie.width();
int movieHeight = mMovie.height();
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
float scaleW = (float) movieWidth / (float) maximumWidth;
mScale = 1f / scaleW;
mMeasuredMovieWidth = maximumWidth;
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
} else {
setMeasuredDimension(getSuggestedMinimumWidth(),
getSuggestedMinimumHeight());
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mVisible = getVisibility() == View.VISIBLE;
}
@Override
protected void onDraw(Canvas canvas) {
if (mMovie != null) {
if (!mPaused) {
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
} else {
drawMovieFrame(canvas);
}
}
}
/**
* 重绘
*/
@SuppressLint("NewApi")
private void invalidateView() {
if (mVisible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
invalidate();
}
}
}
/**
* 更新当前显示进度
*/
private void updateAnimationTime() {
long now = android.os.SystemClock.uptimeMillis();
// 如果第一帧,记录起始时间
if (mMovieStart == 0) {
mMovieStart = now;
}
// 取出动画的时长
int dur = mMovie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIE_DURATION;
}
// 算出需要显示第几帧
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}
/**
* 绘制图片
* @param canvas 画布
*/
private void drawMovieFrame(Canvas canvas) {
// 设置要显示的帧,绘制即可
mMovie.setTime(mCurrentAnimationTime);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScale, mScale);
mMovie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
}
@SuppressLint("NewApi")
@Override
public void onScreenStateChanged(int screenState) {
super.onScreenStateChanged(screenState);
mVisible = screenState == SCREEN_STATE_ON;
invalidateView();
}
@SuppressLint("NewApi")
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
/**
* 将gif图片转换成byte[]
* @return byte[]
*/
private byte[] getGiftBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = getResources().openRawResource(mGiftId);
byte[] b = new byte[1024];
int len;
try {
while ((len = is.read(b, 0, 1024)) != -1) {
baos.write(b, 0, len);
}
baos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos.toByteArray();
}
}
3、MainActivity.java:
package com.practice.noyet.giftest;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
private MyGiftView gif1;
private MyGiftView gif2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gif1 = (MyGiftView) findViewById(R.id.gif1);
gif2 = (MyGiftView) findViewById(R.id.gif2);
/** ........ */
}
}
一共有2位博乐进行推荐
[置顶] Android中使用Movie显示gif动态图的更多相关文章
- [置顶] Android中使用sqlite3操作SQLite
SQLite库包含一个名字叫做sqlite3的命令行,它可以让用户手工输入并执行面向SQLite数据库的SQL命令.本文档提供一个样使用sqlite3的简要说明. 一.创建数据库: 1.将sqlit ...
- [置顶] Android EditText/TextView使用SpannableString显示复合文本
在Android中EditText用于编辑文本,TextView用于显示文本,但是有时候我们需要对其中的文本进行样式等方面的设置.Android为我们提供了SpannableString类来对指定文本 ...
- [置顶] Android学习系列-Android中解析xml(7)
Android学习系列-Android中解析xml(7) 一,概述 1,一个是DOM,它是生成一个树,有了树以后你搜索.查找都可以做. 2,另一种是基于流的,就是解析器从头到尾解析一遍xml文件. ...
- [置顶] Android开发笔记(成长轨迹)
分类: 开发学习笔记2013-06-21 09:44 26043人阅读 评论(5) 收藏 Android开发笔记 1.控制台输出:called unimplemented OpenGL ES API ...
- [置顶] Android AlarmManager实现不间断轮询服务
在消息的获取上是选择轮询还是推送得根据实际的业务需要来技术选型,例如对消息实时性比较高的需求,比如微博新通知或新闻等那就最好是用推送了.但如果只是一般的消息检测比如更新检查,可能是半个小时或一个小时一 ...
- [置顶]
Android 适配真要命?
原始尺寸场景 相信大家对上面也有所有耳闻另外就是如何计算屏幕的密度一般都是按照勾股定理例如中等屏幕密度 480^2+800^2开根号 然后除以当前屏幕尺寸3.5-4.2之间尺寸. 对于刚出来的那些An ...
- 【有人@我】Android中高亮变色显示文本中的关键字
应该是好久没有写有关技术类的文章了,前天还有人在群里问我,说群主很长时间没有分享干货了,今天分享一篇Android中TextView在大段的文字内容中如何让关键字高亮变色的文章 ,希望对大家有所帮助, ...
- [置顶] Android系统五大布局详解Layout
我们知道Android系统应用程序一般是由多个Activity组成,而这些Activity以视图的形式展现在我们面前,视图都是由一个一个的组件构成的.组件就是我们常见的Button.TextEdit等 ...
- [置顶] Android布局管理器 - 详细解析布局实现
布局管理器都是以ViewGroup为基类派生出来的; 使用布局管理器可以适配不同手机屏幕的分辨率,尺寸大小; 布局管理器之间的继承关系 : 在上面的UML图中可以看出, 绝对布局 帧布局 网格布局 相 ...
随机推荐
- 如何在脚本中执行SQL语句并获得结果输出?
这里需要用到的工具叫做sqlcmd.exe, 它随SQL server的安装而安装. 该可执行程序的位置在: C:\Program Files\Microsoft SQL Server\xxx\Too ...
- 读书笔记-C#中装箱拆箱性能
前言 最近在看王涛大神的<你必须知道的.NET(第二版)>一书,嗯,首先膜拜一下…. 在书中的第五章-品味类型中,对装箱与拆箱一节感触很深,概念本身相信每一个程序猿都不陌生,装 ...
- Java基础(二):基本数据类型和变量类型
一.java基本数据类型: 变量就是申请内存来存储值.也就是说,当创建变量的时候,需要在内存中申请空间.内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据. Java 的两 ...
- [JSP]JSP中include指令和include动作的差别
include指令是编译阶段的指令,即include所包括的文件的内容是编译的时候插入到JSP文件里,JSP引擎在推断JSP页面未被改动,否则视为已被改动. 因为被包括的文件是在编译时才插入的.因此假 ...
- Android中操作SQLite数据库
我又回到了安卓的学习当中,忙来忙去终于忙的差不多有时间做自己的事情了,这感觉实在是太棒了!!本来想写android的控件以及他们的监视器的,但是我查了查android的手册,基本上都能查到,但是查有些 ...
- 独立开发人员低成本推广APP的18条技巧
导语:知道并不等于运行,有些最主要的推广方法往往会被忽略.这些,是自国外开发人员总结出的这18条经验. 如今市面上充满了大牌子大公司和大制作的手机游戏,常常有游戏花300万成本开发,然后再花2000万 ...
- Discuz常见大问题-如何允许用户插入视频-如何自己在页面中插入视频
从视频的下面分享中获取html代码 然后粘贴到你创建页面的指定位置(注意从优酷复制的视频宽度和高度可能比较小,你可以自己调整,或者占据100%) 最终的实现效果
- Linux一些基本命令一(学习笔记三)
菜鸟记录. 一.更改主机名 hostname 新的主机名 hostname ln0491 将主机名更改为ln0491 登出再登陆,就变为新的主机名 二.新建文件夹和删除 如:在当前路径新建data文件 ...
- Uubntu14.04下 Hadoop集群环境搭建
1机器信息 master192.168.3.10 hadoop/ hadoop slave1192.168.3.11 hadoop/ hadoop slave2192.168.3.12 hadoop/ ...
- Sql_Handle and Plan_Handle Explained
For batches, the SQL handles are hash values based on the SQL text. For database objects such as sto ...
2015-10-08 20:36:45 