Skip to content
This repository was archived by the owner on Aug 2, 2024. It is now read-only.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.samples.apps.sunflower

import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
Expand All @@ -26,13 +27,20 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ShareCompat
import androidx.core.view.ViewCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.google.android.material.snackbar.Snackbar
import com.google.samples.apps.sunflower.databinding.FragmentPlantDetailBinding
import com.google.samples.apps.sunflower.utilities.InjectorUtils
import com.google.samples.apps.sunflower.utilities.MoveViews
import com.google.samples.apps.sunflower.viewmodels.PlantDetailViewModel

/**
Expand Down Expand Up @@ -61,6 +69,9 @@ class PlantDetailFragment : Fragment() {
plantDetailViewModel.addPlantToGarden()
Snackbar.make(view, R.string.added_plant_to_garden, Snackbar.LENGTH_LONG).show()
}

ViewCompat.setTransitionName(detailImage, plantId)
requestListener = imageListener
}

plantDetailViewModel.plant.observe(this, Observer { plant ->
Expand All @@ -71,6 +82,11 @@ class PlantDetailFragment : Fragment() {
}
})

postponeEnterTransition() // wait for Glide callback to start transition
sharedElementEnterTransition = MoveViews().apply {
interpolator = FastOutSlowInInterpolator() // Material standard easing
}

setHasOptionsMenu(true)

return binding.root
Expand Down Expand Up @@ -105,4 +121,27 @@ class PlantDetailFragment : Fragment() {
else -> super.onOptionsItemSelected(item)
}
}

val imageListener = object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
startPostponedEnterTransition()
return false
}

override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
startPostponedEnterTransition()
return false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.doOnLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
Expand Down Expand Up @@ -50,6 +51,12 @@ class PlantListFragment : Fragment() {
binding.plantList.adapter = adapter
subscribeUi(adapter)

// wait RecyclerView to layout for detail to list image return animation
postponeEnterTransition()
binding.plantList.doOnLayout {
startPostponedEnterTransition()
}

setHasOptionsMenu(true)
return binding.root
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package com.google.samples.apps.sunflower.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.samples.apps.sunflower.PlantListFragment
Expand All @@ -35,8 +37,7 @@ class PlantAdapter : ListAdapter<Plant, PlantAdapter.ViewHolder>(PlantDiffCallba
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val plant = getItem(position)
holder.apply {
bind(createOnClickListener(plant.plantId), plant)
itemView.tag = plant
bind(createOnClickListener(), plant)
}
}

Expand All @@ -45,23 +46,32 @@ class PlantAdapter : ListAdapter<Plant, PlantAdapter.ViewHolder>(PlantDiffCallba
LayoutInflater.from(parent.context), parent, false))
}

private fun createOnClickListener(plantId: String): View.OnClickListener {
return View.OnClickListener {
val direction = PlantListFragmentDirections.ActionPlantListFragmentToPlantDetailFragment(plantId)
it.findNavController().navigate(direction)
private fun createOnClickListener(): OnPlantItemClickListener {
return object : OnPlantItemClickListener {
override fun onPlantItemClick(rootView: View, plant: Plant) {
val binding = DataBindingUtil.getBinding<ListItemPlantBinding>(rootView)
val navigatorExtras = FragmentNavigatorExtras(binding!!.plantItemImage to plant.plantId)
Comment thread
andhie marked this conversation as resolved.
Outdated

val direction = PlantListFragmentDirections.ActionPlantListFragmentToPlantDetailFragment(plant.plantId)
rootView.findNavController().navigate(direction, navigatorExtras)
}
}
}

class ViewHolder(
private val binding: ListItemPlantBinding
) : RecyclerView.ViewHolder(binding.root) {

fun bind(listener: View.OnClickListener, item: Plant) {
fun bind(listener: OnPlantItemClickListener, item: Plant) {
binding.apply {
clickListener = listener
plant = item
executePendingBindings()
}
}
}

interface OnPlantItemClickListener {
fun onPlantItemClick(rootView: View, plant: Plant)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.samples.apps.sunflower.adapters

import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder
import android.widget.ImageView
import android.widget.TextView
Expand All @@ -24,15 +25,17 @@ import androidx.core.text.italic
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestListener
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.samples.apps.sunflower.R

@BindingAdapter("imageFromUrl")
fun bindImageFromUrl(view: ImageView, imageUrl: String?) {
@BindingAdapter("imageFromUrl", "requestListener", requireAll = false)
fun bindImageFromUrl(view: ImageView, imageUrl: String?, listener: RequestListener<Drawable>?) {
if (!imageUrl.isNullOrEmpty()) {
Glide.with(view.context)
.load(imageUrl)
.transition(DrawableTransitionOptions.withCrossFade())
.listener(listener)
.into(view)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Comment thread
andhie marked this conversation as resolved.
Outdated
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.samples.apps.sunflower.utilities

import android.content.Context
import android.util.AttributeSet
import androidx.transition.ChangeBounds
import androidx.transition.ChangeImageTransform
import androidx.transition.ChangeTransform
import androidx.transition.TransitionSet

class MoveViews : TransitionSet {

constructor() {
init()
}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}

private fun init() {
addTransition(ChangeBounds())
.addTransition(ChangeTransform())
.addTransition(ChangeImageTransform())
}
}
7 changes: 6 additions & 1 deletion app/src/main/res/layout/fragment_plant_detail.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<variable
name="viewModel"
type="com.google.samples.apps.sunflower.viewmodels.PlantDetailViewModel" />

<variable
name="requestListener"
type="com.bumptech.glide.request.RequestListener&lt;android.graphics.drawable.Drawable>" />
</data>

<androidx.coordinatorlayout.widget.CoordinatorLayout
Expand Down Expand Up @@ -56,7 +60,8 @@
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
app:imageFromUrl="@{viewModel.plant.imageUrl}"
app:layout_collapseMode="parallax" />
app:layout_collapseMode="parallax"
app:requestListener="@{requestListener}" />

<androidx.appcompat.widget.Toolbar
android:id="@+id/detail_toolbar"
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/res/layout/list_item_plant.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<data>
<variable
name="clickListener"
type="android.view.View.OnClickListener"/>
type="com.google.samples.apps.sunflower.adapters.PlantAdapter.OnPlantItemClickListener"/>
<variable
name="plant"
type="com.google.samples.apps.sunflower.data.Plant"/>
Expand All @@ -30,7 +30,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{clickListener}">
android:onClick="@{v -> clickListener.onPlantItemClick(v, plant)}">

<ImageView
android:id="@+id/plant_item_image"
Expand All @@ -42,6 +42,7 @@
android:layout_marginStart="@dimen/margin_small"
android:contentDescription="@string/a11y_plant_item_image"
android:scaleType="centerCrop"
android:transitionName="@{plant.plantId}"
app:imageFromUrl="@{plant.imageUrl}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down
6 changes: 1 addition & 5 deletions app/src/main/res/navigation/nav_garden.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@

<action
android:id="@+id/action_plant_list_fragment_to_plant_detail_fragment"
app:destination="@id/plant_detail_fragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
app:destination="@id/plant_detail_fragment" />
</fragment>

<fragment
Expand Down