Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 74bbfe3

Browse files
Update tutorial 2-6
1 parent a857221 commit 74bbfe3

File tree

10 files changed

+288
-97
lines changed

10 files changed

+288
-97
lines changed

‎README.md‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,11 @@ objects that are not shared in both fragments.
322322
of ***Transition*** start and end point to same value.
323323

324324
#### Note:
325-
🔥🔥🔥 In tutorial 2-4 and 2-5, having same background color for both fragments causing second fragment's **ENTER TRANSITION(CircularReveal and Slide.BOTTOM)** to not work.
326-
So when using **Transitions** that extend ```Visiblity``` class such as Slide, or Fade be careful about background color. This is only for **enterTransition** for destination and **reEnterTransition** for source fragments. **exitTransition** and **returnTransition** do not need visibility change.
325+
🔥🔥🔥 In tutorial 2-4 and 2-5, having same background color for both fragments causing second fragment's **ENTER TRANSITION(CircularReveal and Slide.BOTTOM)** to NOT work.
326+
So when using **Transitions** that extend ```Visiblity``` class such as Slide, or Fade be careful about background color. Having same background only messes **enterTransition** for destination and **returnTransition** for source fragments.
327327

328328
To prevent this use one of the solutions below:
329329

330-
331330
1- Set callback and set start and end properties for starting and ending scenes with
332331
```
333332
setEnterSharedElementCallback(object : SharedElementCallback() {
@@ -362,13 +361,20 @@ To prevent this use one of the solutions below:
362361

363362
2- Use **custom transitions** that extend either ```Transition``` or ```Visibility``` and force value changes.
364363

364+
3- Use a separate view for the background, rather than on the root view because it is a shared element. Otherwise it interferes with the window enter transition i.e. as it is resized for the shared element transition, many views are considered 'off-screen' so visibility transitions are not run.
365+
365366
* ⚠️ Transitions that extend ```Visibility``` such as ```Slide```, ```Fade```, or ```Explode``` depends on ***visibility*** of the view. If
366-
visibility is changed from ```View.INVISIBLE``` to ```View.VISIBLE``` ```onAppear``` method of ```Visibility``` class is called, if visibility changes
367-
```View.VISIBLE``` to ```View.INVISIBLE``` ```onDisappear``` method is called. With difference between visibility of starting and ending scenes, or manual visibility change it's possible to play transitions
368-
from backwards.
367+
visibility is changed from ```View.INVISIBLE``` to ```View.VISIBLE``` ```onAppear``` method of ```Visibility``` class is called, if visibility changes from
368+
```View.VISIBLE``` to ```View.INVISIBLE``` ```onDisappear``` method is called. With difference between visibility of starting and ending scenes, or manual visibility change it's possible to play transitions in reverse.
369+
370+
* ⚠️ With **EXIT** or **RETURN** transitions ```captureEndValues``` is not called, because of this use a transition that extends ```Visibility``` for ```exitTransition``` and ```returnTransition``` to start,
371+
and be aware that Animator from ```onDisAppear``` is called while current transition is exit or return.
372+
373+
#### Summary
374+
Enter, return transition are effected from background color, use transitions that extend ```Visibility``` and
375+
create visibility change from Invisible to Visible if enter or return transition **DO NOT START**.
369376

370-
* ⚠️ When current transition is **EXIT** or **RETURN** transition ```captureEndValues``` is not called, because of this use a transition that extends ```Visibility``` for ```exitTransition``` and ```returnTransition``` to start,
371-
and be aware that Animator from ```onDisAppear``` is called while current transition is exit or return.
377+
Exit, and reEnter transitions also require visibility change but from invisible to visible should be the correct order.
372378

373379
#### 🤩 Note: Breaker of chains — Transition Groups
374380

‎Tutorial3-1Transitions/src/main/java/com/smarttoolfactory/tutorial3_1transitions/MockDataCreator.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ object MockDataCreator {
6464
}
6565

6666

67-
println("🤩 MockDataCreator data: $data")
67+
println("🤩 MockDataCreator data: ${data.hashCode()}")
6868

6969

7070
return data

‎Tutorial3-1Transitions/src/main/java/com/smarttoolfactory/tutorial3_1transitions/adapter/TravelAdapter.kt‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,27 +78,27 @@ class TravelViewHolder(
7878
binding.tvDate.text = model.date
7979
binding.tvBody.text = model.body
8080

81+
binding.ivAvatar.transitionName = "${model.id}"
82+
8183
setUpHorizontalImageList(model)
8284

8385
isExpanded = expandedIds.contains(model.id)
8486

8587
val showExpandButton = model.images != null
8688

87-
println(
88-
"🍏 TravelViewHolder id: ${model.id}, " +
89-
"position: $bindingAdapterPosition, " +
90-
"isExpanded: $isExpanded, " +
91-
"viewHolder: ${this.hashCode()}, " +
92-
"model: ${model.hashCode()}"
93-
)
94-
89+
// println(
90+
// "🍏 TravelViewHolder id: ${model.id}, " +
91+
// "position: $bindingAdapterPosition, " +
92+
// "isExpanded: $isExpanded, " +
93+
// "viewHolder: ${this.hashCode()}, " +
94+
// "model: ${model.hashCode()}"
95+
// )
9596

96-
97-
if (model.images == null) {
98-
binding.ivExpand.visibility = View.INVISIBLE
97+
if (showExpandButton) {
98+
binding.ivExpand.visibility = View.VISIBLE
9999
binding.recyclerView.visibility = View.GONE
100100
} else {
101-
binding.ivExpand.visibility = View.VISIBLE
101+
binding.ivExpand.visibility = View.INVISIBLE
102102
binding.recyclerView.visibility = View.GONE
103103
}
104104

@@ -129,6 +129,7 @@ class TravelViewHolder(
129129
onItemClick?.invoke(binding, model)
130130
}
131131

132+
setUpExpandedStatus()
132133
binding.executePendingBindings()
133134
}
134135

Original file line numberDiff line numberDiff line change
@@ -1,24 +1,163 @@
11
package com.smarttoolfactory.tutorial3_1transitions.chapter2_fragment_transitions
22

33
import android.os.Bundle
4+
import android.view.Gravity
45
import android.view.LayoutInflater
56
import android.view.View
67
import android.view.ViewGroup
8+
import android.widget.ImageView
9+
import android.widget.TextView
10+
import androidx.core.view.doOnNextLayout
11+
import androidx.core.view.doOnPreDraw
712
import androidx.fragment.app.Fragment
13+
import androidx.transition.*
14+
import com.bumptech.glide.Glide
15+
import com.bumptech.glide.request.RequestOptions
816
import com.smarttoolfactory.tutorial3_1transitions.R
17+
import com.smarttoolfactory.tutorial3_1transitions.adapter.model.TravelModel
918

1019
class Fragment2_6ExpandCollapseDetails : Fragment() {
1120

21+
lateinit var travelModel: TravelModel
22+
23+
override fun onCreate(savedInstanceState: Bundle?) {
24+
super.onCreate(savedInstanceState)
25+
26+
val args = requireArguments()
27+
travelModel = Fragment2_6ExpandCollapseDetailsArgs.fromBundle(args).travelArgs
28+
29+
}
30+
1231
override fun onCreateView(
1332
inflater: LayoutInflater,
1433
container: ViewGroup?,
1534
savedInstanceState: Bundle?
1635
): View? {
17-
return inflater.inflate(R.layout.fragment2_6detail, container, false)
36+
37+
val view = inflater.inflate(R.layout.fragment2_6detail, container, false)
38+
bindViews(view)
39+
40+
prepareSharedElementTransition(view)
41+
postponeEnterTransition()
42+
43+
return view
1844
}
1945

46+
2047
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
2148
super.onViewCreated(view, savedInstanceState)
49+
50+
51+
view.doOnNextLayout {
52+
(it.parent as? ViewGroup)?.doOnPreDraw {
53+
startPostponedEnterTransition()
54+
}
55+
}
56+
}
57+
58+
private fun bindViews(view: View) {
59+
val ivAvatar = view.findViewById<ImageView>(R.id.ivAvatar)
60+
val tvTitle = view.findViewById<TextView>(R.id.tvTitle)
61+
val tvDate = view.findViewById<TextView>(R.id.tvDate)
62+
val tvBody = view.findViewById<TextView>(R.id.tvBody)
63+
64+
ivAvatar.transitionName = "${travelModel.id}"
65+
66+
val requestOptions = RequestOptions()
67+
requestOptions
68+
.placeholder(R.drawable.ic_baseline_account_circle_24)
69+
70+
Glide
71+
.with(view.context)
72+
.setDefaultRequestOptions(requestOptions)
73+
.load(travelModel.drawableRes)
74+
.circleCrop()
75+
.into(ivAvatar)
76+
77+
tvTitle.text = travelModel.title
78+
tvDate.text = travelModel.date
79+
tvBody.text = travelModel.body
80+
}
81+
82+
83+
private fun prepareSharedElementTransition(view: View) {
84+
85+
// allowEnterTransitionOverlap = true
86+
87+
setUpSharedElementTransition(view)
88+
89+
enterTransition = createEnterTransition(view)
90+
returnTransition = createReturnTransition(view)
2291
}
2392

93+
94+
95+
96+
97+
private fun setUpSharedElementTransition(view: View) {
98+
99+
sharedElementReturnTransition =
100+
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
101+
.apply {
102+
duration = 300
103+
}
104+
105+
// This is shared transition for imageView and title
106+
107+
val transitionSet = TransitionSet()
108+
val moveTransition =
109+
TransitionInflater.from(context).inflateTransition(android.R.transition.move)
110+
.apply {
111+
duration = 300
112+
}
113+
114+
val fade = Fade(Fade.MODE_IN)
115+
.apply {
116+
duration = 300
117+
}
118+
119+
transitionSet.addTransition(moveTransition)
120+
transitionSet.addTransition(fade)
121+
sharedElementEnterTransition = transitionSet
122+
}
123+
124+
private fun createEnterTransition(view: View): Transition {
125+
126+
val transitionEnter = TransitionSet()
127+
128+
val fade = Fade(Fade.MODE_IN)
129+
.apply {
130+
duration = 300
131+
}
132+
val slide = Slide(Gravity.BOTTOM)
133+
.apply {
134+
duration = 300
135+
}
136+
137+
transitionEnter.addTransition(fade)
138+
transitionEnter.addTransition(slide)
139+
return transitionEnter
140+
141+
}
142+
143+
private fun createReturnTransition(view: View): Transition {
144+
145+
val transitionReturn = TransitionSet()
146+
147+
val fade = Fade(Fade.MODE_OUT)
148+
.apply {
149+
duration = 300
150+
}
151+
val slide = Slide(Gravity.BOTTOM)
152+
.apply {
153+
duration = 300
154+
}
155+
156+
transitionReturn.addTransition(fade)
157+
transitionReturn.addTransition(slide)
158+
return transitionReturn
159+
}
160+
161+
162+
24163
}

‎Tutorial3-1Transitions/src/main/java/com/smarttoolfactory/tutorial3_1transitions/chapter2_fragment_transitions/Fragment2_6ExpandCollapseList.kt‎

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ import android.view.View
66
import android.view.ViewGroup
77
import androidx.core.view.doOnPreDraw
88
import androidx.fragment.app.Fragment
9+
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
910
import androidx.navigation.NavDirections
1011
import androidx.navigation.fragment.FragmentNavigatorExtras
1112
import androidx.navigation.fragment.findNavController
1213
import androidx.recyclerview.widget.DividerItemDecoration
1314
import androidx.recyclerview.widget.RecyclerView
15+
import androidx.transition.TransitionSet
1416
import com.smarttoolfactory.tutorial3_1transitions.MockDataCreator
1517
import com.smarttoolfactory.tutorial3_1transitions.R
1618
import com.smarttoolfactory.tutorial3_1transitions.adapter.TravelAdapter
1719
import com.smarttoolfactory.tutorial3_1transitions.adapter.model.TravelModel
1820
import com.smarttoolfactory.tutorial3_1transitions.databinding.ItemTravelBinding
21+
import com.smarttoolfactory.tutorial3_1transitions.transition.visibility.AlphaChange
22+
import com.smarttoolfactory.tutorial3_1transitions.transition.visibility.ScaleChange
1923

2024
@Suppress("UNCHECKED_CAST")
2125
class Fragment2_6ExpandCollapseList : Fragment() {
@@ -39,6 +43,65 @@ class Fragment2_6ExpandCollapseList : Fragment() {
3943

4044
private fun prepareTransitions(view: View) {
4145

46+
createExitTransition(view)
47+
48+
createReEnterTransition(view)
49+
}
50+
51+
private fun createExitTransition(view: View) {
52+
val recyclerView = view.findViewById<View>(R.id.recyclerView)
53+
54+
val transitionSetExit = TransitionSet()
55+
56+
val scaleX = .95f
57+
val scaleY = .95f
58+
59+
val scaleAnimation = ScaleChange(scaleX, scaleY, 1f, 1f)
60+
.apply {
61+
interpolator = LinearOutSlowInInterpolator()
62+
duration = 300
63+
addTarget(recyclerView)
64+
}
65+
66+
val fadeAnimation = AlphaChange(0.3f, 1f)
67+
.apply {
68+
interpolator = LinearOutSlowInInterpolator()
69+
duration = 300
70+
addTarget(recyclerView)
71+
}
72+
73+
transitionSetExit.addTransition(scaleAnimation)
74+
transitionSetExit.addTransition(fadeAnimation)
75+
76+
exitTransition = transitionSetExit
77+
}
78+
79+
private fun createReEnterTransition(view: View) {
80+
val recyclerView = view.findViewById<View>(R.id.recyclerView)
81+
82+
val transitionSeRetEnter = TransitionSet()
83+
84+
val scaleX = .9f
85+
val scaleY = .9f
86+
87+
val scaleAnimation = ScaleChange(scaleX, scaleY, 1f, 1f, forceVisibilityChange = true)
88+
.apply {
89+
interpolator = LinearOutSlowInInterpolator()
90+
duration = 300
91+
addTarget(recyclerView)
92+
}
93+
94+
val fadeAnimation = AlphaChange(0.3f, 1f, forceVisibilityChange = true)
95+
.apply {
96+
interpolator = LinearOutSlowInInterpolator()
97+
duration = 300
98+
addTarget(recyclerView)
99+
}
100+
101+
transitionSeRetEnter.addTransition(scaleAnimation)
102+
transitionSeRetEnter.addTransition(fadeAnimation)
103+
104+
reenterTransition = transitionSeRetEnter
42105
}
43106

44107
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -49,7 +112,7 @@ class Fragment2_6ExpandCollapseList : Fragment() {
49112

50113
val listAdapter =
51114
TravelAdapter(recycledViewPool) { binding: ItemTravelBinding, model: TravelModel ->
52-
// goToDetailPage(binding, model)
115+
goToDetailPage(binding, model)
53116
}
54117

55118

@@ -69,12 +132,12 @@ class Fragment2_6ExpandCollapseList : Fragment() {
69132

70133
private fun goToDetailPage(binding: ItemTravelBinding, model: TravelModel) {
71134

72-
val direction: NavDirections = Fragment2_6ExpandCollapseListDirections
73-
.actionFragment26ExpandCollapseListToFragment26ExpandCollapseDetails(model)
135+
val direction: NavDirections = Fragment2_6ExpandCollapseListDirections
136+
.actionFragment26ExpandCollapseListToFragment26ExpandCollapseDetails(model)
74137

75138
val extras = FragmentNavigatorExtras(
76139
binding.ivAvatar to binding.ivAvatar.transitionName,
77-
binding.tvTitle to binding.tvTitle.transitionName,
140+
// binding.tvTitle to binding.tvTitle.transitionName,
78141
)
79142

80143
findNavController().navigate(direction, extras)

‎Tutorial3-1Transitions/src/main/java/com/smarttoolfactory/tutorial3_1transitions/transition/visibility/ScaleChange.kt‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ import androidx.transition.Visibility
1010

1111
/**
1212
*
13-
* * If for fragment's **enterTransition** or **returnTransition** does not start use [forceVisibilityChange]
13+
* * If fragment's **enterTransition** or **returnTransition** do not start, use [forceVisibilityChange]
1414
* to create visibility difference between starting and ending scenes. Starting with **View.VISIBLE**
1515
* returns [Animator] from [Visibility.onDisappear].
1616
*
17-
* * Do not use ```forceVisibilityChange``` in fragment's ```exitTransition``` or ```returnTransition```
18-
* transitions, but you can set start visibility as **View.INVISIBLE** and end visibility as **View.VISIBLE**
19-
* to have correct ending scene. Beware that transition will run from reverse in an endings scene so
20-
* you might want to change start and end value(s)' ordering.
17+
* * Do NOT use ```forceVisibilityChange``` by itself in fragment's ```exitTransition``` or ```returnTransition```
18+
* transitions because default values for this transition is from invisible to visible but
19+
* for ```exiTransition```and ```returnTransition``` it should go from visible to invisible
20+
* You ou can set start visibility as **View.INVISIBLE** and end visibility as **View.VISIBLE**
21+
* to have correct ending scenes for exit, and return transitions.
22+
*
23+
* Beware that transition will run in reverse in an endings scene so
24+
* you might want to change start and end value(s)'.
2125
*/
2226
class ScaleChange(
2327
private var startScaleX: Float = 1f,

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /