范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Android开发之ImageView播放GIF动画实例

  Android开发之ImageView播放GIF动画实例
  Android的原生控件并不支持播放GIF格式的图片,如果想在Android中显示一张GIF动态图片,可以利用 ImageView控件来完成,但是放进去之后,你会发现,ImageView它只会显示这张图片的第一帧,不会产生任何的动画效果。我们必须通过自定义控件的方式来实现ImageView播放GIF 图片的功能。
  首先我们来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF动态图片。
  先新建一个项目PowerImageViewTest,这里使用Android 4.0+Eclipse。
  由于是要自定义控件,会需要一些自定义的控件属性,因此我们需要在values目录下新建一个attrs.xml的文件,在这个文件中添加项目需要的自定义属性。
  这里我们目前暂时只需要一个自动播放auto_play属性,XML文件代码如下:
  <?xml version="1.0" encoding="utf-8"?>
  
  
  
  这个文件完成之后,下面我们来开始编写主类PowerImageView类,由于PowerImageView类需要支持ImageView的所有功能,我们必须要让PowerImageView继承自ImageView,代码如下:
  public class PowerImageView extends ImageView implements OnClickListener {
  /**
  * 播放GIF动画的关键类
  */
  private Movie mMovie;
  /**
  * 开始播放按钮图片
  */
  private Bitmap mStartButton;
  /**
  * 记录动画开始的时间
  */
  private long mMovieStart;
  /**
  * GIF图片的宽度
  */
  private int mImageWidth;
  /**
  * GIF图片的高度
  */
  private int mImageHeight;
  /**
  * 图片是否正在播放
  */
  private boolean isPlaying;
  /**
  * 是否允许自动播放
  */
  private boolean isAutoPlay;
  /**
  * PowerImageView构造函数。
  *
  * @param context
  */
  public PowerImageView(Context context) {
  super(context);
  }
  /**
  * PowerImageView构造函数。
  *
  * @param context
  */
  public PowerImageView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
  }
  /**
  * PowerImageView构造函数,在这里完成所有必要的初始化操作。
  *
  * @param context
  */
  public PowerImageView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PowerImageView);
  int resourceId = getResourceId(a, context, attrs);
  if (resourceId != 0) {
  // 当资源id不等于0时,就去获取该资源的流
  InputStream is = getResources().openRawResource(resourceId);
  // 使用Movie类对流进行解码
  mMovie = Movie.decodeStream(is);
  if (mMovie != null) {
  // 如果返回值不等于null,就说明这是一个GIF图片,下面获取是否自动播放的属性
  isAutoPlay = a.getBoolean(R.styleable.PowerImageView_auto_play, false);
  Bitmap bitmap = BitmapFactory.decodeStream(is);
  mImageWidth = bitmap.getWidth();
  mImageHeight = bitmap.getHeight();
  bitmap.recycle();
  if (!isAutoPlay) {
  // 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
  mStartButton = BitmapFactory.decodeResource(getResources(),R.drawable.start_play);
  setOnClickListener(this);
  }
  }
  }
  }
  @Override
  public void onClick(View v) {
  if (v.getId() == getId()) {
  // 当用户点击图片时,开始播放GIF动画
  isPlaying = true;
  invalidate();
  }
  }
  @Override
  protected void onDraw(Canvas canvas) {
  if (mMovie == null) {
  // mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法
  super.onDraw(canvas);
  } else {
  // mMovie不等于null,说明是张GIF图片
  if (isAutoPlay) {
  // 如果允许自动播放,就调用playMovie()方法播放GIF动画
  playMovie(canvas);
  invalidate();
  } else {
  // 不允许自动播放时,判断当前图片是否正在播放
  if (isPlaying) {
  // 正在播放就继续调用playMovie()方法,一直到动画播放结束为止
  if (playMovie(canvas)) {
  isPlaying = false;
  }
  invalidate();
  } else {
  // 还没开始播放就只绘制GIF图片的第一帧,并绘制一个开始按钮
  mMovie.setTime(0);
  mMovie.draw(canvas, 0, 0);
  int offsetW = (mImageWidth - mStartButton.getWidth()) / 2;
  int offsetH = (mImageHeight - mStartButton.getHeight()) / 2;
  canvas.drawBitmap(mStartButton, offsetW, offsetH, null);
  }
  }
  }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  if (mMovie != null) {
  // 如果是GIF图片则重写设定PowerImageView的大小
  setMeasuredDimension(mImageWidth, mImageHeight);
  }
  }
  /**
  * 开始播放GIF动画,播放完成返回true,未完成返回false。
  *
  * @param canvas
  * @return 播放完成返回true,未完成返回false。
  */
  private boolean playMovie(Canvas canvas) {
  long now = SystemClock.uptimeMillis();
  if (mMovieStart == 0) {
  mMovieStart = now;
  }
  int duration = mMovie.duration();
  if (duration == 0) {
  duration = 1000;
  }
  int relTime = (int) ((now - mMovieStart) % duration);
  mMovie.setTime(relTime);
  mMovie.draw(canvas, 0, 0);
  if ((now - mMovieStart) >= duration) {
  mMovieStart = 0;
  return true;
  }
  return false;
  }
  /**
  * 通过Java反射,获取到src指定图片资源所对应的id。
  *
  * @param a
  * @param context
  * @param attrs
  * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
  */
  private int getResourceId(TypedArray a, Context context, AttributeSet attrs) {
  try {
  Field field = TypedArray.class.getDeclaredField("mValue");
  field.setAccessible(true);
  TypedValue typedValueObject = (TypedValue) field.get(a);
  return typedValueObject.resourceId;
  } catch (Exception e) {
  e.printStackTrace();
  } finally {
  if (a != null) {
  a.recycle();
  }
  }
  return 0;
  }
  }
  这个类的代码注释已经非常详细了,我再来简单地解释一下。可以看到,我们重写了ImageView中所有的构建函数,使得 PowerImageView的用法可以和ImageView完全相同。在构造函数中,则是对所有必要的数据进行了初始化操作。首先,我们调用了 getResourceId()方法去获取图片资源对应的id值,在getResourceId()方法内部是通过Java的反射机制来进行获取的。得到了图片资源的id后,我们将它转换成InputStream,然后传入到Movie.decodeStream()方法中以解码出Movie对象。如果得到的Movie对象等于null,说明这是一张普通的图片资源,就不再进行任何特殊处理,因为父类ImageView都帮我们处理好了。如果得到的 Movie对象不等于null,则说明这是一张GIF图片,接着就要去获取是否允许自动播放、图片的宽高等属性的值。如果不允许自动播放,还要给播放按钮 注册点击事件,默认是不允许自动播放的。
  接下来会进入到onMeasure()方法中。在这个方法中我们进行判断,如果这是一张GIF图片,则需要将PowerImageView的宽高重定义,使得控件的大小刚好可以放得下这张GIF图片。
  再往后就会进入到onDraw()方法中。在这个方法里同样先判断当前是一张普通的图片还是GIF图片,如果是普通的图片就直接调用 super.onDraw()方法交给ImageView去处理就好了。如果是GIF图片,则先判断该图是否允许自动播放,允许的话就调用 playMovie()方法去播放GIF图片就好,不允许的话则会先在PowerImageView中绘制该GIF图片的第一帧,并在图片上绘制一个播放 按钮,当用户点击了播放按钮时,再去调用playMovie()方法去播放GIF图片。
  下面我们来看看playMovie()方法中是怎样播放GIF图片的吧。可以看到,首先会对动画开始的时间做下记录,然后对动画持续的时间做下记 录,接着使用当前的时间减去动画开始的时间,得到的时间就是此时PowerImageView应该显示的那一帧,然后借助Movie对象将这一帧绘制到屏 幕上即可。之后每次调用playMovie()方法都会绘制一帧图片,连贯起来也就形成了GIF动画。注意,这个方法是有返回值的,如果当前时间减去动画 开始时间大于了动画持续时间,那就说明动画播放完成了,返回true,否则返回false。
  完成了PowerImageView的编写,下面我们就来看一看如何使用它吧,其实非常简单,打开或新建activity_main.xml,代码如下所示:
  
  
  
  可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张图片即可,这里指定的anim就是一张GIF图片。然后我们让PowerImageView在布局里居中显示MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifest.xml中还有一点需要注意,有些4.0 以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated属性来完成,代码如下所示:
  <?xml version="1.0" encoding="utf-8"?>
  
  
  android:allowBackup="true"
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme"
  android:hardwareAccelerated="false"
  >
  android:name="com.example.powerimageviewtest.MainActivity"
  android:label="@string/app_name" >
  
  
  
  
  现在可以来运行一下代码了,一打开程序你就会看到GIF图片的第一帧,点击图片之后就可以播放GIF动画了。
  然后我们还可以通过修改activity_main.xml中的代码,给它加上允许自动播放的属性,代码如下所示:
  
  
  
  这里使用了刚才我们自定义的属性,通过attr:auto_play来启用和禁用自动播放功能。现在将auto_play属性指定成true后,PowerImageView上就不会再显示一个播放按钮,而是会循环地自动播放动画。不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图 片,PowerImageView表现的结果就和ImageView完全一致,现在我们来放一张普通的PNG图片,修改 activity_main.xml中的代码,如下所示:
  
  
  
  这里在src属性里面指定了一张名字为myphoto的PNG图片,图片在布局正中央显示出来了,正是普通ImageView所具备的功能。我们还可以在PowerImageView中指定android:scaleType等属性,用法和原生的ImageView完全一样。

35岁的程序员,如何避免职业生涯的焦虑?这3类困境要避免最近和很多35岁的程序员聊天,确实能感觉到他们对于职业生涯的焦虑,公司卡大龄程序员入职的情况也是真实存在的,但35岁以后在公司担任技术要职架构师和CTO的也是普遍存在的。那么如何解大公司早报格力申请董明珠秘书孟羽童商标破防了成B站年度弹幕(整理卢思叶)今天是11月30日星期二,过去的二十四小时有这些公司动态可以关注微信公布互联互通新进展,将在群聊中试行电商外链访问合兴餐饮集团回应吉野家使用发臭肉沫华为斥资1。88亿神舟老总声援联想联想高管拿10亿年薪根本就不多,甚至还有点少文北桥校对北桥自从司马南发布了关于联想的7条视频后,联想可谓是被推上了舆论的风口浪尖。毕竟联想作为国内早期的科技公司,不仅没有把国内的芯片操作系统带动起来,反而变成了大家口中的组装联想预热了个寂寞,雷军给高通站台,小米12信息首现如此早的时间召开发布会,这在国内应该是第一次。对,说的就是高通骁龙技术峰会,大清早一起来全都是关于它的消息。不过对于普通用户来讲,大家只关注哪家会使用,什么时候能正式商用。根据高通访谈视频美团为什么要自己研发硬件产品?本期,跟大家分享一个餐饮系统硬件的话题。我们知道,目前市面上有很多品牌的硬件产品,那为什么美团还要选择自己研发呢?今天我们邀请了美团餐饮系统硬件产品负责人谭勇,跟大家分享这背后的原没了华为的中国手机市场,四大国产品牌在高端手机领域不堪一击这两天,苹果重返中国第一刷爆了数码圈,而我也写了一篇文章分析了原因,得出的结论是主要是因为四大国产品牌接不住华为让出的高端手机市场份额,才会让苹果借着iPhone13的春风重返中国世纪华通再度牵手腾讯盛趣游戏将联运腾讯游戏产品世纪华通(002602)11月30日晚间公告,全资子公司盛趣游戏与腾讯签署联运合作框架协议。此前10月份,世纪华通刚宣布与腾讯云计算签订战略合作协议,发力IDC业务布局。再度签收也智能锁排行榜的十大品牌有哪些呢?很多人在准备选购智能锁时都会有些无从下手,毕竟手机汽车的品牌随口就能说出来很多,但是智能锁的品牌却鲜有人问津,那么笔者就从京东销量上来为大家介绍一些畅销的品牌供大家参考,这样在选购冬季洗澡不再担心忽冷忽热!美的新品燃气热水器智能即开即热冬季严寒大风,家里的热水器洗澡时又出现了忽冷忽热的情况?其实是你的热水器该换啦!今天介绍的这款美的新品燃气热水器JSQ30RS6就是不错的选择,零冷水设计开机即热,沐浴无需过久等待盒马配送员工伤难背后众包权益仍难保障本报记者刘旺北京报道盒马配送员在配送路上出现事故,在咨询工伤认证过程中,发现劳动关系已变更,不是曾签订合同的公司。进行劳动仲裁时,甚至发现此前劳务合同上的公章是假的。这是发生在盒马美团Q3财报对于餐饮的老板意义在哪?外卖业务收入美团Q3财报显示外卖业务收入264。85亿元,盈利8。76亿元,纯利润3。3个点。美团对这个外卖利润显然不满意,但是目前整体的外卖扣点已经涨的差不多了。利润低最大原因还
鸿蒙168版本,电池健康它来了最近更新到了鸿蒙2。0。0。168版本后,电池健康来了。准确的说是显示出来了。如果有用华为手机的朋友更新到168版本后。打开设置电池更多电池设置便可以看到电池最大容量。我看到很多人骁龙888旗舰降价1300元,120Hz2K曲面屏,4099就可入手虽然目前骁龙888Plus机型已经发布了不少,但骁龙888机型依然深受用户欢迎,骁龙888Plus依然采用5纳米工艺,性能提升并不是很大,骁龙888芯片已经性能过剩,完全可以满足日9月安卓新机全面盘点最低1799元即可入手早上好,马上就要到国庆假期了,不知道各位都定好回家的车票了吗?其实,国庆假期前夕是一年一度的换机热潮,毕竟适逢长假,谁不想手持一款潮流新机回家跟家人团聚呢?今天我就为各位用户总结了从AK47进化到了AK12?全面革新之作九州风神AK620上手评测前言九州风神,相信这个散热品牌熟悉DIY的小伙伴都应该知道。作为目前国内最大的电脑散热器研发和生产厂商,这个品牌散热器的销量相信也无需多言,我自己都先后购入不少。从反人类400(玄4档调温,一键速热,完美烧水壶值得拥有热水壶作为家庭必备的小家电之一,可谓家家都要用,每天都要用。但是传统的电热水壶存在不够智能的问题,日常生活中只能烧开水,正渴的时候水滚烫喝不了,等想起来喝了,它又放凉了,另外还有使乌镇之光超算中心近日落成超级计算机何以超级?央广网北京9月28日消息据中央广播电视总台中国之声新闻超链接报道,在世界互联网大会永久举办地桐乡乌镇,乌镇之光超算中心近日落成。作为浙江首个大型超算中心,乌镇之光从开工建设,到完成将再次改变世界的七大技术展望以前从来没有一个时代,技术进步在地球上引起如此迅速的发展。近年来,数字化和新技术已经从根本上改变了世界但仍然看不到尽头。相反,已经发生的事情激发了人们对在可预见的未来可能发生的事情每月交300元租金快递小哥有了10秒换电站来源北京日报快递小哥有了10秒换电站本报讯(记者陈强)大兴区林校路街道兴政中里社区有处平房院,好几位快递外卖小哥租住在这里。最近,街道在院里装上了换电站,小哥们每月交300元租金,装个系统教你安装全新Windows11全程展示,精彩无限跟我一起,穿越时间!Windows11是微软最新的操作系统,相信已经没有人不知道了,但是很多人还不知道如何安装Windows11,甚至不知道如何下载微软原版的安装文件,别急,如果你36个月流畅不卡顿OneUI3让三星GalaxyZFold35G久用如新近日,三星新一代折叠屏手机三星GalaxyZFold35G在国内正式亮相,该机不仅带来了沉浸式的影音娱乐体验和更高效的大屏生产力,其搭载的最新OneUI3系统中还引入了AI系统加速华硕无畏Pro14酷睿版笔记本评测2。8KOLED屏带来全新视觉体验与传统的LCD屏幕相比,OLED的每一个像素都可以单独发光,显示黑色时甚至可以完全关闭,不需要LCD那样的逆光系统,黑色时甚至可以完全关闭,所以能做到更高的亮度与对比度,暗色时能耗