需求:
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()
}
}