动画webP、apng探讨AndroidAndroid开发

Android-ImageView支持Apng动画播放

2019-12-06  本文已影响0人  湘北南

1. 概述

在深入了解Apng动画播放之前,我们需要对Apng的结构有所了解,具体参见Apng动画介绍。对Apng的整体结构有所了解后,下面我们来讲讲Apng动画的播放,主要包括Apng解析和Apng渲染两个过程,这个参见Android-Apng动画的播放。下面我们讲讲ApngImageView是怎么实现Apng的动画播放的,先看一下效果图。

ApngImageView的效果图

2. ApngImageView的实现

ApngImageView的实现主要是靠ApngPlayAssist类来实现的,ApngPlayAssist是实现了Runnable接口,主要做了以下三个事情:

public void run() {
            if (mAnimParams == null) {
                return;
            }
            Log.d(TAG, "PlayThread run()");
            try {
                // play it
                playAnimation();

                // play end
                stop();

            } catch (InterruptedException e) {
                Log.e(TAG, Log.getStackTraceString(e));

            } finally {
                mFrameRender.recycle();
            }
        }

1)在子线程里面,通过ApngFrameRender和ApngReader读取Apng的每一帧。

说明:playAnimation主要是读取每一帧,然后通知ImageView去绘制每一帧。


private void playAnimation() throws InterruptedException {
            try {
                mFrameRender = new ApngFrameRender();
                // step 1: prepare
                ApngReader reader = new ApngReader(mAnimParams.imagePath);
                ApngACTLChunk actl = reader.getACTL();
                if (mAnimParams.isHasBackground) setBgColor(true);
                // all loop count = apng_internal_loop_count x apng_play_times
                // if apng_internal_loop_count == 0 then set it to 1 (not support loop indefinitely)
                int loopCount = mAnimParams.loopCount * (actl.getNumPlays() == 0 ? 1 : actl.getNumPlays());

                // step 2: draw frames
                boolean isLoop = loopCount == AnimParams.PLAY_4_LOOP;

                for (int lc = 0; lc < loopCount || isLoop; lc++) {
                    // reallocated to head again if loops more the one time
                    if (lc > 0 || isLoop) reader.reset();
                    for (int i = 0; i < actl.getNumFrames(); i++) {
                        long start = System.currentTimeMillis();
                        // get frame data
                        curFrame = reader.nextFrame();
                        if (curFrame == null) break; // if read next frame failed, break loop

                        byte[] data = readStream(curFrame.getImageStream());

                        if (data != null) {
                            //Bitmap frameBmp = BitmapFactory.decodeStream(frame.getImageStream());

                            curFrameBmp = BitmapFactory.decodeByteArray(data, 0, data.length);

                            Log.d(TAG, "read the " + i + " frame:" + (System.currentTimeMillis() - start) + "ms");

                            // init the render and calculate scale rate
                            // at first time get the frame width and height
                            if (lc == 0 && i == 0) {
                                int imgW = curFrame.getWidth(), imgH = curFrame.getHeight();
                                mScale = calculateScale(mAnimParams.scaleType, imgW, imgH, getWidth(), getHeight());
                                mFrameRender.prepare(imgW, imgH);
                            }


                            index++;
                            mStep = ApngLoader.Const.STEP_DRAW_FRAME;
                            ApngImageView.this.postInvalidate();

                            // delay
                            int waitMillis = Math.round(curFrame.getDelayNum() * DELAY_FACTOR / curFrame.getDelayDen())
                                    - (int) (System.currentTimeMillis() - start);
                            Thread.sleep(waitMillis > 0 ? waitMillis : 0);
                        }

                    }
                }

            } catch (Exception e) {
                Log.e(TAG, Log.getStackTraceString(e));
            } finally {
                if (mAnimParams.isHasBackground) setBgColor(false);
            }
        }

2)把读取到Apng的每一帧在Canvas里绘制。

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (mStep){
            case ApngLoader.Const.STEP_CLEAR_CANVAS:
                 mApngPlayAssist.clearCanvas(canvas);
                break;
            case ApngLoader.Const.STEP_DRAW_FRAME:
                mApngPlayAssist.drawFrame(canvas);
                break;
           default:
               break;

        }

    }

private void drawFrame(Canvas canvas) {

            if (mIsPlay
                    && mFrameRender != null
                    && curFrame != null
                    && curFrameBmp != null) {

                //start to draw the frame
                try {
                    Matrix matrix = new Matrix();
                    matrix.setScale(mScale, mScale);
                    Bitmap bmp = mFrameRender.render(curFrame, curFrameBmp);
                    //saveBitmap(bmp, index);
                    //anti-aliasing
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                    float[] tranLeftAndTop = ApngUtils.getTranLeftAndTop(canvas, bmp, mAnimParams.align, mScale, mAnimParams.percent);
                    canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
                    matrix.postTranslate(tranLeftAndTop[0], tranLeftAndTop[1]);
                    canvas.drawBitmap(bmp, matrix, null);
                    curFrameBmp.recycle();
                } catch (Exception e) {
                    Log.e(TAG, "draw error msg:" + Log.getStackTraceString(e));
                }
            }
        }

3)动画播放结束,给业务层回调。

 private void stop() {
            mIsPlay = false;
            mStep = ApngLoader.Const.STEP_CLEAR_CANVAS;
            notifyPlayCompeleted();
        }

3. 使用方法

这里我们写了一个ApngLoader,使用方法如下:

private void playAnim(){
        //File file = FileUtils.processApngFile(COLOR_BALL_IMAGE_PATH, this);
        File file1 = FileUtils.processApngFile(COLOR_BALL_IMAGE_PATH, this);

        ApngLoader.getInstance().loadApng(file1.getAbsolutePath(), mApngImageView);

    }

4. 说明

上面的一些说明可能比较简单,具体的实现,大家可以去下面的项目地址下载:

ApngImageView源码:ApngImageView

SakuApng地址:Apng的项目地址-SakuApng

例子演示:

ApngImageView的效果图
上一篇下一篇

猜你喜欢

热点阅读