Skip to content

Commit 4eda46b

Browse files
add shapeshifter heart and other avds
1 parent 7d3c8d9 commit 4eda46b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1936
-7
lines changed

Tutorial2-1AnimatedVectorDrawables/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
</intent-filter>
1818
</activity>
1919
<activity android:name=".Activity1_1Basics" />
20+
<activity android:name=".Activity1_2StateChange" />
2021
</application>
2122

2223
</manifest>

Tutorial2-1AnimatedVectorDrawables/src/main/java/com/smarttoolfactory/tutorial2_1animatedvectordrawables/Activity1_1Basics.kt

+13-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
package com.smarttoolfactory.tutorial2_1animatedvectordrawables
44

55
import android.os.Bundle
6-
import android.util.Log
76
import androidx.appcompat.app.AppCompatActivity
87
import androidx.recyclerview.widget.GridLayoutManager
98
import androidx.recyclerview.widget.RecyclerView
@@ -20,7 +19,7 @@ class Activity1_1Basics : AppCompatActivity() {
2019
override fun onCreate(savedInstanceState: Bundle?) {
2120
super.onCreate(savedInstanceState)
2221
setContentView(R.layout.activity1_1basics)
23-
title = "Ch1-1 AnimatedVectorDrawables"
22+
title = getString(R.string.activity1_1)
2423

2524
val dataList = getVectorDrawableItemList()
2625

@@ -125,6 +124,18 @@ class Activity1_1Basics : AppCompatActivity() {
125124
add(AVDModel(R.drawable.avd_checkable_expandcollapse_expanded_to_collapsed))
126125
add(AVDModel(R.drawable.avd_checkable_radiobutton_checked_to_unchecked))
127126
add(AVDModel(R.drawable.avd_checkable_radiobutton_unchecked_to_checked))
127+
128+
add(HeaderModel("Trims, clips & fills"))
129+
add(AVDModel(R.drawable.avd_heart_fill))
130+
add(AVDModel(R.drawable.avd_heart_unfill))
131+
add(AVDModel(R.drawable.avd_heart_break))
132+
add(AVDModel(R.drawable.avd_trimclip_airplane_disabled_to_enabled))
133+
add(AVDModel(R.drawable.avd_trimclip_airplane_enabled_to_disabled))
134+
add(AVDModel(R.drawable.avd_trimclip_searchback_back_to_search))
135+
add(AVDModel(R.drawable.avd_trimclip_searchback_search_to_back))
136+
add(AVDModel(R.drawable.avd_trimclip_flashlight_disabled_to_enabled))
137+
add(AVDModel(R.drawable.avd_trimclip_flashlight_enabled_to_disabled))
138+
128139
}
129140

130141
return data.toList()
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,106 @@
1+
@file:Suppress("UNCHECKED_CAST")
2+
13
package com.smarttoolfactory.tutorial2_1animatedvectordrawables
24

3-
class Activity1_2StateChange {
5+
import android.os.Bundle
6+
import android.widget.Toast
7+
import androidx.appcompat.app.AppCompatActivity
8+
import androidx.recyclerview.widget.GridLayoutManager
9+
import androidx.recyclerview.widget.RecyclerView
10+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.MultipleViewBinderListAdapter
11+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.itemdecoration.GridSpacingItemDecoration
12+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.viewholder.HeaderViewBinder
13+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.viewholder.ImageButtonViewBinder
14+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.viewholder.ItemClazz
15+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.viewholder.MappableItemBinder
16+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.model.HeaderModel
17+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.model.ImageButtonModel
18+
import kotlinx.android.synthetic.main.activity1_2state_change.*
19+
20+
class Activity1_2StateChange : AppCompatActivity() {
21+
22+
override fun onCreate(savedInstanceState: Bundle?) {
23+
super.onCreate(savedInstanceState)
24+
setContentView(R.layout.activity1_2state_change)
25+
title = getString(R.string.activity1_2)
26+
27+
val dataList = getVectorDrawableItemList()
28+
29+
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
30+
31+
val adapter = MultipleViewBinderListAdapter(
32+
createViewBinders(),
33+
RecyclerView.Adapter.StateRestorationPolicy.ALLOW
34+
).apply {
35+
submitList(dataList)
36+
}
37+
38+
recyclerView.apply {
39+
this.adapter = adapter
40+
addItemDecoration(
41+
GridSpacingItemDecoration(3, 30, dataList)
42+
)
43+
val gridLayoutManager = GridLayoutManager(this@Activity1_2StateChange, 6)
44+
45+
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
46+
47+
override fun getSpanSize(position: Int): Int {
48+
return if (dataList[position] is HeaderModel) {
49+
6
50+
} else {
51+
2
52+
}
53+
}
54+
55+
}
56+
recyclerView.layoutManager = gridLayoutManager
57+
}
58+
59+
imageButton.setOnClickListener {
60+
61+
imageButton.isSelected = !imageButton.isSelected
62+
Toast.makeText(
63+
applicationContext,
64+
"Button drawableState: ${imageButton.drawableState}, selected: ${imageButton.isSelected}",
65+
Toast.LENGTH_SHORT
66+
).show()
67+
}
68+
69+
var isChecked = false
70+
imageView.setOnClickListener {
71+
isChecked = !isChecked
72+
val stateSet = intArrayOf(android.R.attr.state_checked * if (isChecked) 1 else -1)
73+
imageView.setImageState(stateSet, true)
74+
}
75+
}
76+
77+
private fun createViewBinders(): HashMap<ItemClazz, MappableItemBinder> {
78+
79+
val headViewBinder = HeaderViewBinder()
80+
val imageButtonViewBinder = ImageButtonViewBinder()
81+
82+
return HashMap<ItemClazz, MappableItemBinder>()
83+
.apply {
84+
85+
put(
86+
imageButtonViewBinder.modelClazz,
87+
imageButtonViewBinder as MappableItemBinder
88+
)
89+
90+
put(
91+
headViewBinder.modelClazz,
92+
headViewBinder as MappableItemBinder
93+
)
94+
}
95+
}
96+
97+
private fun getVectorDrawableItemList(): List<Any> {
98+
99+
val data = mutableListOf<Any>().apply {
100+
add(HeaderModel("Animated State Change"))
101+
add(ImageButtonModel(R.drawable.asl_trimclip_heart))
102+
}
103+
104+
return data.toList()
105+
}
4106
}

Tutorial2-1AnimatedVectorDrawables/src/main/java/com/smarttoolfactory/tutorial2_1animatedvectordrawables/MainActivity.kt

+8-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ class MainActivity : AppCompatActivity(), BaseAdapter.OnRecyclerViewItemClickLis
5858
activityClassModels.add(
5959
ActivityClassModel(
6060
Activity1_1Basics::class.java,
61-
"Ch1-1 AnimatedVectorDrawables"
61+
getString(R.string.activity1_1)
62+
)
63+
)
64+
65+
activityClassModels.add(
66+
ActivityClassModel(
67+
Activity1_2StateChange::class.java,
68+
getString(R.string.activity1_2)
6269
)
6370
)
6471
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.smarttoolfactory.tutorial2_1animatedvectordrawables.adapter.viewholder
2+
3+
import android.view.ViewGroup
4+
import androidx.recyclerview.widget.RecyclerView
5+
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
6+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.R
7+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.databinding.ItemImageButtonBinding
8+
import com.smarttoolfactory.tutorial2_1animatedvectordrawables.model.ImageButtonModel
9+
10+
class ImageButtonViewBinder :
11+
MappableItemViewBinder<ImageButtonModel, ImageButtonViewHolder>(ImageButtonModel::class.java) {
12+
13+
override fun createViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
14+
return ImageButtonViewHolder(
15+
parent.inflate(getItemLayoutResource())
16+
)
17+
}
18+
19+
override fun bindViewHolder(model: ImageButtonModel, viewHolder: ImageButtonViewHolder) {
20+
viewHolder.bind(model)
21+
}
22+
23+
override fun getItemLayoutResource(): Int {
24+
return R.layout.item_image_button
25+
}
26+
27+
override fun areItemsTheSame(oldItem: ImageButtonModel, newItem: ImageButtonModel): Boolean {
28+
return oldItem.drawableRes == newItem.drawableRes
29+
}
30+
31+
override fun areContentsTheSame(oldItem: ImageButtonModel, newItem: ImageButtonModel): Boolean {
32+
return oldItem == newItem
33+
}
34+
}
35+
36+
class ImageButtonViewHolder(private val binding: ItemImageButtonBinding) :
37+
RecyclerView.ViewHolder(binding.root) {
38+
39+
fun bind(model: ImageButtonModel) {
40+
41+
val imageButton = binding.imageButton
42+
43+
val drawable = AnimatedVectorDrawableCompat.create(
44+
binding.root.context,
45+
model.drawableRes
46+
)
47+
48+
imageButton.setImageDrawable(drawable)
49+
}
50+
}

Tutorial2-1AnimatedVectorDrawables/src/main/java/com/smarttoolfactory/tutorial2_1animatedvectordrawables/model/AVDModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package com.smarttoolfactory.tutorial2_1animatedvectordrawables.model
33
import androidx.annotation.DrawableRes
44

55
data class AVDModel(@DrawableRes val drawableRes: Int)
6-
data class SeekableVDModel(@DrawableRes val drawableRes: Int)
6+
data class SeekableVDModel(@DrawableRes val drawableRes: Int)
7+
data class ImageButtonModel(@DrawableRes val drawableRes: Int)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<set xmlns:android="http://schemas.android.com/apk/res/android">
2+
3+
<!-- First reset state as the animation is delayed. -->
4+
<objectAnimator
5+
android:duration="0"
6+
android:propertyName="trimPathEnd"
7+
android:valueFrom="0"
8+
android:valueTo="0"/>
9+
10+
<objectAnimator
11+
android:duration="0"
12+
android:propertyName="strokeAlpha"
13+
android:valueFrom="0.4"
14+
android:valueTo="0.4"/>
15+
16+
<!-- Now trim-reveal & fade in the heart path. -->
17+
<objectAnimator
18+
android:duration="300"
19+
android:interpolator="@android:interpolator/fast_out_slow_in"
20+
android:propertyName="F"
21+
android:startOffset="400"
22+
android:valueFrom="0"
23+
android:valueTo="1"/>
24+
25+
<objectAnimator
26+
android:duration="300"
27+
android:interpolator="@android:interpolator/fast_out_slow_in"
28+
android:propertyName="strokeAlpha"
29+
android:startOffset="400"
30+
android:valueFrom="0.4"
31+
android:valueTo="1"/>
32+
33+
</set>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<set
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:ordering="sequentially">
4+
5+
<!-- First reset state. -->
6+
<objectAnimator
7+
android:duration="0"
8+
android:propertyName="fillAlpha"
9+
android:valueFrom="1"
10+
android:valueTo="1"/>
11+
12+
<objectAnimator
13+
android:duration="300"
14+
android:interpolator="@android:interpolator/linear_out_slow_in"
15+
android:propertyName="fillAlpha"
16+
android:startOffset="100"
17+
android:valueFrom="1"
18+
android:valueTo="0"/>
19+
20+
</set>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<objectAnimator
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:duration="@integer/searchback_show_hide_arrow_head_duration"
4+
android:interpolator="@android:interpolator/fast_out_slow_in"
5+
android:propertyName="trimPathEnd"
6+
android:valueFrom="@fraction/searchback_arrow_head_back_trim_end"
7+
android:valueTo="@fraction/searchback_arrow_head_search_trim_end"/>
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<set xmlns:android="http://schemas.android.com/apk/res/android">
2+
3+
<!-- First we need to immediately reset state. -->
4+
<objectAnimator
5+
android:duration="0"
6+
android:propertyName="trimPathEnd"
7+
android:valueFrom="@fraction/searchback_arrow_head_search_trim_end"
8+
android:valueTo="@fraction/searchback_arrow_head_search_trim_end"/>
9+
10+
<!-- Then run the animation after a delay. -->
11+
<objectAnimator
12+
android:duration="@integer/searchback_show_hide_arrow_head_duration"
13+
android:interpolator="@android:interpolator/fast_out_slow_in"
14+
android:propertyName="trimPathEnd"
15+
android:startOffset="@integer/searchback_show_arrow_head_delay"
16+
android:valueFrom="@fraction/searchback_arrow_head_search_trim_end"
17+
android:valueTo="@fraction/searchback_arrow_head_back_trim_end"/>
18+
19+
</set>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<objectAnimator
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:propertyName="pathData"
4+
android:repeatCount="-1"
5+
android:repeatMode="restart"
6+
android:valueFrom="M21 32 L35 32 L35 32 L21 32 Z"
7+
android:valueTo="M21 10 L35 10 L35 32 L21 32 Z"
8+
android:valueType="pathType"
9+
android:startOffset="300"
10+
android:duration="1200"
11+
android:interpolator="@android:interpolator/fast_out_slow_in" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<set
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:ordering="together">
4+
5+
<!-- reset state -->
6+
<objectAnimator
7+
android:propertyName="strokeAlpha"
8+
android:valueFrom="1"
9+
android:valueTo="1"
10+
android:duration="0" />
11+
<objectAnimator
12+
android:propertyName="trimPathStart"
13+
android:valueFrom="0"
14+
android:valueTo="0"
15+
android:duration="0" />
16+
<objectAnimator
17+
android:propertyName="trimPathEnd"
18+
android:valueFrom="0"
19+
android:valueTo="0"
20+
android:duration="0" />
21+
<objectAnimator
22+
android:propertyName="strokeWidth"
23+
android:valueFrom="0"
24+
android:valueTo="4"
25+
android:duration="0" />
26+
27+
<!-- decrease the trim end i.e trace out the circle & tick shapes -->
28+
<objectAnimator
29+
android:propertyName="trimPathEnd"
30+
android:valueFrom="0"
31+
android:valueTo="1"
32+
android:duration="1000"
33+
android:interpolator="@android:interpolator/linear_out_slow_in" />
34+
35+
<!-- animate the trim start to remove the circle & end up with just the tick -->
36+
<objectAnimator
37+
android:propertyName="trimPathStart"
38+
android:valueFrom="0"
39+
android:valueTo="0.89"
40+
android:startOffset="400"
41+
android:duration="600"
42+
android:interpolator="@android:interpolator/linear_out_slow_in" />
43+
44+
<!-- animate the stroke width (from matching progress spinner, to standard icon stroke) -->
45+
<objectAnimator
46+
android:propertyName="strokeWidth"
47+
android:valueFrom="4"
48+
android:valueTo="2"
49+
android:startOffset="800"
50+
android:duration="500"
51+
android:interpolator="@android:interpolator/linear_out_slow_in" />
52+
53+
<!-- hide the tick 800ms after the anim finishes (replaced by the tick->plus morph anim) -->
54+
<objectAnimator
55+
android:propertyName="strokeAlpha"
56+
android:valueFrom="1"
57+
android:valueTo="0"
58+
android:startOffset="1800"
59+
android:duration="0" />
60+
61+
</set>

0 commit comments

Comments
 (0)