Backlight87
10/24/2017 - 9:29 AM

可以同时展示Image和Gif的自定义控件

同时可以支持Image和gif的展示器 缺点:如果gif太大(最好不超过5M)可能会崩溃,可以试试用矩阵进行压缩 gif必须连续播放完成中间不能停(与我的计时方式有关)

不过现在也不推荐了,如果项目有引入直接就用glide加载就好了,现在的框架都是比较完善的

 
 //可以获取控件对应属性名的值ID,注意是ResourceValue
 private int getResourceId(AttributeSet attrs) {
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            if (attrs.getAttributeName(i).equals("src")) {
                return attrs.getAttributeResourceValue(i, 0);
            }
        }
        
        
        
//如何下载gif文件
一开始我一直右键点击保存图片会发生文件格式问题,后来找到:
只要把gif往外拖拖到桌面就好了


//播放不了gif
有可能是硬件加速没有关
解决办法:在manifest清单的application下配置属性:
android:hardwareAccelerated="false"

//就跟平常ImageView的使用一样

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:attr="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.administrator.applicationtext.activity.PowerImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/tetete"
        attr:auto_play="false" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PowerImageView">
        <attr name="auto_play" format="boolean"></attr>
    </declare-styleable>
    
</resources>
/**
 * 
 * 基本原理就是继承imageView,然后把src资源给movie解码,如果movie为空就按照原来imageview走,否则把movie绘制到屏幕上
 * 同时可以支持Image和gif的展示器
 * 缺点:如果gif太大(最好不超过5M)可能会崩溃,可以试试用矩阵进行压缩
 * gif必须连续播放完成中间不能停(与我的计时方式有关)
 * Created by 洪少鹏 on 2017/10/24.
 */
public class PowerImageView extends android.support.v7.widget.AppCompatImageView implements View.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;

    public PowerImageView(Context context) {
        super(context);
    }

    public PowerImageView(Context context, @Nullable 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(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();*/
                //可以直接通过movie示例获取宽高
                mImageWidth = mMovie.width();
                mImageHeight = mMovie.height();
                if (!isAutoPlay) {
                    // 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
                    mStartButton = BitmapFactory.decodeResource(getResources(),
                            R.drawable.ic_play_arrow_black_24dp);
                    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();//这个就是要求重新执行一次draw过程,所以就是画一帧,刷新再画一帧
            } 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);
            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;
    }

    /**
     * 通过遍历attr属性列表
     *
     * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
     */
    private int getResourceId(AttributeSet attrs) {
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            if (attrs.getAttributeName(i).equals("src")) {
                return attrs.getAttributeResourceValue(i, 0);
            }
        }
        return 0;
    }
}