DialogFragment中的Kotlin合成和自定义布局

时间:2015-12-27 23:11:29

标签: android kotlin kotlin-android-extensions

我们说我有这个布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ImageButton
    android:id="@+id/add_dep_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:layout_marginEnd="5dp"
    android:layout_marginRight="5dp"
    android:src="@android:drawable/ic_input_add" />

<EditText
    android:id="@+id/add_dep_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@id/add_dep_btn"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_alignTop="@id/add_dep_btn"
    android:layout_marginLeft="5dp"
    android:layout_marginStart="5dp"
    android:layout_toLeftOf="@id/add_dep_btn"
    android:layout_toStartOf="@id/add_dep_btn" />

<android.support.v7.widget.RecyclerView
    android:id="@+id/dep_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/add_dep_btn" />

<TextView
    android:id="@+id/empty_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/add_dep_text"
    android:layout_margin="20dp"
    android:gravity="center"
    android:text="@string/no_dep"
    android:textSize="22sp" />
</RelativeLayout>

我在DialogFragment中使用它:

class DepartmentChoiceDialog : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        builder.setTitle(R.string.choose_or_create_dep)
            .setView(R.layout.department_chooser_dialog)
            .setNegativeButton(android.R.string.cancel, { d, i ->
                d.cancel()
            })
        return builder.create()
    }
}

如果我使用合成来引用小部件:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    dep_list.layoutManager = LinearLayoutManager(activity)
    dep_list.itemAnimator = DefaultItemAnimator()
    dep_list.setHasFixedSize(true)
}

我在运行时遇到了这个错误:

  

java.lang.NullPointerException:尝试调用虚方法&#39; android.view.View android.view.View.findViewById(int)&#39;在null对象引用上                                                                           at MyDialog ._ $ _ findCachedViewById(DepartmentChoiceDialog.kt:0)

我不明白如何在DialogFragment案例中使用合成。它在Fragment和Activity中工作正常。

10 个答案:

答案 0 :(得分:9)

我找到了一种适用于自定义对话框的方法。

class ServerPickerDialogFragment: AppCompatDialogFragment() 
{
  // Save your custom view at the class level
  lateinit var customView: View;
  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                            savedInstanceState: Bundle?): View? 
  {
       // Simply return the already inflated custom view
       return customView
  }

  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
      // Inflate your view here
      customView = context!!.layoutInflater.inflate(R.layout.dialog_server_picker, null) 
      // Create Alert Dialog with your custom view
      return AlertDialog.Builder(context!!)
             .setTitle(R.string.server_picker_dialog_title)
             .setView(customView)
             .setNegativeButton(android.R.string.cancel, null)
             .create()
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
  {
    super.onViewCreated(view, savedInstanceState)
    // Perform remaining operations here. No null issues.
    rbgSelectType.setOnCheckedChangeListener({ _, checkedId ->
      if(checkedId == R.id.rbSelectFromList) {
             // XYZ
      } else {
             // ABC
      }
    })
  }
}

答案 1 :(得分:5)

默认情况下,这似乎不支持,但我发现最简单的方法就是这样。在基础对话框类中:

protected abstract val containerView: View

override fun getView() = containerView

在子类中:

override val containerView by unsafeLazy {
    View.inflate(context, R.layout.dialog_team_details, null) as ViewGroup
}

然后您可以像往常一样使用合成视图,并使用containerView作为对话框的视图。

答案 2 :(得分:3)

所以我不确定这是否已经解决了...我刚刚遇到过这个问题。如果您有自定义对话框视图,请创建一个扩展DialogFragment的类并使用&#34;对话框&#34;用于在布局中导入视图的对象。我在撰写本文时使用this._htmlClient.get('path', { responseType: 'text' }) .subscribe(data => { console.log(data) }); Android Studio 3.1.3

Kotlin version 1.2.41

答案 3 :(得分:2)

更改为 onCreateView 实施

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.department_chooser_dialog, container, false)
}

并在 department_chooser_dialog 中使用自定义标题(TextView)和取消(按钮)

onActivityCreated 将在onCreateView之后运行,并且会很好。

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    dep_list.layoutManager = LinearLayoutManager(activity)
    dep_list.itemAnimator = DefaultItemAnimator()
    dep_list.setHasFixedSize(true)
}

答案 4 :(得分:1)

将代码从onActivityCreated移至onViewCreated方法。 像这样:

import kotlinx.android.synthetic.main.department_chooser_dialog.dep_list

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    dep_list.apply {
        layoutManager = LinearLayoutManager(activity)
        itemAnimator = DefaultItemAnimator()
        setHasFixedSize(true)
    }
}

我实际上并没有深入研究生成的代码,也许还有一个错误。

答案 5 :(得分:1)

以前的答案不起作用,因为在使用onCreateDialog时不会调用onViewCreated。你应该首先导入kotlinx ... department_chooser_dialog。查看 .dep_list,然后按如下方式使用它:

mocha test.js --fgrep "Scenario 5"

答案 6 :(得分:1)

因为默认视图的值来自fragment(kotlin generate方法_ $ _ findCachedViewById),但是如果我们从对话框创建View,导致片段视图为null,所以我们不能直接使用默认的xxx,但我们可以使用对话框。 xxx替换默认xxx

答案 7 :(得分:1)

setContentView在OnActivityCreated调用内。因此,通过一组综合的控件来监视事件需要在此处调用:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.layout_email_ga_code, container)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
    super.onActivityCreated(savedInstanceState)
    dialog?.window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)

    btn_back?.setOnClickListener {
        mOnClickListener?.onClickCancel()
        dismiss()
    }
}

它奏效了。

答案 8 :(得分:0)

片段中的kotlin代码如下:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.your_layout, container, false).apply {
        mContentView = this
        button1.setOnClickListener { 
            //do something
        }
    }
}

反编译字节码后,可以看到合成属性的实现: ((Button)this._$_findCachedViewById(id.button1)) _$_findCachedViewById方法:

public View _$_findCachedViewById(int var1) {
  if (this._$_findViewCache == null) {
     this._$_findViewCache = new HashMap();
  }

  View var2 = (View)this._$_findViewCache.get(var1);
  if (var2 == null) {
     View var10000 = this.getView();
     if (var10000 == null) {
        return null;
     }

     var2 = var10000.findViewById(var1);
     this._$_findViewCache.put(var1, var2);
  }

  return var2;

}

所以魔术只是this.getView() Fragment.mView属性是在Fragment.onCreateView(inflater, container, savedInstanceState)之后分配的。,如果在onCreateView()方法中使用Kotlin综合属性,则会有一个NPE。来自FragmentManager.moveToState()的代码:

case Fragment.CREATED:
    ...
    f.mView = f.performCreateView(f.performGetLayoutInflater(
                                f.mSavedFragmentState), container, 
    f.mSavedFragmentState);
    ...

要修复NPE,请确保getView方法返回非空视图。

private var mContentView: View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.your_layout, container, false).apply {
        mContentView = this
    }
}
override fun getView(): View? {
    return mContentView
}

,并在onDestroyView()生命周期回调中,将mContentView设置为null。

override fun onDestroyView() {
    super.onDestroyView()
    mView = null
}

答案 9 :(得分:-1)

可以通过您在onCreateDialog中充气的视图访问这些视图。因此,如果您将视图保存在变量(rootView)中,则可以从YourDialogFragment内的任何方法访问视图。

// ...
import kotlinx.android.synthetic.main.your_layout.view.*

class YourDialogFragment : DialogFragment() {

    private lateinit var rootView: View

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

        rootView = activity.layoutInflater.inflate(R.layout.your_layout, null as ViewGroup?)

        rootView.someTextView.text = "Hello" // works
    }
}