mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-24 14:45:46 +00:00
more animations
This commit is contained in:
parent
765204821a
commit
efa21e28c0
15 changed files with 513 additions and 107 deletions
4
Habitica/res/drawable/circle_gray_700.xml
Normal file
4
Habitica/res/drawable/circle_gray_700.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
41
Habitica/res/layout/can_hatch_item.xml
Normal file
41
Habitica/res/layout/can_hatch_item.xml
Normal 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>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue