实现带动画的悬浮添加按钮

788 阅读3分钟

1.前言

需求如下:我们可以通过语音输入、文字输入等方式创建文章,需要使用多个悬浮按钮供用户进行笔记文章。 那么如何优雅实现多个悬浮按钮呢?我采取了多个悬浮按钮+属性动画的知识组合实现了该需求。 效果展示如下:

2.实现步骤

2.1 在xml设置三个重叠的悬浮按钮,按位置摆放好

这里我们准备从AS自带的矢量图的三张图片(按关键词查询即可),分别是

  1. drawable_add
  2. drawable_brush
  3. 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这个没有先后顺序的动画集合。

  1. 关于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);
  1. 关于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);
  1. 关于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的部分知识。