1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

Android: Add Snackbar UI component

This commit is contained in:
Anish Mishra
2025-02-08 19:02:56 +05:30
parent 36d90c73a8
commit b89957efce
3 changed files with 124 additions and 0 deletions

View File

@@ -33,11 +33,21 @@ package org.godotengine.godot.utils
import android.app.Activity
import android.app.AlertDialog
import android.content.DialogInterface
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import org.godotengine.godot.R
import kotlin.math.abs
/**
* Utility class for managing dialogs.
@@ -183,5 +193,91 @@ internal class DialogUtils {
dialog.show()
}
}
/**
* Displays a Snackbar with an optional action button.
*
* @param context The Context in which the Snackbar should be displayed.
* @param message The message to display in the Snackbar.
* @param duration The duration for which the Snackbar should be visible (in milliseconds).
* @param actionText (Optional) The text for the action button. If `null`, the button is hidden.
* @param actionCallback (Optional) A callback function to execute when the action button is clicked. If `null`, no action is performed.
*/
fun showSnackbar(activity: Activity, message: String, duration: Long = 3000, actionText: String? = null, action: (() -> Unit)? = null) {
activity.runOnUiThread {
val bottomMargin = activity.resources.getDimensionPixelSize(R.dimen.snackbar_bottom_margin)
val inflater = LayoutInflater.from(activity)
val customView = inflater.inflate(R.layout.snackbar, null)
val popupWindow = PopupWindow(
customView,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
val messageView = customView.findViewById<TextView>(R.id.snackbar_text)
messageView.text = message
val actionButton = customView.findViewById<Button>(R.id.snackbar_action)
if (actionText != null && action != null) {
actionButton.text = actionText
actionButton.visibility = View.VISIBLE
actionButton.setOnClickListener {
action.invoke()
popupWindow.dismiss()
}
} else {
actionButton.visibility = View.GONE
}
addSwipeToDismiss(customView, popupWindow)
popupWindow.showAtLocation(customView, Gravity.BOTTOM, 0, bottomMargin)
Handler(Looper.getMainLooper()).postDelayed({ popupWindow.dismiss() }, duration)
}
}
private fun addSwipeToDismiss(view: View, popupWindow: PopupWindow) {
view.setOnTouchListener(object : View.OnTouchListener {
private var initialX = 0f
private var dX = 0f
private val threshold = 300f // Swipe distance threshold.
override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialX = event.rawX
dX = view.translationX
}
MotionEvent.ACTION_MOVE -> {
val deltaX = event.rawX - initialX
view.translationX = dX + deltaX
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
val finalX = event.rawX - initialX
if (abs(finalX) > threshold) {
// If swipe exceeds threshold, dismiss smoothly.
view.animate()
.translationX(if (finalX > 0) view.width.toFloat() else -view.width.toFloat())
.setDuration(200)
.withEndAction { popupWindow.dismiss() }
.start()
} else {
// If swipe is canceled, return smoothly.
view.animate()
.translationX(0f)
.setDuration(200)
.start()
}
}
}
return true
}
})
}
}
}