本文為菜鳥窩作者蔣志碧的連載。“從 0 開始開發(fā)一款直播 APP ”系列來聊聊時下最火的直播 APP,如何完整的實現(xiàn)一個類"騰訊直播"的商業(yè)化項目
一、Android 中的逐幀動畫
先來說說什么是逐幀動畫,逐幀動畫是一種常見的動畫形式(Frame By Frame),其原理是在「連續(xù)的關(guān)鍵幀」中分解動畫動作,也就是在時間軸的每幀上逐幀繪制不同的內(nèi)容,使其連續(xù)播放而成動畫。 因為逐幀動畫的幀序列內(nèi)容不一樣,不但給制作增加了負擔而且最終輸出的文件量也很大,但它的優(yōu)勢也很明顯:逐幀動畫具有非常大的靈活性,幾乎可以表現(xiàn)任何想表現(xiàn)的內(nèi)容,而它類似與電影的播放模式,很適合于表演細膩的動畫。
在 Android 中逐幀動畫需要得到 AnimationDrawable 類的支持,是 Drawable 的間接子類。它主要用來創(chuàng)建一個逐幀動畫,并且可以對幀進行拉伸,把它設(shè)置為 View 的背景即可使用 AnimationDrawable.start() 方法播放。既然逐幀動畫是需要播放一幀一幀的圖像,所以需要為其添加幀。
AnimationDrawable 結(jié)構(gòu)圖

官方示例
用于創(chuàng)建逐幀動畫的對象,由一系列 Drawable 對象定義,可用作 View 對象的背景。 創(chuàng)建逐幀動畫的最簡單的方法是將 XML 文件中的動畫定義在 res/drawablefolder 中,并將其設(shè)置為 View 對象的背景。 然后,調(diào)用start() 來運行動畫。 在 XML 中定義的 AnimationDrawable 由單個 <animation-list> 元素和一系列嵌套的 <item> 標簽組成。 每個項目定義一個動畫框架。 參見下面的例子。

二、利用逐幀動畫實現(xiàn)自定義 Switch 控件
在谷歌的 Material Design 官方專題中,花了整整一頁來介紹 Delightful Details,其中有幀動畫的絕佳例子。

接下來講解直播中用到的控件,效果如下

定義了兩個 Animation-list,文件存放在 res/drawable 目錄下
switch_open.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
根標簽為animation-list,其中 oneshot 代表著是否只展示一遍,設(shè)置為 false 會不停的循環(huán)播放動畫
根標簽下,通過 item 標簽對動畫中的每一個圖片進行聲明
android:duration 表示展示所用的該圖片的時間長度
-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/btn_switch_off" android:duration="80"/>
<item android:drawable="@drawable/btn_switch_press" android:duration="80"/>
<item android:drawable="@drawable/btn_switch_on" android:duration="80"/>
</animation-list>
switch_close.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/btn_switch_on" android:duration="80" />
<item android:drawable="@drawable/btn_switch_press" android:duration="80" />
<item android:drawable="@drawable/btn_switch_off" android:duration="80" />
</animation-list>
CustomSwitch.java
CustomSwitch 繼承自 AppCompatImageView,AppCompatImageView 繼承自 ImageView,實現(xiàn)其構(gòu)造函數(shù)和方法
public class CustomSwitch extends android.support.v7.widget.AppCompatImageView {
private boolean mChecked = false;
private AnimationDrawable mAnimationDrawable;
private Handler mHandler;
private Runnable mRunnable;
//這里三個構(gòu)造函數(shù)參數(shù)類似,相互調(diào)用,在第三個構(gòu)造函數(shù)中初始化動畫
public CustomSwitch(Context context) {
this(context, null);
}
public CustomSwitch(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//初始化動畫
private void init() {
mHandler = new Handler();
mRunnable = new Runnable() {
@Override
public void run() {
onAnimation();
}
};
}
/**
* 動畫播放
*/
private void onAnimation() {
//判斷是否選中
if (mChecked) {
setImageResource(R.drawable.btn_switch_on);
} else {
setImageResource(R.drawable.btn_switch_off);
}
}
//設(shè)置選中
public void setChecked(boolean checked, boolean playAnim) {
if (checked == mChecked) {
return;
}
mChecked = checked;
//判斷是否正在播放
if (playAnim) {
setImageResource(mChecked ? R.drawable.switch_open : R.drawable.switch_close);
mAnimationDrawable = (AnimationDrawable) getDrawable();
mAnimationDrawable.start();
mHandler.postDelayed(mRunnable, getTotalDuration(mAnimationDrawable));
}
}
/**
* 返回總時長
* @param animationDrawable
* @return
*/
private long getTotalDuration(AnimationDrawable animationDrawable) {
int duration = 0;
for (int i = 0; i < animationDrawable.getNumberOfFrames(); i++) {
duration += animationDrawable.getDuration(i);
}
return duration;
}
public boolean isChecked() {
return mChecked;
}
}
三、CustomSwitch 的使用
activity_publish.xml
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="contentDescription">
<com.dali.admin.livestreaming.ui.customviews.CustomSwitch
android:id="@+id/btn_lbs"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:onClick="onClick"
android:src="@drawable/btn_switch_off"/>
</RelativeLayout>
PublishActivity.java
public class PublishActivity extends BaseActivity implements View.OnClickListener{
private CustomSwitch mBtnSwitch;
@Override
protected void setActionBar() {
}
@Override
protected void setListener() {
mBtnSwitch.setOnClickListener(this);
}
@Override
protected void initData() {
}
@Override
protected void initView() {
mBtnSwitch = obtainView(R.id.btn_record);
}
@Override
protected int getLayoutId() {
return R.layout.activity_publish;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_record:
if (mBtnSwitch.isChecked()) {
mBtnSwitch.setChecked(false, true);
mTvRecord.setText("不進行錄制");
} else {
mBtnSwitch.setChecked(true, true);
mTvRecord.setText("進行錄制");
}
break;
}
}
詳情請轉(zhuǎn)至 GitHub
參考:
https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
http://www.cnblogs.com/plokmju/p/android_AnimationDrawable.html
擼這個項目的一半,你就是大神 , 戳http://mp.weixin.qq.com/s/ZagocTlDfxZpC2IjUSFhHg