最近公司要做的项目中要求实现一个简单的视频录制功能的组件,我简单设计了一个,主要功能就是开始,暂停,停止和显示录制时间长度。首先看一下效果图:
可以看到是一个非常简单的动画效果,为了方便使用,我把他做成了aar并发布到了jCenter,集成方式:
compile 'com.rangaofei:sakarecordview:0.0.2'
组件里用到的库也非常简单,包括databinding,属性动画和layouttransition。通过这个简单的库简单的介绍一下LayoutTransition的用法,其中也会插入一些简单的databinding和属性动画的知识点,遇到困难请自行解决。
使用方法: 在xml文件中添加自定义控件:
<com.hanlinbode.sakarecordview.RecordView
android:id="@+id/rv_saka"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="30dp"
app:record_view_time_string="HHMMSS" />
record_view_time_string
属性是枚举类型,用来表示时间表示形式:
HHMMSS 00:00:00
MMSS 00:00
HH_MM_SS 00-00-00
MM_SS 00-00
//更新时间
void updateTime(long)
/*设置监听器,
void onInitial();
void onStartRecord();
void onPauseRecord();
void onResumeRecord();
void onStopRecord();*/
void setRecordListener(RecordListener)
void setDebug(boolean)
LayoutTransition简介
来源于官方文档
LayoutTransition能够在viewgroup的布局发生变化时产生一个动画效果。可以通过
ViewGroup.setLayoutTransition(LayoutTransition transition)
来设置过度效果。调用这个方法将会使用内置的过渡动画(alpha值变化,xy位置变化等),开发者可用通过`LayoutTransition.setAnimator(int transitionType,Animator animator)来设置自己的过渡效果。能够出发动画的情况有两种:
- item添加(设置View.VISIBLE也可)
- item移除(设置View.GON也可)
当viewgroup中发生上述两种行为时,或者由于添加删除而引起其他item变化,都会触发动画。
过渡动画的触发种类
这个种类指的是在发生某种行为时(例如item添加或者删除),共有5种:
CHANGE_APPEARING,CHANGE_DISAPPERING,APPEARING,DISAPPEARING,CHANGING
。每种状态有自己的一个位标记。
CHANGE_APPEARING
指示动画将会在新的控件添加到viewgroup中的时候引起其他view变化触发。它的标志位是0x01。也就是当addview或者将非VISIBLE状态的view设置为VISIBILE状态时其他的view被影响到时也会触发。
CHANGE_DISAPPEARING
指示动画将会在viewgroup删除控件的时候引起其他view变化触发,它的标志位是0x02。也就是当removeview或者将VISIBLE状态的view设置为非VISIBLE状态时其他的view被影响到也会触发。
APPEARING
当新的view添加到viewgroup中的时候触发。它的标志位是0x04。也就是当addview或者将非VISIBLE状态的view设置为VISIBILE状态时会触发。
DISAPPERAING
指示动画将会在viewgroup删除控件时触发,它的标志位是0x08。也就是当removeview或者将VISIBLE状态的view设置为非VISIBLE状态时会触发。
CHANGING
出去前边的四种,当布局发生变化时会触发动画。它的标志位是0x10。这个标志位默认是不激活的,但是可以通过enableTransitonType(int)来激活。
了解了这些,这个库基本就能实现了。
RecordView分析
左边的开始和暂停按钮是一个checkbox实现的,通过一个简单的selector来切换图片,并在右侧布局出现和消失的时候有一个缩放动画。我们可以通过设置一个简单的ObjectAnimator监听器来实现这个缩放:
ObjectAnimator animShow = ObjectAnimator.ofFloat(null, "scaleX", 0, 1);
animShow.setInterpolator(new OvershootInterpolator());
animShow.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (isDebug()) {
Log.e(TAG, "show anim value=" + (float) animation.getAnimatedValue());
}
recordState.setPlayScale(1 + (float) animation.getAnimatedValue() / 5);
}
});
layoutTransition.setAnimator(LayoutTransition.APPEARING, animShow);
ObjectAnimator animHide = ObjectAnimator.ofFloat(null, "alpha", 1, 0);
animHide.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (isDebug()) {
Log.e(TAG, "hide anim value=" + (float) animation.getAnimatedValue());
}
recordState.setPlayScale(1 + (float) animation.getAnimatedValue() / 5);
}
});
layoutTransition.addTransitionListener(this);
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animHide);
binding.rootView.setLayoutTransition(layoutTransition);
binding.rootContainer.setLayoutTransition(layoutTransition);
record是自定一个一个类,用来设置显示的图片和时间,并保存缩放的状态:
public class RecordState extends BaseObservable implements Parcelable {
private boolean recording;
private String time = "00:00:00";
private float playScale = 1;
@DrawableRes
private int playDrawable;
@DrawableRes
private int stopDrawable;
public RecordState(int playDrawable, int stopDrawable) {
this.playDrawable = playDrawable;
this.stopDrawable = stopDrawable;
}
@Bindable
public boolean isRecording() {
return recording;
}
public void setRecording(boolean recording) {
this.recording = recording;
notifyPropertyChanged(BR.recording);
}
//省略其他的getter和setter
@Bindable
public float getPlayScale() {
return playScale;
}
public void setPlayScale(float playScale) {
this.playScale = playScale;
notifyPropertyChanged(BR.playScale);
}
//省略parcelable代码
}
这里需要提一个view的局限性,就是只能改变x或者y的缩放,不能同时改变,所以这里做了一个双向绑定并写了一个adapter来设置同时更改X和Y的scale值:
public class CheckboxAttrAdapter {
@BindingAdapter("checkListener")
public static void setCheckBoxListener(CheckBox view, CompoundButton.OnCheckedChangeListener listener) {
view.setOnCheckedChangeListener(listener);
}
@BindingAdapter("android:button")
public static void setButton(CheckBox view, @DrawableRes int drawableId) {
view.setButtonDrawable(drawableId);
}
@BindingAdapter("recordScale")
public static void setRecordScale(CheckBox view, float scale) {
view.setScaleX(scale);
view.setScaleY(scale);
}
}
然后在xml文件中可以直接饮用属性:
<CheckBox
android:id="@+id/start"
android:layout_width