@@ -11,6 +11,8 @@ import android.view.MotionEvent
1111import android.view.View
1212import android.view.animation.AccelerateDecelerateInterpolator
1313import android.view.animation.DecelerateInterpolator
14+ import kotlin.math.max
15+ import kotlin.math.min
1416
1517enum class AnimationType {
1618 SUCCESSFUL ,
@@ -37,8 +39,13 @@ class LoadingButton @JvmOverloads constructor(
3739
3840 private const val DEFAULT_COLOR = Color .BLUE
3941 private const val DEFAULT_TEXT_COLOR = Color .WHITE
42+
4043 }
4144
45+ private val mDensity = resources.displayMetrics.density
46+
47+ private val defaultMinHeight = 48 * mDensity
48+
4249 var animationEndAction: ((AnimationType ) -> Unit )? = null
4350
4451 var rippleEnable = true
@@ -113,8 +120,9 @@ class LoadingButton @JvmOverloads constructor(
113120 invalidate()
114121 }
115122
116- private val mDensity = resources.displayMetrics.density
123+
117124 private var mCurrentState = STATE_BUTTON
125+ private var mMinHeight = defaultMinHeight
118126
119127 private var mColorPrimary = DEFAULT_COLOR
120128 private var mDisabledBgColor = Color .LTGRAY
@@ -178,11 +186,12 @@ class LoadingButton @JvmOverloads constructor(
178186 rippleEnable = ta.getBoolean(R .styleable.LoadingButton_lb_rippleEnable , true )
179187 mRippleAlpha = ta.getFloat(R .styleable.LoadingButton_lb_btnRippleAlpha , 0.3f )
180188 mButtonCorner = ta.getFloat(R .styleable.LoadingButton_lb_cornerRadius , 2 * mDensity)
189+ mMinHeight = ta.getDimension(R .styleable.LoadingButton_lb_min_height , defaultMinHeight)
181190 ta.recycle()
182191 }
183192
184193 mPaint.apply {
185- setLayerType(View . LAYER_TYPE_SOFTWARE , this )
194+ setLayerType(LAYER_TYPE_SOFTWARE , this )
186195 isAntiAlias = true
187196 color = mColorPrimary
188197 style = Paint .Style .FILL
@@ -237,15 +246,14 @@ class LoadingButton @JvmOverloads constructor(
237246
238247 override fun onSizeChanged (w : Int , h : Int , oldw : Int , oldh : Int ) {
239248 super .onSizeChanged(w, h, oldw, oldh)
240- mRadius = (height - mPadding * 2 ) .toInt() / 2
241-
249+ val viewHeight = max(h, mMinHeight .toInt())
250+ mRadius = (viewHeight - mPadding * 2 ).toInt() / 2
242251 mButtonRectF.top = mPadding
243- mButtonRectF.bottom = height - mPadding
244-
252+ mButtonRectF.bottom = viewHeight - mPadding
245253 mArcRectF.left = (width / 2 - mRadius).toFloat()
246254 mArcRectF.top = mPadding
247255 mArcRectF.right = (width / 2 + mRadius).toFloat()
248- mArcRectF.bottom = height - mPadding
256+ mArcRectF.bottom = viewHeight - mPadding
249257 }
250258
251259 override fun setEnabled (enabled : Boolean ) {
@@ -282,29 +290,30 @@ class LoadingButton @JvmOverloads constructor(
282290
283291 override fun onDraw (canvas : Canvas ) {
284292 super .onDraw(canvas)
293+ val viewHeight = max(height, mMinHeight.toInt())
285294 when (mCurrentState) {
286295 STATE_BUTTON , STATE_ANIMATION_STEP1 -> {
287- val cornerRadius = (mRadius - mButtonCorner) * (mScaleWidth / (width / 2 - height / 2 ).toFloat()) + mButtonCorner
296+ val cornerRadius = (mRadius - mButtonCorner) * (mScaleWidth / (width / 2 - viewHeight / 2 ).toFloat()) + mButtonCorner
288297 mButtonRectF.left = mScaleWidth.toFloat()
289298 mButtonRectF.right = (width - mScaleWidth).toFloat()
290299 canvas.drawRoundRect(mButtonRectF, cornerRadius, cornerRadius, mPaint)
291300 if (mCurrentState == STATE_BUTTON ) {
292- canvas.drawText(mText, (width - mTextWidth) / 2 , (height - mTextHeight) / 2 + mPadding * 2 , mTextPaint)
301+ canvas.drawText(mText, (width - mTextWidth) / 2 , (viewHeight - mTextHeight) / 2 + mPadding * 2 , mTextPaint)
293302 if ((mTouchX > 0 || mTouchY > 0 ) && rippleEnable) {
294- canvas.clipRect(0f , mPadding, width.toFloat(), height - mPadding)
303+ canvas.clipRect(0f , mPadding, width.toFloat(), viewHeight - mPadding)
295304 canvas.drawCircle(mTouchX, mTouchY, mRippleRadius, ripplePaint)
296305 }
297306 }
298307 }
299308 STATE_ANIMATION_STEP2 -> {
300- canvas.drawCircle((width / 2 ).toFloat(), (height / 2 ).toFloat(), (mRadius - mScaleHeight).toFloat(), mPaint)
301- canvas.drawCircle((width / 2 ).toFloat(), (height / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
309+ canvas.drawCircle((width / 2 ).toFloat(), (viewHeight / 2 ).toFloat(), (mRadius - mScaleHeight).toFloat(), mPaint)
310+ canvas.drawCircle((width / 2 ).toFloat(), (viewHeight / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
302311 }
303312 STATE_ANIMATION_LOADING -> {
304313 mPath.reset()
305314 mPath.addArc(mArcRectF, (270 + mAngle / 2 ).toFloat(), (360 - mAngle).toFloat())
306315 if (mAngle != 0 ) {
307- mMatrix.setRotate(mDegree.toFloat(), (width / 2 ).toFloat(), (height / 2 ).toFloat())
316+ mMatrix.setRotate(mDegree.toFloat(), (width / 2 ).toFloat(), (viewHeight / 2 ).toFloat())
308317 mPath.transform(mMatrix)
309318 mDegree + = 10
310319 }
@@ -314,20 +323,20 @@ class LoadingButton @JvmOverloads constructor(
314323 mPath.reset()
315324 mPath.addArc(mArcRectF, (270 + mAngle / 2 ).toFloat(), mEndAngle.toFloat())
316325 if (mEndAngle != 360 ) {
317- mMatrix.setRotate(mDegree.toFloat(), (width / 2 ).toFloat(), (height / 2 ).toFloat())
326+ mMatrix.setRotate(mDegree.toFloat(), (width / 2 ).toFloat(), (viewHeight / 2 ).toFloat())
318327 mPath.transform(mMatrix)
319328 mDegree + = 10
320329 }
321330 canvas.drawPath(mPath, mStrokePaint)
322331 }
323332 STATE_ANIMATION_SUCCESS -> {
324333 canvas.drawPath(mSuccessPath!! , mPathEffectPaint)
325- canvas.drawCircle((width / 2 ).toFloat(), (height / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
334+ canvas.drawCircle((width / 2 ).toFloat(), (viewHeight / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
326335 }
327336 STATE_ANIMATION_FAILED -> {
328337 canvas.drawPath(mFailedPath!! , mPathEffectPaint)
329338 canvas.drawPath(mFailedPath2!! , mPathEffectPaint2)
330- canvas.drawCircle((width / 2 ).toFloat(), (height / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
339+ canvas.drawCircle((width / 2 ).toFloat(), (viewHeight / 2 ).toFloat(), mRadius - mDensity, mStrokePaint)
331340 }
332341 }
333342 }
@@ -446,10 +455,10 @@ class LoadingButton @JvmOverloads constructor(
446455 }
447456
448457 private fun measureDimension (defaultSize : Int , measureSpec : Int ) =
449- when (View . MeasureSpec .getMode(measureSpec)) {
450- View . MeasureSpec .EXACTLY -> View . MeasureSpec .getSize(measureSpec)
451- View . MeasureSpec .AT_MOST -> Math . min(defaultSize, View . MeasureSpec .getSize(measureSpec))
452- View . MeasureSpec .UNSPECIFIED -> defaultSize
458+ when (MeasureSpec .getMode(measureSpec)) {
459+ MeasureSpec .EXACTLY -> MeasureSpec .getSize(measureSpec)
460+ MeasureSpec .AT_MOST -> min(defaultSize, MeasureSpec .getSize(measureSpec))
461+ MeasureSpec .UNSPECIFIED -> defaultSize
453462 else -> defaultSize
454463 }
455464
@@ -486,10 +495,10 @@ class LoadingButton @JvmOverloads constructor(
486495 }
487496
488497 private fun playStartAnimation (isReverse : Boolean ) {
489-
498+ val viewHeight = max(height, mMinHeight.toInt())
490499 val animator = ValueAnimator .ofInt(
491- if (isReverse) width / 2 - height / 2 else 0 ,
492- if (isReverse) 0 else width / 2 - height / 2 )
500+ if (isReverse) width / 2 - viewHeight / 2 else 0 ,
501+ if (isReverse) 0 else width / 2 - viewHeight / 2 )
493502 .apply {
494503 duration = 400
495504 interpolator = AccelerateDecelerateInterpolator ()
@@ -647,13 +656,14 @@ class LoadingButton @JvmOverloads constructor(
647656
648657 private fun scaleSuccessPath () {
649658 val scaleMatrix = Matrix ()
659+ val viewHeight = max(height, mMinHeight.toInt())
650660 ValueAnimator .ofFloat(1.0f , 0.0f )
651661 .apply {
652662 duration = 300
653663 interpolator = AccelerateDecelerateInterpolator ()
654664 addUpdateListener { valueAnimator ->
655665 val value = valueAnimator.animatedValue as Float
656- scaleMatrix.setScale(value, value, (width / 2 ).toFloat(), (height / 2 ).toFloat())
666+ scaleMatrix.setScale(value, value, (width / 2 ).toFloat(), (viewHeight / 2 ).toFloat())
657667 mSuccessPath!! .transform(scaleMatrix)
658668 invalidate()
659669 }
@@ -666,13 +676,14 @@ class LoadingButton @JvmOverloads constructor(
666676
667677 private fun scaleFailedPath () {
668678 val scaleMatrix = Matrix ()
679+ val viewHeight = max(height, mMinHeight.toInt())
669680 ValueAnimator .ofFloat(1.0f , 0.0f )
670681 .apply {
671682 duration = 300
672683 interpolator = AccelerateDecelerateInterpolator ()
673684 addUpdateListener { valueAnimator ->
674685 val value = valueAnimator.animatedValue as Float
675- scaleMatrix.setScale(value, value, (width / 2 ).toFloat(), (height / 2 ).toFloat())
686+ scaleMatrix.setScale(value, value, (width / 2 ).toFloat(), (viewHeight / 2 ).toFloat())
676687 mFailedPath!! .transform(scaleMatrix)
677688 mFailedPath2!! .transform(scaleMatrix)
678689 invalidate()
0 commit comments