'Return animation of shared element not working
I have created a recyclerview in Fragment A and on click of a element it opens the second fragment, where the image from one oage to another should animate. The enter aniamtion is working fine, but the exit animation isn't working. The fragments are added with NavigationEditor.
This is the code :
RecyclerView fragment ->
public class SightSeeingFragment extends Fragment {
RecyclerView sightSeeingRv;
ArrayList<Place> placeArrayList;
SightSeeingAdapter sightSeeingAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
postponeEnterTransition();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sight_seeing, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
postponeEnterTransition();
init(view);
}
private void init(View view) {
sightSeeingRv = view.findViewById(R.id.sight_seeing_rv);
setRecyclerView();
}
private void setRecyclerView() {
new SetRecyclerView().execute();
}
class SetRecyclerView extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
sightSeeingRv.setLayoutManager(linearLayoutManager);
}
@Override
protected Void doInBackground(Void... voids) {
placeArrayList = new Gson().fromJson(placesToVisit,
new TypeToken<List<Place>>() {
}.getType());
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (sightSeeingAdapter == null)
sightSeeingAdapter = new SightSeeingAdapter(getContext(), placeArrayList);
sightSeeingRv.setAdapter(sightSeeingAdapter);
sightSeeingRv.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
startPostponedEnterTransition();
return true;
}
});
}
}
}
Adapter ->
public class SightSeeingAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Context context;
ArrayList<Place> placeArrayList;
public SightSeeingAdapter(Context context, ArrayList<Place> placeArrayList) {
this.context = context;
this.placeArrayList = placeArrayList;
if(this.placeArrayList == null)
placeArrayList = new ArrayList<>();
}
@Override
public int getItemViewType(int position) {
if(position == 0)
return 0;
else return 1;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(viewType == 0)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sightseeing_row_0, parent, false);
return new SightSeeingTextViewHolder(view);
}else
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_sight_seeing, parent, false);
return new SightSeeingViewHolder(view);
}}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(position!=0) {
((SightSeeingViewHolder)holder).bindView(position-1);
}
}
@Override
public int getItemCount() {
return placeArrayList.size()+1;
}
class SightSeeingTextViewHolder extends RecyclerView.ViewHolder {
public SightSeeingTextViewHolder(@NonNull View itemView) {
super(itemView);
}
}
class SightSeeingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView nameOfPlace;
TextView descOfPlace;
ImageView imageOfPlace;
CardView placeRow;
int position;
public SightSeeingViewHolder(@NonNull View itemView) {
super(itemView);
nameOfPlace = itemView.findViewById(R.id.name_of_place);
descOfPlace = itemView.findViewById(R.id.desc_of_place);
imageOfPlace = itemView.findViewById(R.id.image_of_place);
placeRow = itemView.findViewById(R.id.place_row);
}
public void bindView(int position) {
Place place = placeArrayList.get(position);
this.position = position;
nameOfPlace.setText(place.getNameOfPlace());
descOfPlace.setText(place.getShortDescription());
placeRow.setTag(position);
placeRow.setOnClickListener(this);
Glide.with(context).load(Utils.getDrawableImage(place.getImage(),context)).into(imageOfPlace);
ViewCompat.setTransitionName(imageOfPlace, position+"");
}
@Override
public void onClick(View view) {
int position = (int) view.getTag();
FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder()
.addSharedElement(imageOfPlace,position+"").build();
Navigation.findNavController(view)
.navigate(SightSeeingFragmentDirections.actionNavigationSightseeingToPlaceholder()
.setPlaceDetail(placeArrayList.get(position)).setTransitionName(position+""),extras);
}
}
}
Fragment B ->
public class DetailOfPlaceFragment extends Fragment implements View.OnClickListener {
Place place;
ImageView placeImage;
TextView placeDetails;
Button checkTheLocation;
String transitionName;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
postponeEnterTransition();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail_of_place, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
place = DetailOfPlaceFragmentArgs.fromBundle(getArguments()).getPlaceDetail();
transitionName = DetailOfPlaceFragmentArgs.fromBundle(getArguments()).getTransitionName();
init(view);
}
private void init(View view) {
placeImage = view.findViewById(R.id.place_image);
placeDetails = view.findViewById(R.id.place_details);
checkTheLocation = view.findViewById(R.id.check_the_location);
checkTheLocation.setOnClickListener(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
placeImage.setTransitionName(transitionName);
}
Glide.with(getContext()).load(Utils.getDrawableImage(place.getImage(),getContext())).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
startPostponedEnterTransition();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
startPostponedEnterTransition();
return false;
}
}).into(placeImage);
placeDetails.setText(place.getDescription());
}
@Override
public void onClick(View view) {
String url = String.format(getString(R.string.maps_url),place.getLatOnMap()+"",
place.getLondOnMap()+"",place.getNameOfPlace());
Utils.openActionIntent(url,getContext());
}
}
Also the enter animation isn't much fluid due to the postpone animation. But that is a secondary concern.
Solution 1:[1]
Your RecyclerView fragment needs to also use postponeEnterTransition() and startPostponedEnterTransition(): its view is going to be destroyed when it is put on the back stack and without postponing the transition, the shared element transition will attempt to happen before your AsyncTask actually completes that the RecyclerView is repopulated with data, leading to there being no View to return to.
The pull request that added shared element transitions to the GithubBrowserSample is a good place to start when looking for what all the moving pieces that need to be changed are.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ianhanniballake |
