1. 程式人生 > >使用SurfaceView載入多張大解析度圖片做幀動畫,解決OOM問題

使用SurfaceView載入多張大解析度圖片做幀動畫,解決OOM問題

專案需求:動態背景


需求很簡單,只是用幀動畫做一個動態的背景而已,但若是70多張圖片都是1920*1080,一張485k的話,傳統意義上的幀動畫就很難實現了,往往載入10張就開始OOM。

一般來說,常用的實現動態背景的有效方式有三種:

①視訊:果斷粗暴,清晰度很有保證,但是在無限輪播重複的時候,總會有一瞬間的卡頓,這真的很讓人鬱悶。

②GIF動態圖:直接用Glide載入就可以實現無限重複的動態背景,而且銜接沒有絲毫停頓,但是有個問題,清晰度很難保證,有時候GIF太大,載入也會很緩慢。

③幀動畫:清晰度非常高,但是等著OOM吧!

下面我們使用第四種方法,繼承SurfaceView,實現載入多張大圖片,完成真正意義上的幀動畫。

圖片資源介面:PictureInterface

package com.giousa.frameanimationtest;

/**
 * Description:
 * Author:Giousa
 */
public interface PictureInterface {

    int[] srcId =
            {R.drawable.a_00000, R.drawable.a_00001, R.drawable.a_00002, R.drawable.a_00003,
                    R.drawable.a_00004, R.drawable.a_00005, R.drawable.a_00006,
                    R.drawable.a_00007, R.drawable.a_00008, R.drawable.a_00009,
                    R.drawable.a_00010, R.drawable.a_00011, R.drawable.a_00012, R.drawable.a_00013,
                    R.drawable.a_00014, R.drawable.a_00015, R.drawable.a_00016,
                    R.drawable.a_00017, R.drawable.a_00018, R.drawable.a_00019
                    , R.drawable.a_00020, R.drawable.a_00021, R.drawable.a_00022, R.drawable.a_00023,
                    R.drawable.a_00024, R.drawable.a_00025, R.drawable.a_00026,
                    R.drawable.a_00027, R.drawable.a_00028, R.drawable.a_00029
                    , R.drawable.a_00030, R.drawable.a_00031, R.drawable.a_00032, R.drawable.a_00033,
                    R.drawable.a_00034, R.drawable.a_00035, R.drawable.a_00036,
                    R.drawable.a_00037, R.drawable.a_00038, R.drawable.a_00039
                    , R.drawable.a_00040, R.drawable.a_00041, R.drawable.a_00042, R.drawable.a_00043,
                    R.drawable.a_00044, R.drawable.a_00045, R.drawable.a_00046,
                    R.drawable.a_00047, R.drawable.a_00048, R.drawable.a_00049
                    , R.drawable.a_00050, R.drawable.a_00051, R.drawable.a_00052, R.drawable.a_00053,
                    R.drawable.a_00054, R.drawable.a_00055, R.drawable.a_00056,
                    R.drawable.a_00057, R.drawable.a_00058, R.drawable.a_00059
                    , R.drawable.a_00060, R.drawable.a_00061, R.drawable.a_00062, R.drawable.a_00063,
                    R.drawable.a_00064, R.drawable.a_00065, R.drawable.a_00066,
                    R.drawable.a_00067, R.drawable.a_00068, R.drawable.a_00069
                    , R.drawable.a_00070, R.drawable.a_00071, R.drawable.a_00072, R.drawable.a_00073,
                    R.drawable.a_00074, R.drawable.a_00075

            };
}

繼承SurfaceView的類FrameAnimation:
package com.giousa.frameanimationtest;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;

/**
 * Description:
 * Author:Giousa
 * Date:2016/11/4
 * Email:
[email protected]
*/ public class FrameAnimation extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mSurfaceHolder; private boolean mIsThreadRunning = true; // 執行緒執行開關 public static boolean mIsDestroy = false;// 是否已經銷燬 private int[] mBitmapResourceIds;// 用於播放動畫的圖片資源id陣列 private ArrayList<String> mBitmapResourcePaths;// 用於播放動畫的圖片資源path陣列 private int totalCount;//資源總數 private Canvas mCanvas; private Bitmap mBitmap;// 顯示的圖片 private int mCurrentIndext;// 當前動畫播放的位置 private int mGapTime = 150;// 每幀動畫持續存在的時間 private boolean mIsRepeat = false; private OnFrameFinishedListener mOnFrameFinishedListener;// 動畫監聽事件 public FrameAnimation(Context context) { this(context, null); initView(); } public FrameAnimation(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } public FrameAnimation(Context context, AttributeSet attrs) { this(context, attrs, 0); initView(); } private void initView() { mSurfaceHolder = this.getHolder(); mSurfaceHolder.addCallback(this); // 白色背景 setZOrderOnTop(true); setZOrderMediaOverlay(true); } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 當surfaceView銷燬時, 停止執行緒的執行. 避免surfaceView銷燬了執行緒還在執行而報錯. // mIsThreadRunning = false; // try { // Thread.sleep(mGapTime); // } catch (InterruptedException e) { // e.printStackTrace(); // } // // mIsDestroy = true; } /** * 製圖方法 */ private void drawView() { // 無資原始檔退出 if (mBitmapResourceIds == null && mBitmapResourcePaths == null) { Log.e("frameview", "the bitmapsrcIDs is null"); mIsThreadRunning = false; return; } // 鎖定畫布 if(mSurfaceHolder != null){ mCanvas = mSurfaceHolder.lockCanvas(); } try { if (mSurfaceHolder != null && mCanvas != null) { mCanvas.drawColor(Color.WHITE); if (mBitmapResourceIds != null && mBitmapResourceIds.length > 0) mBitmap = BitmapFactory.decodeResource(getResources(), mBitmapResourceIds[mCurrentIndext]); else if (mBitmapResourcePaths != null && mBitmapResourcePaths.size() > 0) { mBitmap = BitmapFactory.decodeFile(mBitmapResourcePaths.get(mCurrentIndext)); } Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); Rect mSrcRect, mDestRect; mSrcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); mDestRect = new Rect(0, 0, getWidth(), getHeight()); mCanvas.drawBitmap(mBitmap, mSrcRect, mDestRect, paint); // 播放到最後一張圖片 if (mCurrentIndext == totalCount - 1) { //TODO 設定重複播放 //播放到最後一張,當前index置零 mCurrentIndext = 0; } } } catch (Exception e) { e.printStackTrace(); } finally { mCurrentIndext++; if(mCurrentIndext >= totalCount){ mCurrentIndext = 0; } if (mCanvas != null) { // 將畫布解鎖並顯示在螢幕上 if(mSurfaceHolder!=null){ mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } if (mBitmap != null) { // 收回圖片 mBitmap.recycle(); } } } @Override public void run() { if (mOnFrameFinishedListener != null) { mOnFrameFinishedListener.onStart(); } // 每隔150ms重新整理螢幕 while (mIsThreadRunning) { drawView(); try { Thread.sleep(mGapTime); } catch (Exception e) { e.printStackTrace(); } } if (mOnFrameFinishedListener != null) { mOnFrameFinishedListener.onStop(); } } /** * 開始動畫 */ public void start() { if (!mIsDestroy) { mCurrentIndext = 0; mIsThreadRunning = true; new Thread(this).start(); } else { // 如果SurfaceHolder已經銷燬丟擲該異常 try { throw new Exception("IllegalArgumentException:Are you sure the SurfaceHolder is not destroyed"); } catch (Exception e) { e.printStackTrace(); } } } /** * 設定動畫播放素材的id * * @param bitmapResourceIds 圖片資源id */ public void setBitmapResoursID(int[] bitmapResourceIds) { this.mBitmapResourceIds = bitmapResourceIds; totalCount = bitmapResourceIds.length; } /** * 設定動畫播放素材的路徑 * * @param bitmapResourcePaths */ public void setmBitmapResourcePath(ArrayList bitmapResourcePaths) { this.mBitmapResourcePaths = bitmapResourcePaths; totalCount = bitmapResourcePaths.size(); } /** * 設定每幀時間 */ public void setGapTime(int gapTime) { this.mGapTime = gapTime; } /** * 結束動畫 */ public void stop() { mIsThreadRunning = false; } /** * 繼續動畫 */ public void reStart() { mIsThreadRunning = false; } /** * 設定動畫監聽器 */ public void setOnFrameFinisedListener(OnFrameFinishedListener onFrameFinishedListener) { this.mOnFrameFinishedListener = onFrameFinishedListener; } /** * 動畫監聽器 * * @author qike */ public interface OnFrameFinishedListener { /** * 動畫開始 */ void onStart(); /** * 動畫結束 */ void onStop(); } /** * 當用戶點選返回按鈕時,停止執行緒,反轉記憶體溢位 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // 當按返回鍵時,將執行緒停止,避免surfaceView銷燬了,而執行緒還在執行而報錯 if (keyCode == KeyEvent.KEYCODE_BACK) { mIsThreadRunning = false; } return super.onKeyDown(keyCode, event); } }

主介面:MainActivity
package com.giousa.frameanimationtest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements PictureInterface{

    private final String TAG = MainActivity.class.getSimpleName();
    private FrameAnimation mFrameAnimation;
    private Button mSecond;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }



    private void initView() {
        mFrameAnimation = (FrameAnimation) findViewById(R.id.frame_animation);
        mSecond = (Button) findViewById(R.id.btn_second);
        mSecond.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

    private void initData() {
        initAnimation();
    }

    private void initAnimation() {
        //設定資原始檔
        mFrameAnimation.setBitmapResoursID(srcId);
        //設定監聽事件
        mFrameAnimation.setOnFrameFinisedListener(new FrameAnimation.OnFrameFinishedListener() {
            @Override
            public void onStop() {
                Log.e(TAG, "stop");

            }

            @Override
            public void onStart() {
                Log.e(TAG, "start");
                Log.e(TAG, Runtime.getRuntime().totalMemory() / 1024 + "k");
            }
        });

        //設定單張圖片展示時長
        mFrameAnimation.setGapTime(150);
        mFrameAnimation.start();
    }
}

主佈局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.giousa.frameanimationtest.MainActivity">

    <com.giousa.frameanimationtest.FrameAnimation
        android:id="@+id/frame_animation"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:gravity="center"
        android:textSize="25sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="跳轉到第二介面"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginBottom="88dp"
        android:id="@+id/btn_second"/>
</RelativeLayout>


相關推薦

使用SurfaceView載入張大解析度圖片動畫解決OOM問題

專案需求:動態背景 需求很簡單,只是用幀動畫做一個動態的背景而已,但若是70多張圖片都是1920*1080,一張485k的話,傳統意義上的幀動畫就很難實現了,往往載入10張就開始OOM。 一般來說,常用的實現動態背景的有效方式有三種: ①視訊:果斷粗暴,清晰度很有保證,

使用圖片動畫的效能優化

背景 QQ群的送禮物功能需要載入幾十張圖然後做幀動畫,但是多張圖片載入造成了非常大的效能開銷,導致圖片開始載入到真正播放動畫的時間間隔比較長。所以需要研究一些優化方案提升載入圖片和幀動畫的效能。 原理分析 iOS系統從磁碟載入一張圖片,使用UIImageView顯示

Qt釋出版解決聲音和圖片、中文字型亂碼問題(需要在main裡寫上QApplication::addLibraryPath("./plugins")才能載入圖片圖片很清楚)

前些天做Qt釋出版,發現居然不顯示圖片,後來才發現原來還有圖片的庫沒加!找找吧,去qt的安裝包,我裝在了F盤,在F盤F:/QT/qt/plugins,找到了plugins,這裡面有個   imageformats是圖片的庫,裡面有jpg,gif等,你用到那種格式就加那種!加的時候一點過要注意,將

Android逐動畫動畫載入圖片過多時OOM異常的解決和替代方法

1.首先新增逐幀動畫 播放逐幀動畫,在工程中res目錄下建立一個anim資料夾,新增動畫anim_welcome.xml檔案如下: <?xml version="1.0" encoding="utf-8"?> <animation-li

圖片抖動(動畫)

angle 屬性 layer 添加 -s rpath 移動 幀動畫 key 1.幀動畫介紹: CAKeyframeAnimation它可以在多個值之間進行動畫. 設置多值之間的屬性為: 後面是一個數組,就是要設

Android 動畫載入動畫

1、建立drawable檔案ring_animation.xml <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.co

Qt移動應用開發(三):使用精靈圖片實現動畫

       上一篇博文講到了Qt Quick對於動畫的一般支援,動畫的形式多樣,配合不同的插值函式,可以幾乎實現所有想要的動畫效果,而對於遊戲的一些特殊的效果比如說幀動畫,Qt更是有專門的類來實現。下面我們就來看看Qt Quick中究竟是對幀動畫是如何實現的吧。 原

android很多圖片做成動畫造成記憶體溢位的解決方法。

package com.familydoctor.widget; import android.os.Handler; import android.util.Log; import android.widget.ImageView; import com.familydo

自己家用電腦站點server解決動態IP、無公網IP、80port被封、HTTP被屏蔽

管理系 映射 綁定 方案 自己 屏蔽 net 數據 web 動態IP、無公網IP、80port被封、HTTP被屏蔽,這些問題都是自己的server做站點服務,easy遇到面對的問題。當出現這些問題時。能夠利用當前的開放網絡資源一一解決。 解決原理分析: 動態IP。公

移動端動畫抖動解決方案

也有 遇到 pri 其他人 fix mage 移動端 anim 其他 概述 今天在做移動端幀動畫的時候遇到了抖動的問題,自己查找了一下資料,並且總結出了3個比較好的解決方案,記錄下來,供以後開發時參考,相信對其他人也有用。 由於移動端用的是rem布局,所以計算下來總會有一些

特效序列動畫可指定開始和結束點

 直接貼程式碼: Shader "James/FX/MeshFrame" { Properties { [Enum(Add, 1, Blend, 10)] _DstBlend ("Blend Mode", Float) = 1 _Ma

Android開發——動畫使用篇章(動畫補間動畫)(一)

Android 動畫分為 view動畫,幀動畫,屬性動畫,本片文章是參考多篇動畫介紹部落格,總結動畫使用API,使用場景。適合日常開發 搬磚使用。 幀動畫 幀動畫是最容易實現的一種動畫,這種動畫更多的依賴於完善的UI資源,他的原理就是將一張張單獨的圖片連貫的進行播放,從而在視覺上

Android - 動畫動畫補間動畫屬性動畫以及插值器)

一: 動畫的分類 幀動畫 補間動畫 屬性動畫 二:解析 1. 幀動畫 (1)定義 這些圖片是將一些列的drawable組合在一起,進行連續的播放, 類似於以前電影源用膠捲進行動畫播放 (2)有圖有真相 (3)準備圖片 看著是不是還行,哈哈,

服務端讀取圖片內容返回前端解決圖片跨域問題

最近在配合前端開發,開發一個圖片裁剪功能的時候,遇到一個oss圖片跨域請求,無法訪問的問題,索性自己寫個介面,先讀取圖片檔案流再直接返回前端。具體程式碼如下。 1.新建檔案流內容封裝類FileContent 1 public class FileContent { 2 private b

網路圖片轉換為base64解決跨域問題

function convertImgToBase64(url, callback, outputFormat) { var canvas = document.createElement('CANVAS'), ctx = canvas.getContext(

Android動畫實現,防OOM,比原生動畫集節約超過十倍的資源

2015年專案接到一個需求,實現一個嚮導動畫,這個動畫一共六十張圖片,當時使用的是全志A33的開發(512的記憶體),通過使用Android的動畫集實現,效果特別卡頓,然後想到這種方式來實現,效果很流暢

關於RecyclerView的下拉重新整理自定義動畫第三方框架PtrFrameLayout使用手冊

首先放上一張gif圖片 首先是xml檔案:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res

Win10安裝後必的優化解決磁碟100%佔用

01關閉家庭組 控制面板–管理工具–服務– HomeGroup Listener和HomeGroup Provider禁用。 02關閉磁碟碎片整理、自動維護計劃任務 選中磁碟C-屬性–工具–對驅動器進行優化和碎片整理–優化–更改設定–取消選擇按計劃執行。

百度地圖 迴圈載入marker並新增多個資訊視窗解決只顯示最後一個視窗資訊的問題

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ShowNurseryInfo.aspx.cs"     Inherits="Bim5D_Web.Nursery.ShowNurseryInfo" %> <!DOCTY

序列動畫今天偶然發現的很有意思的動畫

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <ti