本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
一、Android 中的逐帧动画
先来说说什么是逐帧动画,逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在「连续的关键帧」中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。 因为逐帧动画的帧序列内容不一样,不但给制作增加了负担而且最终输出的文件量也很大,但它的优势也很明显:逐帧动画具有非常大的灵活性,几乎可以表现任何想表现的内容,而它类似与电影的播放模式,很适合于表演细腻的动画。
在 Android 中逐帧动画需要得到 AnimationDrawable 类的支持,是 Drawable 的间接子类。它主要用来创建一个逐帧动画,并且可以对帧进行拉伸,把它设置为 View 的背景即可使用 AnimationDrawable.start() 方法播放。既然逐帧动画是需要播放一帧一帧的图像,所以需要为其添加帧。
AnimationDrawable 结构图
官方示例
用于创建逐帧动画的对象,由一系列 Drawable 对象定义,可用作 View 对象的背景。 创建逐帧动画的最简单的方法是将 XML 文件中的动画定义在 res/drawablefolder 中,并将其设置为 View 对象的背景。 然后,调用start() 来运行动画。 在 XML 中定义的 AnimationDrawable 由单个 <animation-list> 元素和一系列嵌套的 <item> 标签组成。 每个项目定义一个动画框架。 参见下面的例子。
二、利用逐帧动画实现自定义 Switch 控件
在谷歌的 Material Design 官方专题中,花了整整一页来介绍 Delightful Details,其中有帧动画的绝佳例子。
接下来讲解直播中用到的控件,效果如下
定义了两个 Animation-list,文件存放在 res/drawable 目录下
switch_open.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
根标签为animation-list,其中 oneshot 代表着是否只展示一遍,设置为 false 会不停的循环播放动画
根标签下,通过 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,实现其构造函数和方法
public class CustomSwitch extends android.support.v7.widget.AppCompatImageView {
private boolean mChecked = false;
private AnimationDrawable mAnimationDrawable;
private Handler mHandler;
private Runnable mRunnable;
//这里三个构造函数参数类似,相互调用,在第三个构造函数中初始化动画
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);
}
}
//设置选中
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;
}
}
详情请转至 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