CenterCrop image with slow zoom out

Viewed 301

I have created a horizontal recyclerview that shows images taken by a user. These images are center cropped and are displayed using Glide. How can I achieve a slow zoom out while the user is viewing the displayed image.

Please refer to this as an example: https://dribbble.com/shots/8582397-Nomad-iOS-UI-Kit-Favorite-Places

UPDATE:

public class HorizontalGalleryRecyclerViewAdapter extends RecyclerView.Adapter < HorizontalGalleryRecyclerViewAdapter.MyViewHolder > {

  private Context mContext;
  private ArrayList < ImageItems > arrayList;
  public long Last_position;

  private HorizontalGalleryRecyclerViewAdapter.onItemCLickListener mListener;

  public interface onItemCLickListener {
    void onItemClick(int mPosition);
  }

  public void setOnItemClickListener(HorizontalGalleryRecyclerViewAdapter.onItemCLickListener listener) {
    mListener = listener;
  }

  public HorizontalGalleryRecyclerViewAdapter(Context context, ArrayList < ImageItems > arrayList) {
    this.mContext = context;
    this.arrayList = arrayList;
  }

  @NonNull
  @Override
  public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new MyViewHolder((LayoutInflater.from(mContext).inflate(R.layout.horizontal_gallery_recycleriew_item, parent, false)), mListener);
  }

  @Override
  public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
    Glide.with(mContext)
      .load(arrayList.get(position).ImagePath)
      .placeholder(R.drawable.background_gallery_placeholder)
      .error(R.drawable.background_gallery_placeholder)
      .thumbnail(0.5 f).into(holder.img);
    Last_position = position;
  }


  @Override
  public int getItemCount() {
    return arrayList.size();
  }


  public static class MyViewHolder extends RecyclerView.ViewHolder {
    public RoundedImageView img;

    public MyViewHolder(View view, final HorizontalGalleryRecyclerViewAdapter.onItemCLickListener listener) {
      super(view);

      img = view.findViewById(R.id.horizontal_img);
      img.setScaleX(1.1 f);
      img.setScaleY(1.1 f);
      img.animate().setDuration(3 _000).setStartDelay(1 _000)
        .setInterpolator(new AccelerateDecelerateInterpolator())
        .scaleX(1).scaleY(1).start();

      view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
          if (listener != null) {
            int position = getAdapterPosition();
            if (position != RecyclerView.NO_POSITION) {
              listener.onItemClick(getAdapterPosition());
            }
          }
        }
      });


    }
  }
}

1 Answers

This effect could be achieved by scaling down the image. I created a small demo without a RecyclerView as a showcase, so don't forget to call view.animate().cancel() when a view gets recycled.

To be able to scale down the image, I scaled it up first. Then I animated the downscale.

public class MainActivity extends AppCompatActivity {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View view = findViewById(R.id.image);
        view.setScaleX(1.1f);
        view.setScaleY(1.1f);
        view.animate().setDuration(3_000).setStartDelay(1_000)
                .setInterpolator(new AccelerateDecelerateInterpolator())
                .scaleX(1).scaleY(1).start();
    }
}

I chose android:scaleType="centerCrop" in this example, but you should be able to achieve the same when you do the operation with Glide.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/image" />
</FrameLayout>

To achieve the scaling with your implementation, you should set the initial scale with every binding.

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
    holder.img.setScaleX(1.1 f);
    holder.img.setScaleY(1.1 f);
    Glide.with(mContext)
      .load(arrayList.get(position).ImagePath)
      .placeholder(R.drawable.background_gallery_placeholder)
      .error(R.drawable.background_gallery_placeholder)
      .thumbnail(0.5 f).into(holder.img);
}

The ViewHolder itself should only contain the logic for finding the view.

public static class MyViewHolder extends RecyclerView.ViewHolder {
    public RoundedImageView img;

    public MyViewHolder(View view, final HorizontalGalleryRecyclerViewAdapter.onItemCLickListener listener) {
        super(view);
        img = view.findViewById(R.id.horizontal_img);
        view.setOnClickListener(...);
    }
}

The tricky part is to actually start the animation, since the current viewing state is not part of the adapter. You'd have to provide it from outside. You could then provide the information with the payload of notifyItemChanged(int,Object) and handle it in onBindViewHolder(VH,int,List) instead. But eventually onViewAttachedToWindow(VH) could be sufficient for your usecase.

private void startAnimation(VH viewHolder, long duration, long delay) {
    viewHolder.img.animate()
        .setDuration(duration).setStartDelay(delay)
        .setInterpolator(new AccelerateDecelerateInterpolator())
        .scaleX(1).scaleY(1).start();
}
Related