弹出menu初步

This commit is contained in:
renhaoting 2025-10-23 16:16:07 +08:00
parent 636375fe15
commit 7459af2f0a
6 changed files with 270 additions and 5 deletions

View File

@ -3,7 +3,9 @@ package com.remax.visualnovel.ui.Chat
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import android.widget.Toast
import com.dylanc.viewbinding.nonreflection.inflate
import com.remax.visualnovel.R
import com.remax.visualnovel.databinding.ChatInputpanelBinding
@ -16,7 +18,24 @@ class InputPanel @JvmOverloads constructor(context: Context, attrs: AttributeSet
init {
binding = inflate(ChatInputpanelBinding::inflate)
binding?.run {
chatPopMenu.setMenuList(mutableListOf(
PopMenuIconView.MenuItem(R.mipmap.ic_launcher) {
Toast.makeText(context, "首页", Toast.LENGTH_SHORT).show()
},
PopMenuIconView.MenuItem(R.mipmap.ic_launcher) {
Toast.makeText(context, "设置", Toast.LENGTH_SHORT).show()
},
PopMenuIconView.MenuItem(R.mipmap.ic_launcher) {
Toast.makeText(context, "分享", Toast.LENGTH_SHORT).show()
},
PopMenuIconView.MenuItem(R.mipmap.ic_launcher) {
Toast.makeText(context, "收藏", Toast.LENGTH_SHORT).show()
}
))
}
}
}

View File

@ -0,0 +1,143 @@
package com.remax.visualnovel.ui.Chat
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import com.dylanc.viewbinding.nonreflection.inflate
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.OvershootInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import com.remax.visualnovel.R
import com.remax.visualnovel.databinding.LayoutPopIconMenuViewBinding
import com.remax.visualnovel.utils.spannablex.utils.dp
class PopMenuIconView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
data class MenuItem(
val iconResId: Int,
val onClick: (View) -> Unit
)
private val mMenuItemList = mutableListOf<MenuItem>()
private var isMenuShowing = true
private var itemSpacing = 0
private var itemSize = 0
private var mBinding: LayoutPopIconMenuViewBinding? = null
init {
itemSize = 30.dp
itemSpacing = 20.dp
mBinding = inflate(LayoutPopIconMenuViewBinding::inflate)
mBinding?.run {
ivTrigger.setImageResource(R.mipmap.chat_up)
ivTrigger.setOnClickListener { toggleMenu() }
}
}
fun setMenuList(menuList : List<MenuItem>) {
mMenuItemList.clear()
mMenuItemList.addAll(menuList)
updateMenuItems()
}
private fun updateMenuItems() {
mBinding!!.llMenuContainer.removeAllViews()
mMenuItemList.forEachIndexed { index, menuItem ->
ImageView(context).apply {
setImageResource(menuItem.iconResId)
layoutParams = LinearLayout.LayoutParams(itemSize, itemSize).apply {
if (index < mMenuItemList.size - 1) {
bottomMargin = itemSpacing
}
}
setOnClickListener {
menuItem.onClick(this)
dismissMenu()
}
mBinding!!.llMenuContainer.addView(this)
}
}
}
fun toggleMenu() {
if (isMenuShowing) dismissMenu() else showMenu()
}
private fun showMenu() {
if (isMenuShowing) return
isMenuShowing = true
mBinding?.run {
llMenuContainer.visibility = VISIBLE
ivTrigger.setImageResource(R.mipmap.chat_down)
val maxWidthSpec = MeasureSpec.makeMeasureSpec(Int.MAX_VALUE/2, MeasureSpec.AT_MOST)
val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
llMenuContainer.measure(maxWidthSpec, heightSpec)
val measureHeight = llMenuContainer.measuredHeight
val valueYAnim = ValueAnimator.ofInt(0, measureHeight).apply {
addUpdateListener {
val layoutParam = llMenuContainer.layoutParams
layoutParam.height = it.animatedValue as Int
llMenuContainer.layoutParams = layoutParam
}
}
AnimatorSet().apply {
duration = 300
interpolator = OvershootInterpolator(2f)
playTogether(valueYAnim)
start()
}
}
}
private fun dismissMenu() {
if (!isMenuShowing) return
isMenuShowing = false
mBinding!!.ivTrigger.setImageResource(R.mipmap.chat_up)
mBinding?.run {
val valueYAnim = ValueAnimator.ofInt(llMenuContainer.measuredHeight, 0).apply {
addUpdateListener {
var layoutParam = llMenuContainer.layoutParams
layoutParam.height = it.animatedValue as Int
llMenuContainer.layoutParams = layoutParam
}
start()
}
AnimatorSet().apply {
duration = 300
interpolator = DecelerateInterpolator()
playTogether(valueYAnim)
start()
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.remax.visualnovel.utils
import android.content.Context
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.core.widget.PopupWindowCompat
object PopupUtils {
fun showPopup(context: Context, anchorView: View, layoutRes: Int) {
val contentView = LayoutInflater.from(context).inflate(layoutRes, null)
val popupWindow = PopupWindow(
contentView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isOutsideTouchable = true
elevation = 10f
}
PopupWindowCompat.setOverlapAnchor(popupWindow, true)
PopupWindowCompat.showAsDropDown(popupWindow, anchorView, 0, 0, Gravity.START)
}
fun createPopup(context: Context, layoutRes: Int) : PopupWindow {
val contentView = LayoutInflater.from(context).inflate(layoutRes, null)
val popupWindow = PopupWindow(
contentView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isOutsideTouchable = true
elevation = 10f
}
PopupWindowCompat.setOverlapAnchor(popupWindow, true)
return popupWindow
}
fun createPopup(context: Context, contentView: View) : PopupWindow {
val popupWindow = PopupWindow(
contentView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isOutsideTouchable = true
elevation = 10f
}
PopupWindowCompat.setOverlapAnchor(popupWindow, true)
return popupWindow
}
}

View File

@ -6,6 +6,14 @@
android:layout_height="match_parent"
android:background="@mipmap/bg_level_1_page"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_actor_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/splash_bg"
/>
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:layout_width="wrap_content"

View File

@ -7,7 +7,7 @@
android:padding="@dimen/dp_20" >
<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
<!--<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
android:id="@+id/chat_expand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -18,15 +18,21 @@
android:padding="@dimen/dp_12"
app:radiusToken="@string/radius_pill"
app:backgroundColorToken="@string/color_surface_element_normal"
/>
/>-->
<com.remax.visualnovel.ui.Chat.PopMenuIconView
android:id="@+id/chat_pop_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/chat_expand"
app:layout_constraintStart_toEndOf="@id/chat_pop_menu"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="@dimen/dp_10"
app:radiusToken="@string/radius_40"

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
app:radiusToken="@string/radius_pill"
app:backgroundColorToken="@string/color_surface_element_normal"
android:padding="@dimen/dp_14"
>
<LinearLayout
android:id="@+id/ll_menu_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="@dimen/dp_20">
</LinearLayout>
<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
android:id="@+id/iv_trigger"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/chat_up"
/>
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>