full developer custom create android kotlin kotlin-android-extensions

android - developer - Kotlin diseño sintético y personalizado en DialogFragment



dialogfragment android kotlin (9)

Digamos que tengo este diseño:

<?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>

Y lo uso en un 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() } }

Si me refiero al widget utilizando sintético:

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

Tengo este error en el tiempo de ejecución:

java.lang.NullPointerException: Intente invocar el método virtual ''android.view.View android.view.View.findViewById (int)'' en una referencia de objeto nula en MyDialog ._ $ _ findCachedViewById (DepartmentChoiceDialog.kt: 0)

No entiendo cómo usar sintéticos en el caso DialogFragment. Funciona bien en Fragmento y Actividad.


Así que no estoy seguro de si esto ha sido resuelto ... Acabo de encontrar esto. Si tiene una vista de diálogo personalizada, cree una clase que amplíe DialogFragment y use el objeto "diálogo" para importar vistas en el diseño. Estoy usando Android Studio 3.1.3 y Kotlin version 1.2.41 al momento de escribir.

import kotlinx.android.synthetic.main.your_custom_layout.* class SelectCountryBottomSheet : BottomSheetDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog dialog.setContentView(R.layout.your_custom_layout) dialog.some_custom_close_button.setOnClickListener { dismiss() } return dialog } }


Cambio a la implementación de onCreateView

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

y use un título personalizado (TextView) y cancele (Botón) en department_chooser_dialog

onActivityCreated se ejecutará después de onCreateView y estará bien.

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


Debido a que el valor de la vista predeterminada del fragmento (método kotlin genera _ $ _ findCachedViewById), pero si creamos la Vista desde el cuadro de diálogo, la vista del fragmento es nula, por lo que no podemos usar directamente el valor predeterminado xxx, pero podemos usar dialog.xxx reemplazar el valor predeterminado xxx


El código kotlin en un fragmento como este:

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 } } }

Después de descompilar el ((Button)this._$_findCachedViewById(id.button1)) , puede ver la implementación de las propiedades sintéticas: ((Button)this._$_findCachedViewById(id.button1)) y el método _$_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;

}

así que la magia es solo el this.getView() . La propiedad Fragment.mView se asigna después de Fragment.onCreateView(inflater, container, savedInstanceState) , si utiliza las propiedades sintéticas de Kotlin en el método onCreateView (), habrá un NPE. Código de FragmentManager.moveToState() :

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

Para arreglar el NPE, asegúrese de que el método getView devuelva una vista no nula.

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 }

y en la devolución de llamada del ciclo de vida de onDestroyView() , establezca mContentView en nulo.

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


Encontré una forma que funciona para diálogos personalizados.

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 } }) } }


La respuesta anterior no funcionará, porque no se llama a onViewCreated cuando usa onCreateDialog. Primero debe importar kotlinx ... department_chooser_dialog. ver .dep_list, y luego usarlo de la siguiente manera:

import kotlinx.android.synthetic.main.department_chooser_dialog.view.dep_list ... class DepartmentChoiceDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(activity) val dialog = inflater.inflate(R.layout.department_chooser_dialog, null) dialog.dep_list.layoutManager = LinearLayoutManager(activity) dialog.dep_list.itemAnimator = DefaultItemAnimator() dialog.dep_list.setHasFixedSize(true) builder.setTitle(R.string.choose_or_create_dep) .setView(dialog) ...


Mueva su código de onActivityCreated al método onViewCreated . Me gusta esto:

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) } }

En realidad no miré más profundamente el código generado y tal vez hay un error.


Parece que esto no está soportado por defecto todavía, pero he encontrado que la forma más fácil de hacerlo es así. En una clase de diálogo base:

protected abstract val containerView: View override fun getView() = containerView

En una subclase:

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

Luego, puede usar las vistas sintéticas como lo haría normalmente y usar containerView como la vista para su diálogo.


Se puede acceder a las vistas a través de la vista que infla en onCreateDialog . Por lo tanto, si guarda la vista en una variable ( rootView ), puede acceder a las vistas desde cualquier método dentro de 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 } }