Level 1 – Shared element từ RecyclerView sang 1 ImageView
- Share Element từ 1 item của RecyclerView sang 1 ImageView trong Activity
- 2 ImageView cùng load 1 ảnh
Define GalleryItem
class GalleryItem chứa thông tin cho 1 ảnh trong gallery, bao gồm: Id, link ảnh thumbnail và link ảnh full
public class GalleryItem implements Parcelable { private String mId; private String mThumbnailImg; private String mOriginalImg; // Other implementation... }
Id của GalleryItem sẽ được dùng làm transitionName cho ImageView tương ứng.
Khởi tạo RecyclerView tại Activity 1
mPhotoList.setHasFixedSize(false); mPhotoList.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false)); mPhotoList.setAdapter(new RecyclerViewGalleryAdapter());
Tạo ViewHolder chứa 1 ảnh
static class ViewHolder extends RecyclerView.ViewHolder { ImageView mPhotoImg; ViewHolder(View itemView) { super(itemView); mPhotoImg = itemView.findViewById(R.id.photo_img); } }
Khai báo RecyclerViewGalleryAdapter. Mỗi ảnh được gán transitionName bằng với Id của GalleryItem
class RecyclerViewGalleryAdapter extends RecyclerView.Adapter<ViewHolder> { @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(getActivity()).inflate(R.layout.layout_poster_list_item, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(final @NonNull ViewHolder holder, int position) { final GalleryItem galleryItem = mGalleryItems.get(position); // Set the transition name by gallery's id to each ImageView ViewCompat.setTransitionName(holder.mPhotoImg, String.valueOf(galleryItem.getId())); // Load the thumbnail image to the list, cache it into memory and data so the fullscreen thumbnail can be loaded faster Glide.with(activity) .load(Uri.parse(galleryItem.getThumbnailImg())) .apply(new RequestOptions().skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.DATA)) .into(holder.mPhotoImg); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Show fullscreen activity with single photo Intent intent = new Intent(activity, LevelOneFullPhotoActivity.class); intent.putExtra(LevelOneFullPhotoActivity.PHOTO_GALLERY_ITEM, galleryItem); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, holder.mPhotoImg, ViewCompat.getTransitionName(holder.mPhotoImg)); activity.startActivity(intent, options.toBundle()); activity.finish(); } }); } @Override public int getItemCount() { return mGalleryItems != null ? mGalleryItems.size() : 0; } }
Như đoạn code trên, mình dùng Glide để load ảnh kèm theo option cache ảnh vào memory để giảm thời gian giảm thời gian load ảnh ở Acitvity 2.
Hiển thị ảnh full ở Activity 2
Tại Activity 2, ta nhận GalleryItem được truyền tới và load ảnh:
Bundle data = getIntent().getExtras(); if (data != null) { mGallery = data.getParcelable(PHOTO_GALLERY_ITEM); // Postpone the Shared Element transition to wait until image is fully loaded supportPostponeEnterTransition(); // Set the transition name from the selected item ViewCompat.setTransitionName(mPhotoImg, String.valueOf(mGallery.getId())); } loadImg();
Trước khi bắt đầu gọi loadImg(), ta dùng hàm supportPostponeEnterTransition() để tạm dừng transition. Do ảnh sẽ bị delay 1 khoảng thời gian trước khi được load xong, để transition chạy lúc này sẽ gặp hiện tượng chớp, giật và transition sẽ không có tác dụng.
Sau khi load ảnh xong, gọi hàm supportStartPostponedEnterTransition để tiếp tục transition đang được tạm ngưng.
private void loadImg() { Glide.with(this) .load(Uri.parse(mGallery.getThumbnailImg())) .apply(new RequestOptions().dontAnimate().skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.DATA)) .listener(new RequestListener<Drawable>() { @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { // Continue the transition supportStartPostponedEnterTransition(); return false; } @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { // Continue the transition supportStartPostponedEnterTransition(); Toast.makeText(LevelOneFullPhotoActivity.this, "Load image fail", Toast.LENGTH_SHORT).show(); return false; } }) .into(mPhotoImg); }
Lưu ý:
- Nhớ thêm option dontAnimate vào Glide để các animation không overlap nhau
- Nếu đã gọi supportPostponeEnterTransition() nhưng không gọi supportStartPostponedEnterTransition() thì màn hình trắng trống trơn và stuck luôn ở đó, nên luuôn nhớ phải supportStartPostponedEnterTransition ngay cả khi xử lý thành công hay thất bại.
Kết quả Level 1:
Level này cũng không thực sự quá khó. Ta khởi tạo và load ảnh cho từng item trong RecyclerView cũng tương tự như Level 0.
Vấn đề xảy ra với Level này là ta cùng load 1 ảnh và khi ở trạng thái full screen sẽ dễ dàng bị vỡ. Đây là tiền đề tới Level 2
Phần 3: load 2 ảnh khác
Phần 3: Share element level 2nhau – Thumbnail ở Activity 1 và Full ở Activity 2
Full demo app