按下按鈕後做擴大(擴散)動畫

需求
1、錄音按鈕,按住後,做擴散動畫,顯示:錄音中;
2、默認狀態,顯示:按住錄音;
3、鬆手後,控件做反向收縮動畫

UI規定
1、默認,按鈕有個背景色(純色),尺寸爲:116 * 40;
2、按住後控件擴散,擴散過程中,出現一條描邊,擴散到最遠處,且,背景色變成透明;
3、控件擴散後,最大尺寸爲124 * 44;
4、動效時長250毫秒;
5、鬆手後,控件做收縮動畫,效果與上面相反

效果圖示例(靜態圖):
在這裏插入圖片描述
在這裏插入圖片描述
分析:
1、尺寸分析:
擴散前後對比:124/116= 1.0689… ;44/40 = 1.1 。爲了方便實現,這裏定爲1.1。即,做動畫時,從原始尺寸,擴大1.1倍。116 * 1.1=127.6>124;124/1.1=112.7 ;113 * 1.1=124.3 最接近125
2、在設置佈局的時候,一開始,就設置爲最大尺寸,這樣,可以避免其他控件擠佔錄音控件的位置,或者,錄音控件擴大時,擠佔其他控件,引起界面其他控件的不必要重繪。
3、經過上面的計算,結論:(1)在誤差允許範圍內,默認尺寸爲:113 * 40,擴大比例爲1.1,控件最大區域:125 * 44

思路:
拆分3層:1、描邊;2、純色背景;3、文字和小圖片。
因爲動畫時間很短,且,擴大比例不大,純色背景,就只做透明度變化。沒有隨着描邊擴散。一般也看不出來,這裏“取巧”簡化了。

代碼不多,也簡單,就直接上源碼了
代碼:
用到的顏色

<color name="line_color">#55ff0000</color>
<color name="bg_color">#5500ff00</color>

<color name="color_trans">#00000000</color>
<color name="color_white">#ffffff</color>
<color name="color_black">#000000</color>

1、工具類


import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.GradientDrawable;

public class UiUtils {

    public static int dip2px(Context context, float dpValue) {
        final float scale = getResources(context).getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 獲得資源
     */
    public static Resources getResources(Context context) {
        return context.getResources();
    }

    /**
     * 獲取drawable 邊框的自定義方法,不用每次去新建drawable文件
     * @param solidColor       實體顏色
     * @param cornerRadiusInDp dp表示的邊角半徑
     * @param strokeWidthInDp  dp表示的邊線寬度
     * @param strokeColor      邊線顏色
     * @return GradientDrawable
     */
    public static GradientDrawable getGradientDrawable(Context context, int solidColor, int cornerRadiusInDp, float strokeWidthInDp, int strokeColor) {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setCornerRadius(UiUtils.dip2px(context,cornerRadiusInDp));
        drawable.setStroke(UiUtils.dip2px(context,strokeWidthInDp), strokeColor);
        drawable.setColor(solidColor);
        return drawable;
    }

    /**
     * 獲取drawable 邊框的自定義方法,適用於邊線顏色與實體顏色相同的,不用每次去新建drawable文件
     * @param solidColor       實體顏色
     * @param cornerRadiusInDp dp表示的邊角半徑
     * @return GradientDrawable
     */
    public static GradientDrawable getGradientDrawable(Context context, int solidColor, int cornerRadiusInDp) {
        return getGradientDrawable(context,solidColor, cornerRadiusInDp, 0, solidColor);
    }

}

2、佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#eeeeee"
    tools:context=".MainActivity">

    <!-- 44/40=1.1,113*1.1=124.3 -->
    <RelativeLayout
        android:background="@color/color_white"
        android:id="@+id/voice_rl"
        android:layout_width="125dp"
        android:layout_height="44dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp">

        <!--線的view-->
        <View
            android:id="@+id/line_view"
            android:layout_width="113dp"
            android:layout_height="40dp"
            android:layout_centerInParent="true"
            android:visibility="invisible" />

        <!--背景view-->
        <View
            android:id="@+id/bg_view"
            android:layout_width="113dp"
            android:layout_height="40dp"
            android:layout_centerInParent="true" />

        <LinearLayout
            android:id="@+id/str_ll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/iv"
                android:layout_width="15dp"
                android:layout_height="15dp"
                android:src="@mipmap/ic_default" />

            <TextView
                android:id="@+id/str_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="15dp"
                tools:text="錄音" />

        </LinearLayout>


    </RelativeLayout>

</LinearLayout>

3、功能代碼:

import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    //是否攔截動畫
    private var isInterceptAnim: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        iv.setImageResource(R.mipmap.ic_default)
        str_tv.setTextColor(getColor(R.color.color_white))
        str_tv.text = "按住錄音"

        line_view?.background = UiUtils.getGradientDrawable(
            this,
            ContextCompat.getColor(this, R.color.color_trans),
            50,
            1f,
            ContextCompat.getColor(this, R.color.line_color)
        )

        bg_view?.background = UiUtils.getGradientDrawable(
            this,
            ContextCompat.getColor(this, R.color.bg_color),
            50
        )

        voice_rl?.setOnTouchListener(object : View.OnTouchListener {
            override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {

                when (p1?.action) {

                    MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {

                        if (isInterceptAnim.not()) {
                            toAnim(true)
                        }
                        isInterceptAnim = true
                    }

                    MotionEvent.ACTION_UP -> {

                        isInterceptAnim = false

                        toAnim(false)
                    }

                }

                return true

            }

        })

    }


    private val maxScale = 1.1f
    private val minScale = 1f

    private var lineAnim: ValueAnimator? = null
    private var bgAnim: ObjectAnimator? = null

    private var duration: Long = 250L

    private fun toAnim(isDown: Boolean) {

        Log.e("lineAnim ", "isDown = $isDown")

        if (isDown) {
            lineAnim = ValueAnimator.ofFloat(minScale, maxScale)
            bgAnim = ObjectAnimator.ofFloat(
                bg_view,
                "alpha",
                1f, 0f
            )
        } else {
            lineAnim = ValueAnimator.ofFloat(maxScale, minScale)
            bgAnim = ObjectAnimator.ofFloat(
                bg_view,
                "alpha",
                0f, 1f
            )
        }

        lineAnim?.duration = duration
        bgAnim?.duration = duration

        lineAnim?.addUpdateListener {
            var scale = it.animatedValue as Float

            line_view.scaleX = scale
            line_view.scaleY = scale

        }

        bgAnim?.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animator: Animator) {
                bg_view.visibility = View.VISIBLE
                line_view.visibility = View.VISIBLE
                Log.e("bgAnim ", "onAnimationStart")
            }

            override fun onAnimationEnd(animator: Animator) {
                Log.e("bgAnim ", "onAnimationEnd ; isDown = $isDown")
                if (isDown) {

                    line_view.visibility = View.VISIBLE
                    bg_view.visibility = View.GONE

                    iv.setImageResource(R.mipmap.ic_down)
                    str_tv.setTextColor(getColor(R.color.color_black))
                    str_tv.text = "錄音中"


                } else {
                    bg_view.visibility = View.VISIBLE
                    line_view.visibility = View.GONE

                    iv.setImageResource(R.mipmap.ic_default)
                    str_tv.setTextColor(getColor(R.color.color_white))
                    str_tv.text = "按住錄音"
                }
            }

            override fun onAnimationCancel(animator: Animator) {

            }

            override fun onAnimationRepeat(animator: Animator) {

            }
        })

        lineAnim?.start()
        bgAnim?.start()

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章