【问题标题】:Custom DialogFragment with AlertDialog returns EditText as ""带有 AlertDialog 的自定义 DialogFragment 将 EditText 返回为 \"\"
【发布时间】:2022-02-04 09:47:35
【问题描述】:

我有一个习惯对话片段我用它来捕获用户输入,我将使用它来创建数据库条目。我在用着编辑文本在一个警报对话框.我正在尝试为我的应用程序使用单个活动,而我正在学习的原始教程使用多个活动和意图,但在大多数情况下这似乎已经过时了。

当我调试时,我发现编辑文本正在返回 \"\" 并在我打电话时显示为空TextUtils.isEmpty()在里面主要活动 onDialogPositiveClick.

我已经对这里的表格进行了很多梳理,但我对以下内容感到困惑:

1)我找到的许多答案都是 Java 而不是 Kotlin

2)很多人提到 onCreate 但没有指定 onCreateView 与 onCreateDialog 或者是否只有一个我需要覆盖的 onCreate 。

我对此进行了研究,并找到了一些答案,这些答案让我对何时以及是否需要对布局进行膨胀感到有些困惑。这个当前的迭代我根本没有给它充气。我只是将它设置在警报对话框建设者。

可能是我不理解的界面。我应该如何在对话框和 MainActivity 之间传递信息?该界面似乎通过了对话框本身,但是在从对话框中获取 EditText 时我似乎遗漏了一些东西。

我的自定义 DialogFragment

class NewSongFragment : DialogFragment() {
    lateinit var listener: NewSongListener

    lateinit var editNewSong: EditText
    lateinit var editBPM: EditText

    interface NewSongListener {
        fun onDialogPositiveClick(dialog: DialogFragment)
        fun onDialogNegativeClick(dialog: DialogFragment)
    }

    /** The system calls this to get the DialogFragment\'s layout, regardless
    of whether it\'s being displayed as a dialog or an embedded fragment. */
   /*
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout to use as dialog or embedded fragment
        return inflater.inflate(R.layout.fragment_new_song, container, false)

    }
*/
    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    override fun onAttach(context: Context) {
        super.onAttach(context)
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = context as NewSongListener
        } catch (e: ClassCastException) {
            // The activity doesn\'t implement the interface, throw exception
            throw ClassCastException((context.toString() +
                    \" must implement NewSongListener\"))
        }
    }



    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        return activity?.let {
            // Use the Builder class for convenient dialog construction
            val builder = AlertDialog.Builder(it)

            //add inflater
            //val inflater = requireActivity().layoutInflater;
            //val view = inflater.inflate(R.layout.fragment_new_song, null)
            builder
                .setView(R.layout.fragment_new_song)
                .setCancelable(true)
                .setNegativeButton(R.string.cancel,DialogInterface.OnClickListener { dialog, id ->
                    dialog?.cancel()
                })
                .setPositiveButton(R.string.button_save,
                    DialogInterface.OnClickListener {dialog, _ ->
                        listener.onDialogPositiveClick(this)
                    })

            // Create the AlertDialog object and return it
            builder.create()
        } ?: throw IllegalStateException(\"Activity cannot be null\")

    }

}

我的主要活动

class MainActivity : AppCompatActivity(),NewSongFragment.NewSongListener {
    private val songViewModel: SongViewModel by viewModels {
        SongViewModelFactory((application as SongApplication).repository)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //create view
        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        val adapter = ItemAdapter(this,
            ItemAdapter.OnClickListener { rating -> songViewModel.insertRating(rating) }
        )
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        //initialize data
        songViewModel.allSongs.observe(this) { song ->
            // Update the cached copy of the songs in the adapter.
            song.let { adapter.submitList(it) }
        }


        // Use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView

        recyclerView.setHasFixedSize(true)

        //add song button
        val fab = findViewById<FloatingActionButton>(R.id.fab)
        fab.setOnClickListener {
            showNewSongDialog()
            }
        }

    private fun showNewSongDialog() {
        // Create an instance of the dialog fragment and show it
        val dialog = NewSongFragment()
        dialog.show(supportFragmentManager, \"NewSongFragment\")
    }
    override fun onDialogPositiveClick(dialog: DialogFragment) {
        // User touched the dialog\'s positive button
        val editNewSong = dialog.view?.findViewById<EditText>(R.id.newSongTitle)
        val editBPM = dialog.view?.findViewById<EditText>(R.id.newSongBpm)
        if(TextUtils.isEmpty(editNewSong?.text)){

        }else{
            val newSong = Song(editNewSong?.text.toString(),100)
            songViewModel.insertSong(newSong)
            val rating = Rating(System.currentTimeMillis(),newSong.songTitle, 50)
            songViewModel.insertRating(rating)
        }

    }

    override fun onDialogNegativeClick(dialog: DialogFragment) {
        // User touched the dialog\'s negative button
    }


}

标签: android kotlin android-edittext android-alertdialog android-dialogfragment


【解决方案1】:

您正在添加带有资源标识符的布局,因此您获取视图的调用返回 null。 (为什么?视图在内部膨胀,只是处理方式不同。)因为您使用的是警报对话框要收集数据,您必须添加一个膨胀的视图。

我还将建议您更改界面以隐藏对话框的详细信息;主活动没有理由知道对话的内部结构。它只需要歌曲名称和 BPM 以及其他一些东西。您会发现代码更易于理解和维护。

这是一个轻微的返工。此代码仅捕获歌曲标题,但也可以轻松扩展以包含其他数据。

新歌片段

interface NewSongListener {
    fun onDialogPositiveClick(songTitle: String)
    fun onDialogNegativeClick(dialog: DialogFragment)
}

val inflater = requireActivity().layoutInflater;
val view = inflater.inflate(R.layout.fragment_new_song, null)
builder
    .setView(view)
    .setCancelable(true)
    .setNegativeButton(R.string.cancel, DialogInterface.OnClickListener { dialog, id ->
        dialog?.cancel()
    })
    .setPositiveButton(R.string.button_save)
    { dialog, _ ->
        Log.d("Applog", view.toString())
        val songTitle = view?.findViewById<EditText>(R.id.newSongTitle)?.text
        listener.onDialogPositiveClick(songTitle.toString())
    }

在 MainActivity.kt

override fun onDialogPositiveClick(songTitle: String) {
    // songTitle has the song title string
}

Android 对话框有一些怪癖。 Here 是进行片段/活动通信的多种方式。

【讨论】:

  • 这对我来说更有意义,只是传递了需要的东西,但我使用的开发指南让它通过了对话框???现在我之前尝试过,但无法让它工作,开发指南让我第二次猜测自己。我之前也通过了膨胀视图而不是资源ID,但它没有解决“”问题。我会再试一次传递字符串。
  • 另外,Log.d 的含义是什么?我对此完全不熟悉,而且我唯一看到它的地方是在 Java 示例中。
  • @LukeMartin 我发布的代码有效,所以试一试。 Log.d() 只是在 logcat 中记录一个条目。它仅用于调试,可以删除。
  • 我不能让它像那样工作。首先,您的版本取消了 DialogInterface.OnClickListener 并传递了它。给听众。我的希望我传递一个字符串,而不是这个。
  • @LukeMartin 我的错误。我没有做所有的改变。我在答案中更新了该行。
【解决方案2】:

因为您将对话框添加为Fragment,所以您应该使用onCreateView 来扩充视图,而不是尝试在onCreateDialog 中添加视图。

【讨论】:

  • 如您所见,我的onCreateView 被注释掉了。当我在那里放大视图时,它不会显示在警报对话框中。我尝试了各种方法,在onCreateView 中进行膨胀,并尝试在AlertDialog 构建器中使用setView 添加它。我试过根本不添加到构建器中。所有这些都导致布局根本没有添加到对话框中。
  • 好吧,您根本不应该尝试在onCreateDialog 中“构建”对话框。在onCreateDialog 中删除您的代码,并将其视为将视图膨胀到onCreateView 的普通片段。您需要在 Fragment 布局中添加按钮等。否则不要使用片段,直接在需要的地方构建对话框。
  • 我正在使用一个警报对话框,因为我希望它像一个弹出窗口,当我查找如何做一个弹出窗口时,这就是我发现的。有没有办法使用分段这也像一个弹出视图?
  • 好吧,您可以像您正在做的那样将DialogFragment 子类化,但不要覆盖onCreateDialog 并自己手动提供视图,或者直接在您的活动中构建AlertDialog 以及如何在那里显示它(并完全跳过片段)。无论如何,听起来你有一些对你有用的建议。
猜你喜欢
  • 2018-04-19
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多