使用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