Android Carousel Image Slider With Material Design
Papon Ahasan
Posted on September 11, 2024
Dependency
implementation 'com.google.android.material:material:1.9.0'
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
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" />
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>
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
}
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
}
}
💖 💪 🙅 🚩
Papon Ahasan
Posted on September 11, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
githubcopilot AI Innovations at Microsoft Ignite 2024 What You Need to Know (Part 2)
November 29, 2024