From 53f2a4266f3e42296bdcc0f0c8365b2cd4a152b1 Mon Sep 17 00:00:00 2001
From: Runt <qingingrunt2010@qq.com>
Date: Fri, 12 Dec 2025 17:31:35 +0000
Subject: [PATCH] 输入框

---
 libmvi/src/main/java/com/runt/open/mvi/base/model/BaseViewModel.kt |   47 +++++++--
 libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt        |  139 ++++++++++++++++++++-------
 libmvi/src/main/java/com/runt/open/mvi/data/MessageState.kt        |   29 +++--
 libmvi/src/main/java/com/runt/open/mvi/data/InputMessageState.kt   |   31 ++++++
 libmvi/src/main/java/com/runt/open/mvi/base/LayoutView.kt          |    9 +
 libmvi/src/main/java/com/runt/open/mvi/data/Message.kt             |   21 ++++
 6 files changed, 211 insertions(+), 65 deletions(-)

diff --git a/libmvi/src/main/java/com/runt/open/mvi/base/LayoutView.kt b/libmvi/src/main/java/com/runt/open/mvi/base/LayoutView.kt
index 5a863fc..8a40fea 100644
--- a/libmvi/src/main/java/com/runt/open/mvi/base/LayoutView.kt
+++ b/libmvi/src/main/java/com/runt/open/mvi/base/LayoutView.kt
@@ -4,6 +4,9 @@
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import com.runt.open.mvi.base.model.BaseViewModel
+import com.runt.open.mvi.data.InputMessageState
+import com.runt.open.mvi.data.MessageState
+import com.runt.open.mvi.views.InputDialog
 import com.runt.open.mvi.views.LoadingDialog
 import com.runt.open.mvi.views.MessageDialog
 import com.runt.open.mvi.views.PopupWindow
@@ -25,7 +28,11 @@
         layout()
         // 显示 loading 弹窗
         LoadingDialog(isLoading)
-        MessageDialog(messageState)
+        if(messageState is MessageState){
+            MessageDialog(message = messageState as MessageState)
+        }else if(messageState is InputMessageState){
+            InputDialog(message = messageState as InputMessageState)
+        }
         PopupWindow(popupState)
     }
 
diff --git a/libmvi/src/main/java/com/runt/open/mvi/base/model/BaseViewModel.kt b/libmvi/src/main/java/com/runt/open/mvi/base/model/BaseViewModel.kt
index 7c8909b..c9a38e9 100644
--- a/libmvi/src/main/java/com/runt/open/mvi/base/model/BaseViewModel.kt
+++ b/libmvi/src/main/java/com/runt/open/mvi/base/model/BaseViewModel.kt
@@ -3,14 +3,16 @@
 import android.content.Intent
 import android.net.Uri
 import android.os.Build
-import android.provider.MediaStore
 import android.util.Log
+import androidx.compose.ui.text.input.KeyboardType
 import androidx.core.content.FileProvider
 import androidx.lifecycle.ViewModel
 import com.google.gson.Gson
 import com.runt.open.mvi.base.BaseActivity
 import com.runt.open.mvi.base.LayoutView
+import com.runt.open.mvi.data.InputMessageState
 import com.runt.open.mvi.data.LoadingState
+import com.runt.open.mvi.data.Message
 import com.runt.open.mvi.data.MessageState
 import com.runt.open.mvi.data.PopupMessage
 import com.runt.open.mvi.retrofit.AndroidScheduler
@@ -27,7 +29,6 @@
 import okhttp3.RequestBody.Companion.toRequestBody
 import okhttp3.Response
 import java.io.File
-import java.io.FileInputStream
 import java.io.FileOutputStream
 import java.io.IOException
 import java.io.InputStream
@@ -47,9 +48,9 @@
     val isLoading = _isLoading.asStateFlow()
 
     val messageSetDismiss = {
-        _messageState.value = _messageState.value.copy(isVisible = false);
+        _messageState.value = MessageState(isVisible = false)
     }
-    private val _messageState = MutableStateFlow(MessageState(isVisible = false, setDismiss = { }))
+    private val _messageState = MutableStateFlow(Message(isVisible = false, setDismiss = { }))
     val messageState = _messageState.asStateFlow()
 
     private val _popupState = MutableStateFlow(PopupMessage(isVisible = false,"",""))
@@ -91,22 +92,46 @@
     fun hideLoading() {
         _isLoading.value = _isLoading.value.copy(isVisible = false);
     }
-
-    fun showDialog(
-        title : String = "" , message : String = "" , confirmText : String = "确定" , cancelText : String = "" ,
+    fun showInputDialog(
+        title : String = "" , message : String = "" , hint : String = "",regex : String = "",
+        confirmText:String = "确定", cancelText:String = "取消",
+        maxLines:Int = 1, minLength:Int = 0, maxLength:Int = 0,
+        inputType : KeyboardType = KeyboardType.Text ,
         touchOutside : Boolean = true , //空白和系统返回 是否关闭
         showClose : Boolean = false , //显示关闭图标(默认不显示)
-        confirmDissmiss : Boolean = true , //点击确定是否关闭
-        cancelDissmiss : Boolean = true , //点击取消是否关闭
+        confirmDismiss : Boolean = true , //点击确定是否关闭
+        cancelDismiss : Boolean = true , //点击取消是否关闭
+        onDismissRequest : () -> Unit = {} ,
+        onCancelRequest : () -> Unit = {} ,
+        onConfirmRequest : (String) -> Unit = {} ,
+    ){
+        _messageState.value = InputMessageState(title = title, message = message, hint = hint, regex = regex,
+            maxLines = maxLines, minLength = minLength, maxLength = maxLength, inputType = inputType,
+            touchOutside = touchOutside, showClose = showClose, confirmText = confirmText,
+            cancelText = cancelText, confirmDismiss = confirmDismiss, cancelDismiss = cancelDismiss, onCancelRequest = onCancelRequest, onDismissRequest = onDismissRequest,
+            onConfirmRequest = onConfirmRequest, setDismiss = messageSetDismiss);
+    }
+
+    fun showDialog(
+        title : String = "" , message : String = "" ,
+        confirmText : String = "确定" , cancelText : String = "" ,
+        touchOutside : Boolean = true , //空白和系统返回 是否关闭
+        showClose : Boolean = false , //显示关闭图标(默认不显示)
+        confirmDismiss : Boolean = true , //点击确定是否关闭
+        cancelDismiss : Boolean = true , //点击取消是否关闭
         onDismissRequest : () -> Unit = {} ,
         onCancelRequest : () -> Unit = {} ,
         onConfirmRequest : () -> Unit = {} ,
     ){
-        _messageState.value = MessageState(title = title, message = message, touchOutside = touchOutside, showClose = showClose, confirmText = confirmText,
-            cancelText = cancelText, confirmDissmiss = confirmDissmiss, cancelDissmiss = cancelDissmiss, onCancelRequest = onCancelRequest, onDismissRequest = onDismissRequest,
+        _messageState.value = MessageState(title = title, message = message,  touchOutside = touchOutside, showClose = showClose, confirmText = confirmText,
+            cancelText = cancelText, confirmDismiss = confirmDismiss, cancelDismiss = cancelDismiss, onCancelRequest = onCancelRequest, onDismissRequest = onDismissRequest,
             onConfirmRequest = onConfirmRequest, setDismiss = messageSetDismiss);
     }
 
+    fun dismissDialog(){
+        messageSetDismiss.invoke();
+    }
+
     fun showPopupWindow(title : String,message : String){
         _popupState.value = PopupMessage(isVisible = true,title,message)
     }
diff --git a/libmvi/src/main/java/com/runt/open/mvi/data/InputMessageState.kt b/libmvi/src/main/java/com/runt/open/mvi/data/InputMessageState.kt
new file mode 100644
index 0000000..505d179
--- /dev/null
+++ b/libmvi/src/main/java/com/runt/open/mvi/data/InputMessageState.kt
@@ -0,0 +1,31 @@
+package com.runt.open.mvi.data
+
+import androidx.compose.ui.text.input.KeyboardType
+
+/**
+ * @author Runt(qingingrunt2010@qq.com)
+ * @purpose
+ * @date 12/13/25
+ */
+class InputMessageState(
+    isVisible: Boolean = true,
+    title:String = "",
+    val hint:String = "请输入文本",
+    message: String = "加载中...",
+    confirmText:String = "确定",
+    cancelText:String = "取消",
+    val maxLines:Int = 1,
+    val minLength:Int = 0,
+    val maxLength:Int = 0,
+    val inputType : KeyboardType  = KeyboardType.Text ,
+    val regex:String = "",//限制字符
+    touchOutside:Boolean = true,//空白和系统返回 是否关闭
+    showClose:Boolean = false,//显示关闭图标(默认不显示)
+    confirmDismiss:Boolean = true,//点击确定是否关闭
+    cancelDismiss:Boolean = true,//点击取消是否关闭
+    onDismissRequest : () -> Unit = {},
+    onCancelRequest : () -> Unit = {},
+    val onConfirmRequest : (String) -> Unit = {},
+    setDismiss:() -> Unit = {}
+):Message(isVisible,title,message,confirmText,cancelText,touchOutside,showClose,
+    confirmDismiss,cancelDismiss,onDismissRequest,onCancelRequest,setDismiss)
diff --git a/libmvi/src/main/java/com/runt/open/mvi/data/Message.kt b/libmvi/src/main/java/com/runt/open/mvi/data/Message.kt
new file mode 100644
index 0000000..4728946
--- /dev/null
+++ b/libmvi/src/main/java/com/runt/open/mvi/data/Message.kt
@@ -0,0 +1,21 @@
+package com.runt.open.mvi.data
+
+/**
+ * @author Runt(qingingrunt2010@qq.com)
+ * @purpose
+ * @date 12/13/25
+ */
+open class Message (
+    val isVisible: Boolean = true,
+    val title:String = "",
+    val message: String = "加载中...",
+    val confirmText:String = "确定",
+    val cancelText:String = "",
+    val touchOutside:Boolean = true,//空白和系统返回 是否关闭
+    val showClose:Boolean = false,//显示关闭图标(默认不显示)
+    val confirmDismiss:Boolean = true,//点击确定是否关闭
+    val cancelDismiss:Boolean = true,//点击取消是否关闭
+    val onDismissRequest : () -> Unit = {},
+    val onCancelRequest : () -> Unit = {},
+    var setDismiss:() -> Unit = {}
+)
\ No newline at end of file
diff --git a/libmvi/src/main/java/com/runt/open/mvi/data/MessageState.kt b/libmvi/src/main/java/com/runt/open/mvi/data/MessageState.kt
index e2b6f5f..fbb21c6 100644
--- a/libmvi/src/main/java/com/runt/open/mvi/data/MessageState.kt
+++ b/libmvi/src/main/java/com/runt/open/mvi/data/MessageState.kt
@@ -5,18 +5,19 @@
  * @purpose
  * @date 5/18/25
  */
-data class MessageState(
-    val isVisible: Boolean = true,
-    val title:String = "",
-    val message: String = "加载中...",
-    val confirmText:String = "确定",
-    val cancelText:String = "",
-    val touchOutside:Boolean = true,//空白和系统返回 是否关闭
-    val showClose:Boolean = false,//显示关闭图标(默认不显示)
-    val confirmDissmiss:Boolean = true,//点击确定是否关闭
-    val cancelDissmiss:Boolean = true,//点击取消是否关闭
-    val onDismissRequest : () -> Unit = {},
-    val onCancelRequest : () -> Unit = {},
+class MessageState(
+    isVisible: Boolean = true,
+    title:String = "",
+    message: String = "加载中...",
+    confirmText:String = "确定",
+    cancelText:String = "",
+    touchOutside:Boolean = true,//空白和系统返回 是否关闭
+    showClose:Boolean = false,//显示关闭图标(默认不显示)
+    confirmDismiss:Boolean = true,//点击确定是否关闭
+    cancelDismiss:Boolean = true,//点击取消是否关闭
+    onDismissRequest : () -> Unit = {},
+    onCancelRequest : () -> Unit = {},
     val onConfirmRequest : () -> Unit = {},
-    var setDismiss:() -> Unit = {}
-)
\ No newline at end of file
+    setDismiss:() -> Unit = {}
+):Message(isVisible,title,message,confirmText,cancelText,touchOutside,showClose,
+    confirmDismiss,cancelDismiss,onDismissRequest,onCancelRequest,setDismiss)
\ No newline at end of file
diff --git a/libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt b/libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt
index 0208e22..f37ce01 100644
--- a/libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt
+++ b/libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt
@@ -1,6 +1,8 @@
 package com.runt.open.mvi.views
 
+import android.text.TextUtils
 import android.util.Log
+import android.widget.Toast
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
@@ -9,13 +11,17 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.Card
@@ -24,12 +30,19 @@
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TextField
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.Constraints
@@ -37,6 +50,8 @@
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.window.Dialog
 import androidx.compose.ui.window.Popup
+import com.runt.open.mvi.OpenApplication
+import com.runt.open.mvi.data.InputMessageState
 import com.runt.open.mvi.data.LoadingState
 import com.runt.open.mvi.data.MessageState
 import com.runt.open.mvi.data.PopupMessage
@@ -57,10 +72,7 @@
 ) {
     Layout(content, modifier) { measurables, parent ->
         // 给子树宽高用"无限"上限测量
-        val loose = Constraints(
-            minWidth = 0, minHeight = 0,
-            maxWidth = Int.MAX_VALUE, maxHeight = Int.MAX_VALUE
-        )
+        val loose = Constraints(minWidth = 0, minHeight = 0, maxWidth = Int.MAX_VALUE, maxHeight = Int.MAX_VALUE)
         val placeables = measurables.map { it.measure(loose) }
 
         // 自己仍按父约束的大小布局(不撑爆父)
@@ -78,20 +90,13 @@
     if (loadingState.isVisible) {
         Dialog(onDismissRequest = {}) {
             Column(
-                modifier = Modifier
-                    .size(140.dp)
-                    .clip(RoundedCornerShape(16.dp))
-                    .background(Color.White),
+                modifier = Modifier.size(140.dp).clip(RoundedCornerShape(16.dp)).background(Color.White),
                 horizontalAlignment = Alignment.CenterHorizontally,
                 verticalArrangement = Arrangement.Center
             ) {
                 CircularProgressIndicator()
                 Spacer(modifier = Modifier.height(12.dp))
-                Text(
-                    text = loadingState.message,
-                    fontSize = 14.sp,
-                    color = Color.Black
-                )
+                Text(text = loadingState.message, fontSize = 14.sp, color = Color.Black)
             }
         }
     }
@@ -109,33 +114,23 @@
             }
         }) {
             Card(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .wrapContentHeight()
-                    .padding(16.dp),
+                modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(16.dp),
                 shape = RoundedCornerShape(16.dp),
             ) {
                 Column(modifier = Modifier.wrapContentHeight()){
                     Text(text = message.title, fontSize = 16.sp,
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .padding(top = 20.dp , start = 20.dp)
-                            .wrapContentSize(Alignment.Center),
+                        modifier = Modifier.fillMaxWidth().padding(top = 20.dp , start = 20.dp).wrapContentSize(Alignment.Center),
                         textAlign = TextAlign.Center)
                     Text(
                         text = message.message,
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .wrapContentSize()
-                            .padding(top = 30.dp , bottom = 20.dp , start = 14.dp , end = 15.dp)
-                            .wrapContentSize(Alignment.Center),
+                        modifier = Modifier.fillMaxWidth().wrapContentSize().padding(top = 30.dp , bottom = 20.dp , start = 14.dp , end = 15.dp).wrapContentSize(Alignment.Center),
                         textAlign = TextAlign.Center,
                     )
                     Row (modifier = Modifier.padding(start = 15.dp, end = 15.dp, bottom = 20.dp)){
                         if(!message.cancelText.equals("")){
                             Spacer(modifier = Modifier.weight(1f))
                             Button(onClick = {
-                                if(message.cancelDissmiss){
+                                if(message.cancelDismiss){
                                     message.setDismiss.invoke()
                                     message.onDismissRequest.invoke()
                                 }
@@ -151,7 +146,7 @@
                         }
                         Spacer(modifier = Modifier.weight(1f))
                         Button(onClick = {
-                            if(message.confirmDissmiss){
+                            if(message.confirmDismiss){
                                 message.setDismiss.invoke()
                                 message.onDismissRequest.invoke()
                             }
@@ -166,6 +161,80 @@
     }
 }
 
+
+@Composable
+fun InputDialog(message : InputMessageState) {
+    if(!message.isVisible){
+        return
+    }
+    var text by remember {
+        mutableStateOf(if(TextUtils.isEmpty(message.message)) message.hint else message.message)
+    }
+    var modifier : Modifier;
+    var keyboardType = message.inputType;
+    if (message.maxLines == 1) {
+        modifier = Modifier.width(260.dp).height(50.dp).padding(0.dp)
+    } else {
+        modifier = Modifier.width(260.dp).padding(0.dp).heightIn(min = 50.dp, max = 200.dp)
+    }
+    AlertDialog(onDismissRequest = {
+            if(message.touchOutside){
+                message.setDismiss.invoke()
+                message.onDismissRequest.invoke()
+            }
+        } , confirmButton = {
+        TextButton(onClick = {
+            if (message.minLength > 0 || message.maxLength > 0  || !TextUtils.isEmpty(message.regex)) {
+                var flag = true;
+                if(text.length < message.minLength){
+                    flag = false;
+                }
+                if(text.length > message.maxLength && message.maxLength > 0){
+                    flag = false;
+                }
+                if (!message.regex.isEmpty() && !message.regex.toRegex().matches(text)) {
+                    flag = false;
+                }
+                if(flag){
+                    if(message.confirmDismiss){
+                        message.setDismiss.invoke()
+                        message.onDismissRequest.invoke()
+                    }
+                    message.onConfirmRequest(text)
+                } else {
+                    Toast.makeText(OpenApplication.getApplication() , message.hint , Toast.LENGTH_SHORT).show();
+                }
+            } else {
+                if(message.confirmDismiss){
+                    message.setDismiss.invoke()
+                    message.onDismissRequest.invoke()
+                }
+                message.onConfirmRequest(text)
+            }
+        }) {
+            Text(text = message.confirmText)
+        }
+    } , dismissButton = {
+        TextButton(onClick = {
+            if(message.cancelDismiss){
+                message.setDismiss.invoke()
+                message.onDismissRequest.invoke()
+            }
+            message.onCancelRequest.invoke()
+        }) {
+            Text(text = message.cancelText)
+        }
+    } , title = {
+        Text(text = message.title , fontSize = 16.sp)
+    } , text = {
+        TextField(value = text , onValueChange = { //val pattern = """(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]""".toRegex()
+            text = it;
+        } , singleLine = message.maxLines == 1 , maxLines = message.maxLines, modifier = modifier , keyboardOptions = KeyboardOptions(keyboardType = keyboardType) , placeholder = {
+            Text(text = message.hint , fontSize = 14.sp)
+        } , textStyle = TextStyle(fontSize = 14.sp , color = Color.Black))
+    })
+}
+
 @Composable
 fun PopupWindow(popupMessage : PopupMessage){
     if(popupMessage.isVisible){
@@ -173,10 +242,7 @@
             alignment = Alignment.TopCenter,
         ) {
             Surface(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .wrapContentHeight()
-                    .padding(10.dp),
+                modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(10.dp),
                 shadowElevation = 2.dp,
                 border = BorderStroke(1.dp, Color.Gray) ,
                 shape = RoundedCornerShape(8.dp)
@@ -197,18 +263,13 @@
 
 @Composable
 fun TitleBarView(title:String,onBackClick:()->Unit){
-    Row(modifier = Modifier
-        .wrapContentSize(Alignment.Center)
-        .height(50.dp), verticalAlignment = Alignment.CenterVertically) {
+    Row(modifier = Modifier.wrapContentSize(Alignment.Center).height(50.dp), verticalAlignment = Alignment.CenterVertically) {
         Spacer(modifier = Modifier.size(15.dp))
         IconButton(onClick = onBackClick, modifier = Modifier.size(30.dp,30.dp)) {
             Icon(Icons.Default.ArrowBack, contentDescription = null, tint = Color.Black)
         }
 
-        Text(text = "${title}", modifier = Modifier
-            .weight(1f)
-            .fillMaxWidth()
-            .wrapContentSize(Alignment.Center))
+        Text(text = "${title}", modifier = Modifier.weight(1f).fillMaxWidth().wrapContentSize(Alignment.Center))
         Spacer(modifier = Modifier.size(30.dp))
         Spacer(modifier = Modifier.size(15.dp))
     }

--
Gitblit v1.9.1