more animations

This commit is contained in:
Phillip Thelen 2022-05-09 16:38:22 +02:00
parent 765204821a
commit efa21e28c0
15 changed files with 513 additions and 107 deletions

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/gray_700" />
</shape>

View file

@ -13,52 +13,85 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingTop="19dp">
<com.habitrpg.android.habitica.ui.views.CurrencyView
android:id="@+id/gold_view"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="@drawable/armoire_gold_background"
android:gravity="center"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:textStyle="bold"
android:textSize="20sp"
tools:text="118"/>
<FrameLayout
android:layout_width="165dp"
android:layout_height="158dp"
android:background="@drawable/armoire_circle"
android:layout_marginTop="23dp"
android:layout_marginBottom="23dp">
<ImageView
android:id="@+id/icon_view"
android:layout_width="136dp"
android:layout_height="136dp"
android:layout_gravity="center"/>
</FrameLayout>
<TextView
android:id="@+id/title_view"
android:gravity="center_horizontal">
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="+21 Experience"
android:textStyle="bold"
android:textSize="28sp"
android:textColor="@color/text_primary"/>
<TextView
android:id="@+id/subtitle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="You wrestle with the Armoire and gain Experience. Take that!"
android:layout_marginHorizontal="50dp"
android:gravity="center_horizontal"
android:layout_marginTop="8dp"
android:textSize="20sp"
/>
android:orientation="vertical"
android:gravity="center_horizontal">
<com.habitrpg.android.habitica.ui.views.CurrencyView
android:id="@+id/gold_view"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="@drawable/armoire_gold_background"
android:gravity="center"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:textStyle="bold"
android:textSize="20sp"
tools:text="118"/>
<RelativeLayout
android:id="@+id/icon_wrapper"
android:layout_width="165dp"
android:layout_height="158dp"
android:background="@drawable/circle_gray_700"
android:layout_marginTop="23dp"
android:layout_marginBottom="23dp"
android:visibility="invisible">
<ImageView
android:id="@+id/icon_view"
android:layout_width="136dp"
android:layout_height="136dp"
android:layout_centerInParent="true"/>
<com.habitrpg.android.habitica.ui.views.SparkView
android:id="@+id/left_spark_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
app:color="@color/brand_500"
app:thickness="6dp"
app:maxSpacing="8dp"
app:length="12dp"
app:duration="3500"
android:layout_marginTop="12dp"/>
<com.habitrpg.android.habitica.ui.views.SparkView
android:id="@+id/right_spark_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
app:color="@color/brand_500"
app:thickness="6dp"
app:maxSpacing="10dp"
app:duration="4000"
app:length="12dp"
android:layout_marginEnd="4dp"/>
</RelativeLayout>
<TextView
android:id="@+id/title_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="+21 Experience"
android:textStyle="bold"
android:textSize="28sp"
android:textColor="@color/text_primary"/>
<TextView
android:id="@+id/subtitle_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="You wrestle with the Armoire and gain Experience. Take that!"
android:layout_marginHorizontal="50dp"
android:gravity="center_horizontal"
android:layout_marginTop="8dp"
android:textSize="20sp"
/>
</LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
@ -69,7 +102,7 @@
android:background="@drawable/armoire_background"
android:orientation="vertical"
android:gravity="center"
android:paddingHorizontal="12dp"
android:paddingHorizontal="24dp"
android:paddingTop="28dp">
<TextView
android:id="@+id/equipment_count_view"
@ -121,7 +154,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/armoire_drop_rates"
android:textColor="@color/brand_500"
android:textColor="@color/brand_600"
android:layout_marginTop="22dp"
style="@style/Body2"/>
</LinearLayout>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:id="@+id/card_view"
android:layout_height="88dp"
android:layout_width="76dp"
android:background="@drawable/layout_rounded_bg_window_tint_border"
android:orientation="vertical"
android:layout_gravity="center">
<RelativeLayout
android:id="@+id/item_wrapper"
android:layout_width="86dp"
android:layout_height="68dp"
android:layout_gravity="center">
<ImageView
android:id="@+id/hatchingPotion_view"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true" />
<ImageView
android:id="@+id/egg_view"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
<ImageView
android:id="@+id/checkmark_view"
android:layout_width="12dp"
android:layout_height="10dp"
android:src="@drawable/pet_checkmark"
android:layout_gravity="center" />
</LinearLayout>
</FrameLayout>

View file

@ -17,6 +17,10 @@
android:contentDescription="@string/gems"
android:layout_marginTop="20dp"/>
<com.habitrpg.android.habitica.ui.views.SparkView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View file

@ -29,6 +29,7 @@
<attr name="headerTextColor" format="color" />
<attr name="widgetBackgroundRadius" format="dimension" />
<attr name="currency" format="string" />
<attr name="color" format="color" />
<declare-styleable name="AvatarView">
<attr name="showBackground" format="boolean" />
@ -101,7 +102,7 @@
</declare-styleable>
<declare-styleable name="CollapsibleSectionView">
<attr name="title" />
<attr name="color" format="color" />
<attr name="color" />
<attr name="identifier" />
<attr name="hasAdditionalInfo" format="boolean" />
<attr name="titleSize" format="dimension" />
@ -160,4 +161,11 @@
<attr name="text" />
<attr name="currency" />
</declare-styleable>
<declare-styleable name="SparkView">
<attr name="color" />
<attr name="thickness" format="dimension" />
<attr name="length" format="dimension" />
<attr name="maxSpacing" format="dimension" />
<attr name="duration" format="integer" />
</declare-styleable>
</resources>

View file

@ -0,0 +1,86 @@
package com.habitrpg.android.habitica.helpers
import android.view.View
import android.view.ViewAnimationUtils
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.Animation.INFINITE
import android.view.animation.Animation.RELATIVE_TO_SELF
import android.view.animation.Animation.REVERSE
import android.view.animation.AnimationSet
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import android.view.animation.TranslateAnimation
import androidx.core.animation.doOnEnd
import kotlin.random.Random
object Animations {
private fun randomFloat(min: Float, max: Float): Float {
return min + Random.nextFloat() * (max - min)
}
fun bobbingAnimation(amount: Float = 8f): Animation {
val anim = TranslateAnimation(0f, 0f, -amount, amount)
anim.duration = 2500
anim.interpolator = AccelerateDecelerateInterpolator()
anim.repeatCount = INFINITE
anim.repeatMode = REVERSE
return anim
}
fun negativeShakeAnimation(intensity: Float = 1f): Animation {
val anim = AnimationSet(true)
anim.interpolator = LinearInterpolator()
val translate = TranslateAnimation(randomFloat(-2f * intensity, 0f), randomFloat(0f, 2f * intensity), randomFloat(-1f * intensity, 0f), randomFloat(0f, 1f * intensity))
translate.duration = 70
translate.repeatCount = 5
translate.repeatMode = REVERSE
anim.addAnimation(translate)
val rotate = RotateAnimation(randomFloat(-0.4f * intensity, 0f), randomFloat(0f, 0.4f * intensity), RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f)
rotate.duration = 70
rotate.repeatCount = 5
rotate.repeatMode = REVERSE
anim.addAnimation(rotate)
return anim
}
fun circularReveal(view: View, duration: Long = 300) {
val cx = view.width / 2
val cy = view.height / 2
val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0f, finalRadius)
anim.duration = duration
view.visibility = View.VISIBLE
anim.start()
}
fun circularHide(view: View) {
val cx = view.width / 2
val cy = view.height / 2
val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, 0f)
anim.doOnEnd {
view.visibility = View.INVISIBLE
}
anim.start()
}
fun fadeInAnimation(duration: Long = 300): Animation {
val anim = AlphaAnimation(0f, 1f)
anim.interpolator = AccelerateDecelerateInterpolator()
anim.fillBefore = true
anim.fillAfter = true
anim.duration = duration
return anim
}
fun fadeOutAnimation(): Animation {
val anim = AlphaAnimation(1f, 0f)
anim.interpolator = AccelerateDecelerateInterpolator()
return anim
}
}

View file

@ -9,6 +9,7 @@ import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.databinding.AvatarWithBarsBinding
import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.helpers.HealthFormatter
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.Avatar
@ -96,6 +97,9 @@ class AvatarWithBarsViewModel(
if (valueMax != 0) {
cachedMaxHealth = valueMax
}
if (binding.hpBar.currentValue > value) {
binding.hpBar.progressBar.startAnimation(Animations.negativeShakeAnimation())
}
binding.hpBar.set(HealthFormatter.format(value.toDouble()), cachedMaxHealth.toDouble())
}

View file

@ -6,22 +6,26 @@ import android.view.Gravity
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.ActivityArmoireBinding
import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.observeOnce
import com.habitrpg.android.habitica.helpers.AdHandler
import com.habitrpg.android.habitica.helpers.AdType
import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.helpers.loadImage
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.ads.AdButton
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaBottomSheetDialog
import com.plattysoft.leonids.ParticleSystem
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
class ArmoireActivity: BaseActivity() {
@ -86,7 +90,7 @@ class ArmoireActivity: BaseActivity() {
}, RxErrorHandler.handleEmptyError()))
}
handler.prepare {
if (it && binding.adButton.state == AdButton.State.EMPTY) {
if (it && binding.adButton.state == AdButton.State.LOADING) {
binding.adButton.state = AdButton.State.READY
} else if (!it) {
binding.adButton.visibility = View.INVISIBLE
@ -141,6 +145,34 @@ class ArmoireActivity: BaseActivity() {
},
500
)
binding.iconView.startAnimation(Animations.bobbingAnimation())
binding.titleView.alpha = 0f
binding.subtitleView.alpha = 0f
lifecycleScope.launch {
delay(100)
if (binding.iconWrapper.isAttachedToWindow) {
Animations.circularReveal(binding.iconWrapper, 400)
}
binding.leftSparkView.startAnimating()
binding.rightSparkView.startAnimating()
}
binding.titleView.animate().apply {
alpha(1f)
duration = 400
startDelay = 600
start()
}
binding.subtitleView.animate().apply {
alpha(1f)
duration = 400
startDelay = 900
start()
}
hasAnimatedChanges = true
}
@ -171,8 +203,9 @@ class ArmoireActivity: BaseActivity() {
binding.iconView.loadImage("Pet_Food_$key")
}
else -> {
binding.subtitleView.text = getString(R.string.armoireExp, value)
binding.subtitleView.text = getString(R.string.armoireExp_new, value)
binding.iconView.setImageResource(R.drawable.armoire_experience)
binding.iconView.layoutParams = RelativeLayout.LayoutParams(108.dpToPx(this), 122.dpToPx(this))
}
}
}

View file

@ -1,6 +1,11 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
import android.view.View
import android.view.ViewGroup
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.CanHatchItemBinding
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.models.inventory.Animal
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.Food
@ -11,8 +16,10 @@ import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.OwnedMount
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.android.habitica.ui.views.dialogs.PetSuggestHatchDialog
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
@ -66,6 +73,7 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
when (viewType) {
1 -> SectionViewHolder(parent)
2 -> CanHatchViewHolder(parent, animalIngredientsRetriever)
else -> PetViewHolder(parent, equipEvents, feedEvents, animalIngredientsRetriever)
}
@ -78,23 +86,50 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
(holder as? SectionViewHolder)?.bind(obj)
}
is Pet -> {
(holder as? PetViewHolder)?.bind(
obj,
ownedPets?.get(obj.key ?: "")?.trained ?: 0,
eggCount(obj),
potionCount(obj),
canRaiseToMount(obj),
ownsSaddles,
ownedItems?.get(obj.animal + "-eggs") != null,
ownedItems?.get(obj.color + "-hatchingPotions") != null,
ownedMounts?.containsKey(obj.key) == true,
currentPet
)
val trained = ownedPets?.get(obj.key ?: "")?.trained ?: 0
val eggCount = eggCount(obj)
val potionCount = potionCount(obj)
if (trained <= 0 && eggCount > 0 && potionCount > 0) {
(holder as? CanHatchViewHolder)?.bind(
obj,
eggCount,
potionCount,
ownedItems?.get(obj.animal + "-eggs") != null,
ownedItems?.get(obj.color + "-hatchingPotions") != null,
ownedMounts?.containsKey(obj.key) == true,
)
} else {
(holder as? PetViewHolder)?.bind(
obj,
trained,
eggCount,
potionCount,
canRaiseToMount(obj),
ownsSaddles,
ownedItems?.get(obj.animal + "-eggs") != null,
ownedItems?.get(obj.color + "-hatchingPotions") != null,
ownedMounts?.containsKey(obj.key) == true,
currentPet
)
}
}
}
}
override fun getItemViewType(position: Int): Int = if (itemList.size > position && itemList[position] is StableSection) 1 else 2
override fun getItemViewType(position: Int): Int {
if (itemList.size <= position) return 3
return if (itemList[position] is StableSection) {
1
} else {
val pet = itemList[position] as Pet
if (ownedPets?.get(pet.key ?: "")?.trained ?: 0 <= 0 && eggCount(pet) > 0 && potionCount(pet) > 0) {
2
} else {
3
}
}
}
override fun getItemCount(): Int = itemList.size
@ -118,4 +153,68 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
ownsSaddles = if (ownedItems.containsKey("Saddle-food")) (ownedItems["Saddle-food"]?.numberOwned ?: 0) > 0 else false
notifyDataSetChanged()
}
class CanHatchViewHolder(
parent: ViewGroup,
private val ingredientsReceiver: ((Animal, ((Pair<Egg?, HatchingPotion?>) -> Unit)) -> Unit)?
) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.can_hatch_item)),
View.OnClickListener {
private var binding = CanHatchItemBinding.bind(itemView)
private var hasMount: Boolean = false
private var hasUnlockedPotion: Boolean = false
private var hasUnlockedEgg: Boolean = false
private var eggCount: Int = 0
private var potionCount: Int = 0
private var animal: Pet? = null
init {
itemView.setOnClickListener(this)
}
fun bind(
item: Pet,
eggCount: Int,
potionCount: Int,
hasUnlockedEgg: Boolean,
hasUnlockedPotion: Boolean,
hasMount: Boolean,
) {
this.animal = item
this.eggCount = eggCount
this.potionCount = potionCount
this.hasUnlockedEgg = hasUnlockedEgg
this.hasUnlockedPotion = hasUnlockedPotion
this.hasMount = hasMount
DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}")
DataBindingUtils.loadImage(
binding.hatchingPotionView,
"Pet_HatchingPotion_${item.color}"
)
binding.eggView.startAnimation(Animations.bobbingAnimation(4f))
binding.hatchingPotionView.startAnimation(Animations.bobbingAnimation(-4f))
}
override fun onClick(p0: View?) {
val context = itemView.context
val dialog = PetSuggestHatchDialog(context)
animal?.let {
ingredientsReceiver?.invoke(it) { ingredients ->
dialog.configure(
it,
ingredients.first,
ingredients.second,
eggCount,
potionCount,
hasUnlockedEgg,
hasUnlockedPotion,
hasMount
)
dialog.show()
}
}
}
}
}

View file

@ -89,17 +89,6 @@ class PetViewHolder(
} else {
binding.trainedProgressBar.visibility = View.GONE
binding.imageView.alpha = 0.2f
if (canHatch) {
binding.imageView.visibility = View.GONE
binding.itemWrapper.visibility = View.VISIBLE
binding.checkmarkView.visibility = View.VISIBLE
itemView.setBackgroundResource(R.drawable.layout_rounded_bg_window_tint_border)
DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}")
DataBindingUtils.loadImage(
binding.hatchingPotionView,
"Pet_HatchingPotion_${item.color}"
)
}
}
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {

View file

@ -45,8 +45,20 @@ class HabiticaProgressBar(context: Context, attrs: AttributeSet?) : FrameLayout(
}
}
private var currentValue: Double = 0.0
private var maxValue: Double = 0.0
var currentValue: Double = 0.0
set(value) {
if (field != value) {
field = value
updateBar()
}
}
var maxValue: Double = 0.0
set(value) {
if (field != value) {
field = value
updateBar()
}
}
var pendingValue: Double = 0.0
set(value) {
@ -97,16 +109,6 @@ class HabiticaProgressBar(context: Context, attrs: AttributeSet?) : FrameLayout(
updateBar()
}
fun setCurrentValue(value: Double) {
currentValue = value
updateBar()
}
fun setMaxValue(value: Double) {
maxValue = value
updateBar()
}
private fun setLayoutWeight(view: View, weight: Double) {
view.clearAnimation()
val layout = view.layoutParams as? LinearLayout.LayoutParams

View file

@ -0,0 +1,104 @@
package com.habitrpg.android.habitica.ui.views
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.Animation
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.dpToPx
import kotlin.math.min
class SparkView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var spacing: Float = 0f
set(value) {
field = value
invalidate()
}
private var paint: Paint = Paint()
var thickness = 3.dpToPx(context)
var length = 6.dpToPx(context)
var maxSpacing = 5.dpToPx(context)
var animationDuration = 2500L
var color: Int
get() {
return paint.color
}
set(value) {
paint.color = value
}
init {
spacing = maxSpacing.toFloat()
context.theme?.obtainStyledAttributes(attrs, R.styleable.SparkView, 0, 0)?.let {
thickness = it.getDimensionPixelSize(R.styleable.SparkView_thickness, 3.dpToPx(context))
length = it.getDimensionPixelSize(R.styleable.SparkView_length, 6.dpToPx(context))
maxSpacing = it.getDimensionPixelSize(R.styleable.SparkView_maxSpacing, 5.dpToPx(context))
animationDuration = it.getInt(R.styleable.SparkView_duration, 2500).toLong()
color = it.getInt(R.styleable.SparkView_color, ContextCompat.getColor(context, R.color.white))
}
paint.style = Paint.Style.FILL
}
fun startAnimating() {
val anim = ObjectAnimator.ofFloat(thickness.toFloat(), maxSpacing.toFloat())
anim.addUpdateListener {
spacing = it.animatedValue as Float
}
anim.interpolator = AccelerateDecelerateInterpolator()
anim.repeatCount = Animation.INFINITE
anim.repeatMode = ValueAnimator.REVERSE
anim.duration = animationDuration
anim.start()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val desiredSize = (length * 2 + maxSpacing)
val width = when (MeasureSpec.getMode(widthMeasureSpec)) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> min(desiredSize, widthSize)
else -> desiredSize
}
val height = when (MeasureSpec.getMode(heightMeasureSpec)) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> min(desiredSize, heightSize)
else -> desiredSize
}
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val thisCanvas = canvas ?: return
val centerHorizontal = width / 2f
val centerVertical = height / 2f
val offset = (maxSpacing - spacing)/2
drawHorizontal(thisCanvas, offset, centerVertical)
drawHorizontal(thisCanvas, width - length.toFloat() - offset, centerVertical)
drawVertical(thisCanvas, centerHorizontal, offset)
drawVertical(thisCanvas, centerVertical, height - length.toFloat() - offset)
}
private fun drawVertical(canvas: Canvas, x: Float, y: Float) {
canvas.drawRoundRect(x-(thickness/2), y, x+(thickness/2), y+length, thickness/2f, thickness/2f, paint)
}
private fun drawHorizontal(canvas: Canvas, x: Float, y: Float) {
canvas.drawRoundRect(x, y-(thickness/2), x+length, y+(thickness/2), thickness/2f, thickness/2f, paint)
}
}

View file

@ -28,8 +28,19 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
private var binding: ValueBarBinding = ValueBarBinding.inflate(context.layoutInflater, this, true)
private val formatter = NumberFormat.getInstance()
private var currentValue: Double = 0.0
private var maxValue: Double = 0.0
val progressBar: HabiticaProgressBar
get() = binding.progressBar
var currentValue: Double
get() = binding.progressBar.currentValue
set(value) {
binding.progressBar.currentValue = value
}
var maxValue: Double
get() = binding.progressBar.maxValue
set(value) {
binding.progressBar.maxValue = value
}
var barForegroundColor: Int
get() = binding.progressBar.barForegroundColor
@ -169,7 +180,7 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
var animationDelay = 0L
fun set(value: Double, valueMax: Double) {
if (currentValue != value || maxValue != valueMax) {
if (binding.progressBar.currentValue != value || maxValue != valueMax) {
if (animationDuration == 0L || binding.valueTextView.text.isEmpty()) {
currentValue = value
} else {

View file

@ -25,7 +25,7 @@ class AdButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
var state: State = State.EMPTY
var state: State = State.LOADING
set(value) {
field = value
updateViews()
@ -61,7 +61,7 @@ class AdButton @JvmOverloads constructor(
binding.currencyView.setTextColor(ContextCompat.getColor(context, R.color.white))
binding.currencyView.value = 0.0
gravity = Gravity.CENTER
state = State.EMPTY
state = State.LOADING
}
private fun updateViews() {

View file

@ -36,30 +36,22 @@ class QuestMenuView : LinearLayout {
binding.rageIconView.setImageBitmap(HabiticaIconsHelper.imageOfRage())
binding.pendingDamageIconView.setImageBitmap(HabiticaIconsHelper.imageOfDamage())
/*binding.closeButton.setOnClickListener {
hideBossArt()
val preferences = context.getSharedPreferences("collapsible_sections", 0)
preferences?.edit {
putBoolean("boss_art_collapsed", true)
}
}*/
}
fun configure(quest: Quest) {
binding.healthBarView.setCurrentValue(quest.progress?.hp ?: 0.0)
binding.rageBarView.setCurrentValue(quest.progress?.rage ?: 0.0)
binding.healthBarView.currentValue = quest.progress?.hp ?: 0.0
binding.rageBarView.currentValue = quest.progress?.rage ?: 0.0
}
fun configure(questContent: QuestContent) {
this.questContent = questContent
binding.healthBarView.setMaxValue(questContent.boss?.hp?.toDouble() ?: 0.0)
binding.healthBarView.maxValue = questContent.boss?.hp?.toDouble() ?: 0.0
binding.bossNameView.text = questContent.boss?.name
binding.typeTextView.text = context.getString(R.string.boss_quest)
if (questContent.boss?.hasRage == true) {
binding.rageView.visibility = View.VISIBLE
binding.rageBarView.setMaxValue(questContent.boss?.rage?.value ?: 0.0)
binding.rageBarView.maxValue = questContent.boss?.rage?.value ?: 0.0
} else {
binding.rageView.visibility = View.GONE
}
@ -74,9 +66,7 @@ class QuestMenuView : LinearLayout {
binding.topView.setBackgroundColor(questContent?.colors?.mediumColor ?: 0)
binding.bossNameView.gravity = Gravity.START
binding.bossNameView.layoutParams = LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1F)
// binding.bossArtView.visibility = View.GONE
binding.typeTextView.setTextColor(questContent?.colors?.extraLightColor ?: 0)
// binding.closeButton.visibility = View.GONE
}
fun showBossArt() {
@ -84,8 +74,6 @@ class QuestMenuView : LinearLayout {
binding.topView.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
binding.bossNameView.gravity = Gravity.END
binding.bossNameView.layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
// binding.bossArtView.visibility = View.VISIBLE
binding.typeTextView.setTextColor(ContextCompat.getColor(context, R.color.white))
// binding.closeButton.visibility = View.VISIBLE
}
}