DEV Community

Papon Ahasan
Papon Ahasan

Posted on

Android Carousel Image Slider With Material Design

Dependency

implementation 'com.google.android.material:material:1.9.0'
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
Enter fullscreen mode Exit fullscreen mode

Activity Layout

  <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/carousel_recycler_view"    
app:layoutManager="com.google.android.material.carousel.CarouselLayoutManager"
       android:layout_width="match_parent"
       tools:listitem="@layout/carousel_layout"
                    android:background="@drawable/rounded_corners"
      android:layout_height="140dp"
      android:layout_gravity="center"
      android:clipChildren="false"
      android:clipToPadding="false" />
Enter fullscreen mode Exit fullscreen mode

carousel_layout.xml

<com.google.android.material.carousel.MaskableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/carousel_item_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginStart="@dimen/_5sdp"
    android:layout_marginTop="@dimen/_5sdp"
    android:layout_marginEnd="@dimen/_5sdp"
    android:layout_marginBottom="@dimen/_5sdp"
    android:foreground="?attr/selectableItemBackground"
    app:shapeAppearance="?attr/shapeAppearanceCornerExtraLarge">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.github.chrisbanes.photoview.PhotoView
            android:id="@+id/carousel_image_view"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:contentDescription="TODO"
            android:scaleType="centerCrop"
            android:src="@drawable/new_bannerr"
            android:clipToOutline="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/close_btn"
            android:layout_width="@dimen/_30sdp"
            android:layout_height="@dimen/_30sdp"
            android:layout_marginEnd="10dp"
            android:contentDescription="TODO"
            android:padding="8dp"
            android:src="@drawable/close_for_edit_text"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@+id/carousel_image_view"
            app:tint="@color/color_red" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</com.google.android.material.carousel.MaskableFrameLayout>
Enter fullscreen mode Exit fullscreen mode

CarouselAdapter

class CarouselAdapter(
    private val imageList: ArrayList<Uri>,
    val context: Context,
    private val onListEmptyCallback: () -> Unit
) :
    RecyclerView.Adapter<CarouselAdapter.ItemViewHolder>() {

    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val imageView: PhotoView = itemView.findViewById(R.id.carousel_image_view)
        val carouselItemCounter: MaskableFrameLayout =
            itemView.findViewById(R.id.carousel_item_container)
        val deleteButton: ImageView = itemView.findViewById(R.id.close_btn)
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): CarouselAdapter.ItemViewHolder {
        val view =
            LayoutInflater.from(parent.context).inflate(R.layout.carousel_layout, parent, false)
        return ItemViewHolder(view)
    }

    @SuppressLint("RestrictedApi")
    override fun onBindViewHolder(holder: CarouselAdapter.ItemViewHolder, position: Int) {
        holder.imageView.load(imageList[position])

        holder.carouselItemCounter.setOnMaskChangedListener { maskRect ->
            // Any custom motion to run when mask size changes
            holder.deleteButton.translationX = maskRect.left
            holder.deleteButton.setAlpha(lerp(1F, 0F, 0F, 80F, maskRect.left))
        }

        // Handle delete button click
        holder.deleteButton.setOnClickListener {
            removeImage(position)
        }

        // Initialize the adapter with the image click callback
        holder.imageView.setOnClickListener {
            showImagePopup(imageList[position])
        }
    }

    // Function to remove the image from the list
    private fun removeImage(position: Int) {
        imageList.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, imageList.size)  // Update adapter to refresh the layout
        // Check if list is empty and trigger the callback
        if (imageList.isEmpty()) {
            onListEmptyCallback.invoke()
        }
    }

    // Function to show the image in a popup dialog
    private fun showImagePopup(imageUri: Uri) {
        // Create an ImageView dynamically
        val imageView = PhotoView(context)
        imageView.setImageURI(imageUri)
        imageView.adjustViewBounds = true // Allow scaling of the image

        // Create an AlertDialog to show the image
        val dialog = AlertDialog.Builder(context)
            .setView(imageView)
            .setPositiveButton("Close") { dialog, _ ->
                dialog.dismiss()
            }
            .create()

        // Show the dialog
        dialog.show()
        dialog.window?.setDimAmount(0.5f)

    }

    override fun getItemCount() = imageList.size
}

Enter fullscreen mode Exit fullscreen mode

Activity

private val imageList = ArrayList<Uri>()
private val carouselAdapter = CarouselAdapter(imageList, this) {
   updateAdapter()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_medical_record)

   setupRecyclerView()
   updateAdapter()
}

private fun setupRecyclerView() {
     carouselRecyclerView = findViewById(R.id.carousel_recycler_view)
carouselRecyclerView.setLayoutManager(CarouselLayoutManager())
    carouselRecyclerView.adapter = carouselAdapter
    // Initially show the empty view if the list is empty
     if (imageList.isEmpty()) {
            carouselRecyclerView.visibility = View.GONE
        } else {
            carouselRecyclerView.visibility = View.VISIBLE
        }
    }

private fun updateAdapter() {
    carouselRecyclerView.visibility = View.VISIBLE
    carouselAdapter.notifyDataSetChanged()
    // the empty view if the list is empty
    if (imageList.isEmpty()) {
       carouselRecyclerView.visibility = View.GONE
    } else {
       carouselRecyclerView.visibility = View.VISIBLE
     }
 }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)