大綱
主要想實現的是在一個頁面中播放一個動畫、就像突然灑金幣的效果。
我想要的效果是一個activity覆蓋一個activity,并透明。
本文章主要講如何在動畫activity中播放gif動畫:(第一種需要把gif轉換成一幀一幀的格式,后兩種方式是需要把gif--轉換成mp4)
- 通過逐幀動畫
- 內存泄漏、heap size 常識大小 -xms -xmx
- 通過webview播放
為了效果更好,應該對webview進行以下處理:- webview背景透明
- webview右下角縮放按鈕
- webview隱藏滾動條
- 屏幕單機、雙擊放大、滑動無響應事件
- 定時銷毀動畫activity
- webview屏幕適配
- 通過videoview播放(將gif--mp4)
- 自定義videoview 讓播放布滿整個屏幕
- videoview隱藏進度條
- videoview播放本地文件 獲取res/raw的絕對地址
- 通過surfaceview播放(將gif--mp4)
- 學習地址:點這里
- 通過glide開源庫直接播放gif 自行查閱
- 對于復雜動畫大內存mp4 .gif 推薦使用https://github.com/airbnb/lottie-android
com.airbnb.android:lottie這個庫,讓設計師將復雜view通過工具轉成json形式,android客戶端通過加載json方式加載動畫,實現很多復雜動效。
大殺器Lottie:把AE動畫轉換成Android原生動畫
這部分工作主要在設計師那里,但這種方式是最輕量切動畫效果最好,能很好的播放各種炫酷動畫
通過逐幀動畫
首先了解:android動畫分類
逐幀播放應該是最簡單的一種的,就是把連續的圖片連起來播放,設置每幀的時間
有兩種方式、一種是xml 一種是代碼方式實現,
本例我選擇代碼方式,用一個imageview作為承載,
// 核心代碼如下:
private AnimationDrawable anim;
private ImageView imageView;
private void init() {
LayoutInflater inflater= (LayoutInflater)
getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.red_packets_frame_animation_view_layout,this);
imageView=findViewById(R.id.imageView);
anim=new AnimationDrawable();
}
public void play_animation(){
for (int i=10;i<=15;i++){
int id=getResources().getIdentifier("a"+i,"drawable",getContext().getPackageName());
Drawable drawable=getResources().getDrawable(id);
anim.addFrame(drawable,100);//100ms
anim.setOneShot(true);//true 不循環播放
}
imageView.setImageDrawable(anim);
anim.stop();
anim.start();
}
但是這種方式真的很容易出oom!如果幀拆分的圖片過多,盡量不要使用這種方式,另外補充一點,還有一種Movie類,也像幀播放一樣,適用于播放一些小gif,自行了解。
還有提到的oom, 用heap工具可以看到占用內存過多泄漏,在這里簡單了解一下常識,
-xms 初始heap size 一般為物理內存的1/64
-xmx 最大heap size 最大為物理內存的1/4
- JVM內存首先受限于實際的最大物理內存,(運行內存RAM 2\3\4G)
JVM初始分配的內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的內存由-Xmx指定,默認是物理內存的1/4。默認空余堆內存 小于 40%時,JVM就會增大堆直到-Xmx的最大限制;空余堆內存大于70%時,JVM會減少堆直到-Xms的最小限制。因此服務器一般設置-Xms、 -Xmx相等以避免在每次GC 后調整堆的大小.
通過webview播放
在webview播放動畫本來是非常簡單的一件事,但是如想達到我們想要的效果要修改的地方還是很多的。
- webview背景透明
- webview右下角縮放按鈕
- webview隱藏滾動條
- 屏幕單機、雙擊放大、滑動無響應事件
- 定時銷毀動畫activity (通過開啟了一個新的線程,根據動畫時間銷毀activity)
- webview屏幕適配
//核心代碼
public class AnimationActivity extends Activity {
private WebView webView;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation2);
webView=findViewById(R.id.webview);
webView.setVerticalScrollBarEnabled(false); //垂直滾動條不顯示
webView.setHorizontalScrollBarEnabled(false);//水平不顯示
WebSettings webSettings=webView.getSettings();
webSettings.setDisplayZoomControls(false);//隱藏webview縮放按鈕
webSettings.setJavaScriptEnabled(true);
webSettings.setUseWideViewPort(true);//屏幕適配:設置webview推薦使用的窗口,設置為true
webSettings.setLoadWithOverviewMode(true);//設置webview加載的頁面的模式,也設置為true
webSettings.setAllowFileAccess(true);
webSettings.setSupportZoom(true);//是否支持縮放
webSettings.setBuiltInZoomControls(true);//添加對js功能的支持
webView.setWebViewClient(new WebViewClient());
webView.setBackgroundColor(0);
String gifPath = "file:///android_asset/animation.gif";
webView.loadUrl(gifPath);
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
webView.destroy();
AnimationActivity2.this.finish();
}
}
};
new Thread(){
@Override
public void run() {
long startTime = System.currentTimeMillis();
Log.i("test","System.currentTimeMillis()1:"+System.currentTimeMillis());
try {
this.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
Log.i("test","System.currentTimeMillis()2:"+System.currentTimeMillis());
if (endTime - startTime >4000){
//startTime = endTime;
Message message=new Message();
message.what=0;
handler.sendMessage(message);
}
}
} .start();
}
@Override
public void onBackPressed() {
super.onBackPressed();
webView.destroy();
finish();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_SCROLL:
return true;
case MotionEvent.ACTION_MOVE:
return true;
case MotionEvent.ACTION_POINTER_DOWN:
return true;
case MotionEvent.ACTION_MASK:
return true;
case MotionEvent.ACTION_DOWN:
return true;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
}
記得設置activity透明,這樣透明的gif的效果就浮現在另一個activity上,效果相對較好。
android:theme="@android:style/Theme.Translucent.NoTitleBar"
通過videoview播放
因為videoview有默認尺寸,而我們想實現全屏動畫,需要改變它默認大小 所以要重寫相對方法,獲得全屏尺寸
private int screenWidth;
private int screenHeigh;
//提供一個接口 讓activity傳入尺寸
public void setMetrics(int width,int height){
screenWidth=width;
screenHeigh=height;
}
//重新繪制
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(screenWidth, screenHeigh);
}
//核心代碼如下
public class AnimationActivity3 extends Activity {
private GifVideoView gifVideoView;
private MediaController mc;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation3);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width=dm.widthPixels;
int heigh=dm.heightPixels;
gifVideoView=findViewById(R.id.gifVideoView);
mc = new MediaController(this);
mc.setVisibility(View.INVISIBLE);
gifVideoView.setMetrics(width,heigh);
gifVideoView.setBackgroundColor(0);
gifVideoView.setMediaController(mc);
Uri uri=Uri.parse( "android.resource://"+getPackageName()+"/"+R.raw.test);
gifVideoView.setVideoURI(uri);
gifVideoView.start();
gifVideoView.requestFocus();
//視頻編碼格式不支持的情況
gifVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
if(what==MediaPlayer.MEDIA_ERROR_SERVER_DIED){
Log.v("view exception","Media Error,Server Died"+extra);
}else if(what==MediaPlayer.MEDIA_ERROR_UNKNOWN){
Log.v("video exception","Media Error,Error Unknown "+extra);
}
return true;
}
});
}
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}
小demo效果如圖:
Untitled.gif