kotlin 意外超控:以下声明具有相同的JVM签名(getView()Landroid/view/View;)

ee7vknir  于 2022-12-23  发布在  Kotlin
关注(0)|答案(2)|浏览(183)

我尝试将一个Java文件转换为Kotlin文件时,遇到了来自构造函数参数var view: View的问题。我遇到过类似的问题,但还没有遇到过使用对话框的问题。将视图作为承包商参数传递是很重要的,因为我使用该视图实现一个依赖于对话框行为的重要功能。

转换为Kotlin之前

ackage com.indupendo.landing.ui.dialogs;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.indupendo.R;
import com.indupendo.databinding.DialogLoginBinding;
import com.indupendo.globals.utilities.Utils;

public class LoginDialog extends DialogFragment {

    DialogLoginBinding binding;
    View view;

    public LoginDialog(View view) {
        this.view = view;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        binding = DialogLoginBinding.inflate(getLayoutInflater());
        MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(requireActivity(),
                R.style.MaterialAlertDialog_rounded);
        dialogBuilder.setView(binding.getRoot());

        dialogBuilder.setNegativeButton("Cancel", (dialog, which) -> {
            Utils.INSTANCE.showLoginCancellationSnackBar(view, getLayoutInflater());
            dialog.cancel();
        });

        dialogBuilder.setPositiveButton("Login", (dialog, which) -> Toast.makeText(
                getActivity(),
                "Logged In",
                Toast.LENGTH_LONG).show());

        return dialogBuilder.create();
    }

    @Override
    public void onCancel(@NonNull DialogInterface dialog) {
        super.onCancel(dialog);
        Utils.INSTANCE.showLoginCancellationSnackBar(view, getLayoutInflater());
        dialog.cancel();
    }
}

转换后

package com.indupendo.landing.ui.fragments

import android.app.Dialog
import com.indupendo.globals.utilities.Utils.showLoginCancellationSnackBar
import android.os.Bundle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.indupendo.R
import android.content.DialogInterface
import android.view.View
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.indupendo.databinding.DialogLoginBinding

class LoginDialog(var view: View) : DialogFragment() {
    var binding: DialogLoginBinding? = null
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        binding = DialogLoginBinding.inflate(layoutInflater)
        val dialogBuilder = MaterialAlertDialogBuilder(
            requireActivity(),
            R.style.MaterialAlertDialog_rounded
        )
        dialogBuilder.setView(binding!!.getRoot())
        dialogBuilder.setNegativeButton("Cancel") { dialog: DialogInterface, which: Int ->
            showLoginCancellationSnackBar(
                view, layoutInflater
            )
            dialog.cancel()
        }
        dialogBuilder.setPositiveButton("Login") { dialog: DialogInterface?, which: Int ->
            Toast.makeText(
                activity,
                "Logged In",
                Toast.LENGTH_LONG
            ).show()
        }
        return dialogBuilder.create()
    }

    override fun onCancel(dialog: DialogInterface) {
        super.onCancel(dialog)
        showLoginCancellationSnackBar(view, layoutInflater)
        dialog.cancel()
    }
}

我怎样才能重写Kotlin类并获得相同的结果而不出现这个bug呢?

yqyhoc1h

yqyhoc1h1#

当您使用具有如下属性的构造函数时

class LoginDialog(var propertyName: View)

Kotlin正在尝试为它们创建getter和setter,它们的名字是getPropertyName()setPropertyName()
在您的示例中,getter getView()与您的一个基类(www.example.com)中的方法冲突Fragment.java:

/**
 * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}),
 * if provided.
 *
 * @return The fragment's root view, or null if it has no layout.
 */
@Nullable
public View getView() {
    return mView;
}

您有几种方法可以修复此错误:
1.只需更改属性名称

class LoginDialog(var myView: View)

1.将属性初始化替换为简单参数并手动初始化属性

class LoginDialog(view: View) : DialogFragment() {
var myView: View = view
    get() = field
    set(value) {
        field = value
    }

1.用@JvmField注解来注解你的属性。这将指示Kotlin编译器不为这个属性生成getter/setter,并将它公开为一个字段

class LoginDialog(@JvmField internal val view: View) : DialogFragment() {

有点跑题了:
虽然上面的选项可以解决这个错误,但是这个类还有一个更大的问题--向Fragment构造函数发送一个参数是一个相当危险的决定。
Fragment的所有子类都必须包含一个公共的无参数构造函数。框架经常会在需要的时候重新示例化一个fragment类,特别是在状态恢复的时候,并且需要能够找到这个构造函数来示例化它。如果无参数构造函数不可用,在状态恢复的某些情况下会发生运行时异常。
您可以关注此thread了解更多详细信息

ne5o7dgx

ne5o7dgx2#

我考虑了@DmitryArc上面的回答,并想到了一个更安全的解决方案,遵循他们的**PS:“Fragment的所有子类都必须包含一个公共的无参数构造函数。"**不必传递构造函数参数,一种解决方法是必须通过requireActivity()函数和findViewById(...)访问托管Activity的视图,在我的示例中是一个按钮。

package com.indupendo.landing.ui.dialogs

import android.app.Dialog
import com.indupendo.globals.utilities.Utils.showLoginCancellationSnackBar
import android.os.Bundle
import com.indupendo.R
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import android.content.DialogInterface
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.indupendo.databinding.DialogLoginBinding

class LoginDialog : DialogFragment() {
    var binding: DialogLoginBinding? = null
    private var snackBarView: Button? = null // This is required by snackBar as an argument

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        snackBarView = requireActivity().findViewById<View>(R.id.btn_login) as Button
        binding = DialogLoginBinding.inflate(layoutInflater)
        val dialogBuilder = MaterialAlertDialogBuilder(
            requireActivity(),
            R.style.MaterialAlertDialog_rounded
        )
        dialogBuilder.setView(binding!!.getRoot())
        dialogBuilder.setNegativeButton("Cancel") { dialog: DialogInterface, _: Int ->
            showLoginCancellationSnackBar(
                snackBarView!!, layoutInflater
            )
            dialog.cancel()
        }
        dialogBuilder.setPositiveButton("Login") { _: DialogInterface?, _: Int ->
            Toast.makeText(
                activity,
                "Logged In",
                Toast.LENGTH_LONG
            ).show()
        }
        return dialogBuilder.create()
    }

    override fun onResume() {
        super.onResume()
        snackBarView = requireActivity().findViewById<View>(R.id.btn_login) as Button
    }

    override fun onCancel(dialog: DialogInterface) {
        super.onCancel(dialog)
        showLoginCancellationSnackBar(snackBarView!!, layoutInflater)
        dialog.cancel()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
        snackBarView = null
    }
}

PS:欢迎任何人改进这个答案!

相关问题