美文网首页动画webP、apng探讨AndroidAndroid开发
Android-ImageView支持Apng动画播放

Android-ImageView支持Apng动画播放

作者: 湘北南 | 来源:发表于2019-12-06 17:55 被阅读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的效果图

    相关文章

      网友评论

        本文标题:Android-ImageView支持Apng动画播放

        本文链接:https://www.haomeiwen.com/subject/qhvegctx.html