1.前言
需求如下:我们可以通过语音输入、文字输入等方式创建文章,需要使用多个悬浮按钮供用户进行笔记文章。
那么如何优雅实现多个悬浮按钮呢?我采取了多个悬浮按钮+属性动画的知识组合实现了该需求。
效果展示如下:
2.实现步骤
2.1 在xml设置三个重叠的悬浮按钮,按位置摆放好
这里我们准备从AS自带的矢量图的三张图片(按关键词查询即可),分别是
- drawable_add
- drawable_brush
- drawable_voice
然后在控制器MainActivity的xml文件下设置三个悬浮按钮,并通过src属性设置如上三张图片,代码如下:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_sayAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="#EC0A0A"
android:visibility="gone"
android:clickable="true"
android:src="@drawable/drawable_voice"
app:layout_constraintBottom_toBottomOf="@id/btn_add"
app:layout_constraintEnd_toEndOf="@id/btn_add"
app:layout_constraintStart_toStartOf="@id/btn_add"
app:layout_constraintTop_toTopOf="@id/btn_add" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_textAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="#EC0A0A"
android:visibility="gone"
android:clickable="true"
android:src="@drawable/drawable_brush"
app:layout_constraintBottom_toBottomOf="@id/btn_add"
app:layout_constraintEnd_toEndOf="@id/btn_add"
app:layout_constraintStart_toStartOf="@id/btn_add"
app:layout_constraintTop_toTopOf="@id/btn_add"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/drawable_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="30dp"/>
2.创建对应的动画集合(每个按钮都有一对集合。即开闭动画)
这里需要我们有属性动画知识基础,我只使用到了ObjectAnimator以及PropertyValuesHolder这个没有先后顺序的动画集合。
- 关于btn_add按钮的开闭动画
// id为btn_add的按钮关联
addBtn = findViewById(R.id.btn_add);
// 开动画
ObjectAnimator addBtnOpen = ObjectAnimator.ofFloat(addBtn,"rotation",45);
// 闭动画
ObjectAnimator addBtnClose = ObjectAnimator.ofFloat(addBtn,"rotation",0).setDuration(300);
- 关于brushBtn按钮的开闭动画:通过在Y轴上的平移traslationY,以控件对象中心为支点的缩放,在加上透明度有无切换形成一套收放相称的两个
ObjectAnimator对象——animatorBrushOpen 、animatorBrushClose.
brushBtn = findViewById(R.id.btn_brushAdd);
// 笔添加按钮的开动画集合
PropertyValuesHolder brushOpen1 = PropertyValuesHolder.ofFloat("translationY",-150);
PropertyValuesHolder brushOpen2 = PropertyValuesHolder.ofFloat("scaleX",1,0.8f);
PropertyValuesHolder brushOpen3 = PropertyValuesHolder.ofFloat("scaleY",1,0.8f);
PropertyValuesHolder brushOpen4 = PropertyValuesHolder.ofFloat("alpha",0,1);
ObjectAnimator animatorBrushOpen = ObjectAnimator.ofPropertyValuesHolder(brushBtn,brushOpen1,brushOpen2,brushOpen3,brushOpen4).setDuration(300);
// 笔添加按钮的闭动画集合
PropertyValuesHolder brushClose1 = PropertyValuesHolder.ofFloat("translationY",0);
PropertyValuesHolder brushClose2 = PropertyValuesHolder.ofFloat("scaleX",0.8f);
PropertyValuesHolder brushClose3 = PropertyValuesHolder.ofFloat("scaleY",0.8f);
PropertyValuesHolder brushClose4 = PropertyValuesHolder.ofFloat("alpha",1,0);
ObjectAnimator animatorBrushClose = ObjectAnimator.ofPropertyValuesHolder(brushBtn,brushClose1,brushClose2,brushClose3,brushClose4).setDuration(300);
- 关于voiceBtn按钮的开闭动画:同2,差距就是平移的距离不同,避免重叠
voiceBtn = findViewById(R.id.btn_voiceAdd);
// 语音添加按钮开动画集合
PropertyValuesHolder voiceOpen1 = PropertyValuesHolder.ofFloat("translationY",-300);
PropertyValuesHolder voiceOpen2 = PropertyValuesHolder.ofFloat("scaleX",1,0.8f);
PropertyValuesHolder voiceOpen3 = PropertyValuesHolder.ofFloat("scaleY",1,0.8f);
PropertyValuesHolder voiceOpen4 = PropertyValuesHolder.ofFloat("alpha",0,1);
animatorVoiceOpen = ObjectAnimator.ofPropertyValuesHolder(voiceBtn,voiceOpen1,voiceOpen2,voiceOpen3,voiceOpen4).setDuration(300);
// 语言添加按钮闭动画集合
PropertyValuesHolder voiceClose1 = PropertyValuesHolder.ofFloat("translationY",0);
PropertyValuesHolder voiceClose2 = PropertyValuesHolder.ofFloat("scaleX",0.8f);
PropertyValuesHolder voiceClose3 = PropertyValuesHolder.ofFloat("scaleY",0.8f);
PropertyValuesHolder voiceClose4 = PropertyValuesHolder.ofFloat("alpha",1,0);
animatorVoiceClose = ObjectAnimator.ofPropertyValuesHolder(voiceBtn,voiceClose1,voiceClose2,voiceClose3,voiceClose4).setDuration(300);
到这里,我们三个悬浮按钮的开闭动画便设置完成,这里我要特别强调一下,动画有多种实现方式,但是补间动画只能实现视觉效果上的移动,其控件的位置还是处在原来的地方,也就是监听事件等效果点击都只能在原来位置触发,当然并没有解决方法,只需要通过通过onAnimationEnd()在动画结束的时候重画控件的位置就可以,但是这样的话需要我们对每个控件都重画两次位置,太过麻烦,直接像现在采取属性动画一步到位。
3.监听实现
设置一个标识符clicked 显示btn_add是否被点击了,应该start开动画还是闭动画。
同时对移动的悬浮按钮可点击性进行改变。
private boolean clicked = false;// 监听btn_add是否点击,false后续start开动画,true后续start闭动画
// 监听事件实现
private void iniTouch() {
addBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onAddButtonClicked(clicked);
}
});
brushBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"输入添加",Toast.LENGTH_LONG).show();
}
});
voiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"语音添加",Toast.LENGTH_LONG).show();
}
});
}
private void onAddButtonClicked(boolean clicked) {
setAnimation(clicked);
setClickable(clicked);
this.clicked = !clicked;
}
// 是否可以点击,可以省去
private void setClickable(boolean clicked) {
if (clicked){
brushBtn.setClickable(false);
voiceBtn.setClickable(false);
}else {
brushBtn.setClickable(true);
voiceBtn.setClickable(true);
}
}
// 动画设置
private void setAnimation(boolean clicked) {
if (!clicked){
addBtnOpen.start();
animatorBrushOpen.start();
animatorVoiceOpen.start();
}else {
addBtnClose.start();
animatorBrushClose.start();
animatorVoiceClose.start();
}
}
总结
以上便是按需求实现的内容,当然其实有很多可用三方库已经实现了这个需求,主要是我上学期学习动画时很少动手敲代码,这段时间在复习的时候顺便实现了一下,等考完试有空也许会将以上内容自定义封装成控件,练习自定义View的部分知识。