可以播放GIF动画的ImageView

来源:互联网 发布:linux 删除alias 编辑:程序博客网 时间:2024/05/08 18:23

Android的原生控件并不支持播放GIF格式的图片。我们都知道,在Android中如果想要显示一张图片,可以借助ImageView来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果。今天我们来编写一个自定义的增强型ImageView(继承ImageView),可以播放GIF格式的图片,暂且叫做GifImageView吧。

1.自定义属性

<?xml version="1.0" encoding="utf-8"?>  <resources>        <declare-styleable name="GifImageView">          <attr name="auto_play" format="boolean"></attr>      </declare-styleable>    </resources> 

2.自定义View中获取属性值

  private Movie mMovie;//播放动画需要用到的,系统类  private int mImageWidth;//动画的imageview的宽度  private int mImageHeight;//动画imageview的高度  private long mMovieStart = 0;// 播放开始  private boolean isAutoPlay;//是否自动播放  private Bitmap mStartPlay;//开始按钮  private boolean isPlaying=false;//记录是否正在播放  private float mScale;//图片的缩放比  private int mMeasuredGifWidth;//缩放后图片宽  private int mMeasuredGifHeight;//缩放后图片高  ...  private void init(Context context, AttributeSet attrs) {    TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.GifImageView);    // 通过反射拿布局中src的资源id,所以gif文件需要放在布局的src中    int resourceId = getResourceId(attributes, context, attrs);    if (resourceId != 0) {      // 说明是gif动画      // 1.将resourcesId变成流      // 2.用Move来decode解析流      // 3.获得bitmap的长宽      InputStream is = getResources().openRawResource(resourceId);      mMovie = Movie.decodeStream(is);      if (mMovie != null) {        Bitmap bitmap = BitmapFactory.decodeStream(is);        mImageWidth = bitmap.getWidth();        mImageHeight = bitmap.getHeight();        // 用完释放        bitmap.recycle();        // 获得是否允许自动播放,如果不允许自动播放,则初始化播放按钮        isAutoPlay = attributes.getBoolean(R.styleable.GifImageView_auto_play, false);        if (!isAutoPlay) {          mStartPlay = BitmapFactory.decodeResource(getResources(),R.drawable.start_play);          setOnClickListener(this);        }            }    }    //回收资源    attributes.recycle();  }  /**   * 通过反射拿布局中src的资源id   *    * @param attrs   * @param context   * @param attributes   */  private int getResourceId(TypedArray attributes, Context context, AttributeSet attrs) {    try {      Field filed = TypedArray.class.getDeclaredField("mValue");      filed.setAccessible(true);      TypedValue typeValue = (TypedValue) filed.get(attributes);      return typeValue.resourceId;    } catch (Exception e) {      e.printStackTrace();    }    }    return 0;}

3.重写onMesure()

 @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    if (mMovie != null) {        /*         * Calculate horizontal scaling         */        float scaleW = 1f;        int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);        if (measureModeWidth != MeasureSpec.UNSPECIFIED) {            int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);            scaleW = (float) mImageWidth / (float) maximumWidth;        }        /*         * calculate vertical scaling         */        float scaleH = 1f;        int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);        if (measureModeHeight != MeasureSpec.UNSPECIFIED) {            int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);            scaleH = (float) mImageHeight / (float) maximumHeight;        }        /*         * calculate overall scale         */        mScale = 1f / Math.max(scaleH, scaleW);        mMeasuredGifWidth = (int) (mImageWidth * mScale);        mMeasuredGifHeight = (int) (mImageHeight * mScale);        setMeasuredDimension(mMeasuredGifWidth, mMeasuredGifHeight);    }  }

4.重写onDraw()

  @Override  protected void onDraw(Canvas canvas) {    if (mMovie == null) {      // mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法      super.onDraw(canvas);    } else {        // mMovie不等于null,说明是张GIF图片      if (isAutoPlay) {        // 如果允许自动播放,就播放        playMovie(canvas);        invalidate();      } else {        // 不允许自动播放的话        // 1.判断是否正在播放        // 2.获得第一帧的图像        // 3.然后添加播放按钮        if (isPlaying) {          // 如果正在播放就playmoive继续播放          if (playMovie(canvas)) {            isPlaying = false;          }          invalidate();        } else {          // 第一帧          mMovie.setTime(0);          canvas.save(Canvas.MATRIX_SAVE_FLAG);          canvas.scale(mScale, mScale);          mMovie.draw(canvas, 0, 0);// 画          canvas.restore();          // 绘制开始按钮          int offsetW = (mMeasuredGifWidth - mStartPlay.getWidth()) / 2;          int offsetH = (mMeasuredGifHeight - mStartPlay.getHeight()) / 2;          canvas.drawBitmap(mStartPlay, offsetW, offsetH, null);        }      }    }  }  /**   * 播放gif动画   *    * @param canvas   */  private boolean playMovie(Canvas canvas) {    // 1.获取播放的时间    // 2.如果开始start=0,则认为是开始    // 3.记录播放的时间    // 4.设置进度    // 5.画动画    // 6.如果时间大于了播放的时间,则证明结束    long now = SystemClock.uptimeMillis();    if (mMovieStart == 0) {      mMovieStart = now;    }    int duration = mMovie.duration();    if (duration == 0) {      duration = 1000;    }    //记录gif播放了多少时间    int relTime = (int) ((now - mMovieStart) % duration);    mMovie.setTime(relTime);// 设置时间    canvas.save(Canvas.MATRIX_SAVE_FLAG);    canvas.scale(mScale, mScale);    mMovie.draw(canvas, 0, 0);// 画    canvas.restore();    if ((now - mMovieStart) >= duration) {      // 结束      mMovieStart = 0;      return true;    }    return false;  }

5.添加点击事件

  @Override  public void onClick(View v) {    if(v.getId()==getId()){      isPlaying=true;      invalidate();    }  }

还有一点需要注意,有些4.0以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated=false来完成。


现在我们来看看运行后效果如何吧,
布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:attr="http://schemas.android.com/apk/res/com.hx.gifimageview"    android:id="@+id/container"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <com.hx.gifimageview.GifImageView        android:layout_width="150dip"        android:layout_height="150dip"        android:layout_margin="10dp"        android:src="@drawable/shulan"        attr:auto_play="false" />    <com.hx.gifimageview.GifImageView        android:layout_width="150dip"        android:layout_height="150dip"        android:layout_margin="10dp"        android:src="@drawable/shulan"        attr:auto_play="true" />    <com.hx.gifimageview.GifImageView        android:layout_width="150dip"        android:layout_height="150dip"        android:layout_margin="10dp"        android:src="@drawable/jingtai"        attr:auto_play="true" /></LinearLayout>

这里写图片描述
图一的auto_play属性为false,会显示第一帧和一个播放按钮,点击后Gif图片会自动运行。
图二的auto_play属性为true,会自动循环播放Gif。
图三我们给的资源是静态图片,因为自定义View继承ImageView,所以具备ImageView所有特性,因此也能显示静态图片。

Demo地址

0 0