我试图写一个简化的AlertDialog,这样我就可以更多的在家与科特林,如果我不能使自己的语言做机器人,然后使科特林的行为,就像PureBasic将是理论。
我遇到的问题是这个错误:
4027-4027/com.example.cardgamexxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.cardgamexxx, PID: 4027
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1444)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:469)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:114)
at android.app.Dialog.show(Dialog.java:505)
at android.app.AlertDialog$Builder.show(AlertDialog.java:1157)
at com.example.cardgamexxx.Requester.messageRequester(Requester.kt:33)
at com.example.cardgamexxx.MainActivity.onCreate$lambda-3(MainActivity.kt:695)
at com.example.cardgamexxx.MainActivity.lambda$Zt8ODWx7LLRfq4535q7hQg5xQfw(Unknown Source:0)
at com.example.cardgamexxx.-$$Lambda$MainActivity$Zt8ODWx7LLRfq4535q7hQg5xQfw.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:8160)
at android.widget.TextView.performClick(TextView.java:16222)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:8137)
at android.view.View.access$3700(View.java:888)
at android.view.View$PerformClick.run(View.java:30236)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)使用此代码:
package com.example.cardgamexxx
import android.app.AlertDialog
import android.content.Context
import com.example.cardgamexxx.RequesterType.MessageRequester_Ok
import com.example.cardgamexxx.RequesterType.MessageRequester_YesNo
import com.example.cardgamexxx.RequesterType.MessageRequester_YesNoCancel
//------------------------------------------------------------------------------
// Very PureBasic'ish 'Requester' dialogs.
//------------------------------------------------------------------------------
object RequesterType {
const val MessageRequester_Ok = 1 // to have the 'ok' only button (default)
const val MessageRequester_YesNo = 2 // to have 'yes' or 'no' buttons
const val MessageRequester_YesNoCancel = 3 // to have 'yes', 'no' and 'cancel' buttons
}
class Requester {
fun inputRequester() {
}
fun messageRequester(appContext: Context,title: String,message: String,flags: Int) : Int {
var result: Int = 0
if( flags == MessageRequester_Ok ) {
AlertDialog.Builder(appContext)
.setTitle(title) // R.string.question_title
.setMessage(message) // R.string.question_message
.setPositiveButton("Ok") { _, _ -> result = 0 }
.show()
}
if( flags == MessageRequester_YesNo ) {
AlertDialog.Builder(appContext)
.setTitle(title) // R.string.question_title
.setMessage(message) // R.string.question_message
.setPositiveButton("Yes") { _, _ -> result = 0 }
.setNegativeButton("No") { _, _ -> result = 1 }
.show()
}
if( flags == MessageRequester_YesNoCancel ) {
AlertDialog.Builder(appContext)
.setTitle(title) // R.string.question_title
.setMessage(message) // R.string.question_message
.setPositiveButton("Yes") { _, _ -> result = 0 }
.setNegativeButton("No") { _, _ -> result = 1 }
.setNegativeButton("Cancel") { _, _ -> result = 2 }
.show()
}
return result
}
fun openFileRequester() {
}
fun pathRequester() {
}
fun saveFileRequester() {
}
}另一个问题是,在打开时请求者应该是‘代码’阻塞的意思,而不是这一切都很重要,但是如果"messageRequester()“返回一个结果,那么调用"messageRequester()”的代码就可以根据所给出的答案更改流。
我走错路了吗?我是否需要抛出这个想法,重新创造轮子,从根本上制造我自己的。
发布于 2021-07-28 05:22:24
假设您使用应用程序上下文(因为您将其命名为appContext)来显示对话框,我相信这是异常的根本原因。
您应该传递当前的Activity上下文。
更改您的函数签名以确保传递一个活动:
fun messageRequester(activity: Activity, title: String, message: String, flags: Int) : Int {
...
}关于代码阻塞,只有在后台线程上调用messageRequester函数才有可能,而这不是Android的原生方式。
您需要使用一些并发锁来锁定名为messageRequester的线程,并从AlertDialog的按钮侦听器中解锁它,通过将其保存到一个变量来转发它们的答案。
通常,我不建议使用锁,因为如果您不熟悉并发原则,那么使用锁是很危险的。
无论如何,我建议简单地在函数中传递一个lambda回调,然后用按钮侦听器的答案调用它。
使用回调
// enum to hold the answer (you're free to use whatever type you want for callback)
enum class Answer { YES, NO, CANCEL }
// Function signature
fun messageRequester(activity: Activity, title: String, message: String, flags: Int, callback: (answer: Answer) -> Unit) {
...
}
// How to pass result to callback from buttons listener
callback(Answer.YES)
// Passing callback to function
myRequester.messageRequester(activity, message, flags) { answer ->
// Handle answer here
}使用锁
// enum to hold the answer (you're free to use whatever type you want for callback)
enum class Answer { YES, NO, CANCEL }
// Function signature
fun messageRequester(activity: Activity, title: String, message: String, flags: Int) : Answer? {
// Don't use main thread!!!!!!
if (Looper.getMainLooper() == Looper.myLooper()) {
throw IllegalStateException("Suspending main thread is forbidden!")
}
// Variable to hold the answer
var answer: Answer? = null
// The lock that holds the thread until an answer is given
val countDownLatch = CountDownLatch(1)
// How to pass answer from buttons listener (in AlertDialog creation)
Listener {
answer = Answer.YES
countDownLatch.countDown()
}
// Wait for answer forever (not recommended since you can exit dialog without clicking buttons and block the thread forever)
countDownLatch.await()
// Wait for answer for 10 seconds (answer will be null in case of a timeout). returns false for timeouts.
val handled = countDownLatch.await(10L, TimeOut.SECONDS)
return answer
}发布于 2021-07-28 10:42:43
虽然我会很好,并张贴的代码,以防别人发现它有用。
更新添加输入请求程序
像这样打电话;
myreq.messageRequester(this,
"Testing Ok!","with this message",
MessageRequester_Ok) { a ->
Log.d("debug","$a")
}添加了最后一个函数,openFileRequester / PathRequester
添加这个类文件。
package com.example.cardgamexxx
import android.app.Activity
import android.app.AlertDialog
import android.net.Uri
import android.text.InputType
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import com.example.cardgamexxx.RequesterType.InputRequester_Password
import com.example.cardgamexxx.RequesterType.MessageRequester_Ok
import com.example.cardgamexxx.RequesterType.MessageRequester_YesNo
import com.example.cardgamexxx.RequesterType.MessageRequester_YesNoCancel
//------------------------------------------------------------------------------
// Very PureBasic'ish 'Requester' dialogs.
//
// Alert Simplification
// https://stackoverflow.com/questions/68554654/alert-dialog-simplification
//
// Help From:
//
// Shlomi Katriel
// Ticherhaz FreePalestine
//------------------------------------------------------------------------------
object RequesterType {
const val MessageRequester_Ok = 1 // to have the 'ok' only button (default)
const val MessageRequester_YesNo = 2 // to have 'yes' or 'no' buttons
const val MessageRequester_YesNoCancel = 3 // to have 'yes', 'no' and 'cancel' buttons
const val InputRequester_Password = 4
}
enum class RequesterAnswer { YES, NO, CANCEL, OK }
class Requester {
fun inputRequester(activity: Activity, title: String, message: String, flags: Int,callback: (answer: String) -> Unit) {
val editText = EditText(activity)
when (flags) {
InputRequester_Password -> {
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
}
val inputDialog = AlertDialog.Builder(activity)
inputDialog.setTitle(title)
.setView(editText)
.setCancelable(false)
.setNegativeButton("Cancel") { _, _ -> callback("CANCEL") }
.setPositiveButton("OK") { _,_ -> callback (editText.text.toString()) }
inputDialog.show()
}
fun messageRequester(activity: Activity, title: String, message: String, flags: Int,callback: (answer: RequesterAnswer) -> Unit) {
if( flags == MessageRequester_Ok ) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Ok") { _, _ -> callback(RequesterAnswer.OK) }
.show()
}
if( flags == MessageRequester_YesNo ) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Yes") { _, _ -> callback(RequesterAnswer.YES) }
.setNegativeButton("No") { _, _ -> callback(RequesterAnswer.NO) }
.show()
}
if( flags == MessageRequester_YesNoCancel ) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Yes") { _, _ -> callback(RequesterAnswer.YES) }
.setNegativeButton("No") { _, _ -> callback(RequesterAnswer.NO) }
.setNegativeButton("Cancel") { _, _ -> callback(RequesterAnswer.CANCEL) } // doesn't work :(
.show()
}
}
fun openFileRequester(cActivity: AppCompatActivity,filter : String,callback: (answer: Uri) -> Unit) {
val openFileRequester = cActivity.registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri ->
callback(uri)
}
openFileRequester.launch(filter)
}
fun pathRequester(cActivity: AppCompatActivity,filter : String,callback: (answer: Uri) -> Unit) {
val getUserFolderData = cActivity.registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri ->
callback(uri)
}
getUserFolderData.launch(filter.toUri())
}
fun saveFileRequester(cActivity: AppCompatActivity,filter : String,callback: (answer: Uri) -> Unit) {
val getUserFolderData = cActivity.registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri ->
callback(uri)
}
getUserFolderData.launch(filter.toUri())
}
}
/*
How to use.
myreq.messageRequester(this,
"Testing Ok!","with this message",
MessageRequester_Ok) { a ->
Log.d("debug","$a")
}
myreq.messageRequester(this,
"Testing Yes / No","with this message",
MessageRequester_YesNo) { a ->
Log.d("debug","$a")
}
myreq.messageRequester(this,
"Testing Yes/No Cancel!","with this message",
MessageRequester_YesNoCancel) { a ->
Log.d("debug","$a")
}
*/https://stackoverflow.com/questions/68554654
复制相似问题