Verwendung der Datenbindung mit Fragment


182

Ich versuche, dem Beispiel für Datenbindung aus dem offiziellen Google Doc zu folgen https://developer.android.com/tools/data-binding/guide.html

außer dass ich versuche, Daten-Biding auf ein Fragment anzuwenden, nicht auf eine Aktivität.

Der Fehler, den ich derzeit beim Kompilieren erhalte, ist

Error:(37, 27) No resource type specified (at 'text' with value '@{marsdata.martianSols}.

onCreate für Fragment sieht so aus:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MartianDataBinding binding = MartianDataBinding.inflate(getActivity().getLayoutInflater());
    binding.setMarsdata(this);
}

onCreateView für Fragment sieht so aus:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.martian_data, container, false);
}

und Teile meiner Layoutdatei für Fragment sehen folgendermaßen aus:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="marsdata"
            type="uk.co.darkruby.app.myapp.MarsDataProvider" />
    </data>
...

        <TextView
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:text="@{marsdata.martianSols}"
        />

    </RelativeLayout>
</layout>

Mein Verdacht ist, dass MartianDataBindingich nicht weiß, an welche Layoutdatei es gebunden werden soll - daher der Fehler. Irgendwelche Vorschläge?

Antworten:


352

Die Implementierung der Datenbindung muss in der onCreateViewMethode des Fragments erfolgen. Löschen Sie alle in Ihrer OnCreateMethode vorhandenen Datenbindungen. Sie onCreateViewsollten folgendermaßen aussehen:

public View onCreateView(LayoutInflater inflater, 
                         @Nullable ViewGroup container, 
                         @Nullable Bundle savedInstanceState) {
    MartianDataBinding binding = DataBindingUtil.inflate(
            inflater, R.layout.martian_data, container, false);
    View view = binding.getRoot();
    //here data must be an instance of the class MarsDataProvider
    binding.setMarsdata(data);
    return view;
}

Ich musste den Aufruf zu super hinzufügen, damit meine Bindungsklasse generiert wurde.
joey_g216

3
Ich habe stundenlang mit diesem Problem zu kämpfen. Das Problem war, dass ich die falsche Ansicht zurückgegeben habe. +1
TharakaNirmana

1
View view = binding.getRoot(); Ich habe so lange daran festgehalten, dass ich zu Recht ziemlich verärgert bin, dass ich auf developer.android.com keine Dokumentation darüber finden konnte ... Das Problem wurde behoben. Danke dir!
Victor Ude

1
Wenn Sie LiveData und ViewModel verwenden, lesen Sie diese Antwort .
Big McLargeHuge

1
Was ist setMarsdata ()? Ich denke hier verwenden wir setViewModel ()?
Suv

59

Sie werden tatsächlich aufgefordert, die inflateMethode Ihrer generierten Bindung und nicht die DataBindingUtil zu verwenden:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    MainFragmentBinding binding = MainFragmentBinding.inflate(inflater, container, false);
    //set variables in Binding
    return binding.getRoot();
}

Dokumente für DataBindingUtil.inflate () :

Verwenden Sie diese Version nur, wenn layoutId im Voraus unbekannt ist. Verwenden Sie andernfalls die Aufblasmethode der generierten Bindung, um ein typsicheres Aufblasen zu gewährleisten.


Leider bringt mich das mit dem cannot be resolved to a typeFehler beim Erstellen um. Es ist meiner Meinung nach nicht zuverlässig. Wenn ich zuerst mit gehe DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);und es dann ändere FragmentCameraBinding.inflate(inflater, container, false);, funktioniert es, aber nach dem Wiederherstellen gibt es den Fehler erneut.
Alex Burdusel

Funktioniert super. Eigentlich muss keine Layout-Res-ID angegeben werden (was ich mich vorher gefragt habe), da diese automatisch aus der generierten Bindungsdatei ausgewählt wird.
eC Droid

2
Wo legen Sie in diesem Beispiel die Fragment-Layout-ID (z. B. R.layout.fragment_) fest?
Lenin Raj Rajasekaran

Dies sollte die akzeptierte Antwort sein. Es wird DataBindingUtil.inflate
empfohlen,

@LeninRajRajasekaran Die Layout-ID wird durch die Verwendung der MainFragmentBindingKlasse impliziert . Diese Klasse wird aus der Layoutdatei generiert, sodass das gewünschte Layout automatisch angewendet wird.
Emil S.

19

Sogar die anderen Antworten mögen gut funktionieren, aber ich möchte den besten Ansatz nennen.

Verwendung Binding class's inflatewie in der Android-Dokumentation empfohlen .

Eine Möglichkeit ist das Aufblasen, DataBindingUtil aber wenn nur Sie nicht wissen, dass Sie eine Bindungsklasse generiert haben .

- Sie haben automatisch generiert binding class, verwenden Sie diese Klasse anstelle von DataBindingUtil.

In Java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    HomeFragmentBinding binding = HomeFragmentBinding.inflate(inflater, container, false);
    //set binding variables here
    return binding.getRoot();
}

In Kotlin

lateinit var binding: HomeFragmentBinding 
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = HomeFragmentBinding.inflate(inflater, container, false)
    return binding.root
}

In der Dokumentation zur DataBindingUtil- Klasse sehen Sie.

aufblasen

T inflate (LayoutInflater inflater, 
                int layoutId, 
                ViewGroup parent, 
                boolean attachToParent)

Verwenden Sie diese Version nur, wenn layoutId im Voraus unbekannt ist. Verwenden Sie andernfalls die Aufblasmethode der generierten Bindung, um ein typsicheres Aufblasen zu gewährleisten.

Wenn Ihre Layout-Biniding-Klasse nicht generiert wird, lesen Sie diese Antwort .


Warum nicht die inflateMethode verwenden, die das LayoutInflaterals einziges Argument verwendet?
Florian Walther

@FlorianWalther funktioniert das ohne ViewGroup container?
Khemraj

Nun, ich wusste nicht, wann ich diesen Kommentar schrieb. Aber ich habe hier eine gute Antwort bekommen: stackoverflow.com/questions/61571381/…
Florian Walther

1
@FlorianWalther okay, ich habe die Antwort gelesen, die containerbenötigt wird, wenn es attachToRootist true.
Khemraj

16

Wenn Sie ViewModel und LiveData verwenden Dies ist die ausreichende Syntax

Kotlin-Syntax:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return MartianDataBinding.inflate(
        inflater,
        container,
        false
    ).apply {
        lifecycleOwner = viewLifecycleOwner
        vm = viewModel    // Attach your view model here
    }.root
}

10

Versuchen Sie dies in Android DataBinding

FragmentMainBinding binding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false);
        View rootView = binding.getRoot();
        initInstances(savedInstanceState);
        return rootView;
}

7

Man kann einfach das Ansichtsobjekt wie unten erwähnt abrufen

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = DataBindingUtil.inflate(inflater, R.layout.layout_file, container, false).getRoot();

return view;

}

7

Kotlin-Syntax:

lateinit var binding: MartianDataBinding
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = DataBindingUtil.inflate(inflater, R.layout.martian_data, container, false)
    return binding.root
}

6

Wie die meisten gesagt haben, aber vergessen Sie nicht, LifeCycleOwner
Sample in Java festzulegen, dh

public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    BindingClass binding = DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false);
    ModelClass model = ViewModelProviders.of(getActivity()).get(ViewModelClass.class);
    binding.setLifecycleOwner(getActivity());
    binding.setViewmodelclass(model);

    //Your codes here

    return binding.getRoot();
}

5

in meinem Code arbeiten.

private FragmentSampleBinding dataBiding;
private SampleListAdapter mAdapter;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    dataBiding = DataBindingUtil.inflate(inflater, R.layout.fragment_sample, null, false);
    return mView = dataBiding.getRoot();
}

5

Ein vollständiges Beispiel für die Datenbindung von Fragmenten

FragmentMyProgramsBinding ist eine Bindungsklasse, die für res / layout / fragment_my_programs generiert wurde

public class MyPrograms extends Fragment {
    FragmentMyProgramsBinding fragmentMyProgramsBinding;

    public MyPrograms() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
    FragmentMyProgramsBinding    fragmentMyProgramsBinding = DataBindingUtil.inflate(inflater, R
                .layout.fragment_my_programs, container, false);
        return fragmentMyProgramsBinding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    }
}

2

Sehr hilfreicher Blog über Datenbindung: https://link.medium.com/HQY2VizKO1

class FragmentBinding<out T : ViewDataBinding>(
    @LayoutRes private val resId: Int
) : ReadOnlyProperty<Fragment, T> {

    private var binding: T? = null

    override operator fun getValue(
        thisRef: Fragment,
        property: KProperty<*>
    ): T = binding ?: createBinding(thisRef).also { binding = it }

    private fun createBinding(
        activity: Fragment
    ): T = DataBindingUtil.inflate(LayoutInflater.from(activity.context),resId,null,true)
}

Deklarieren Sie den Bindungswert wie folgt in Fragment:

private val binding by FragmentBinding<FragmentLoginBinding>(R.layout.fragment_login)

Vergessen Sie nicht, dies in Fragmenten zu schreiben

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return binding.root
}

1

Ein weiteres Beispiel in Kotlin:

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding = DataBindingUtil
            .inflate< MartianDataBinding >(
                    inflater,
                    R.layout.bla,
                    container,
                    false
            )

    binding.modelName = // ..

    return binding.root
}

Beachten Sie, dass der Name "MartianDataBinding" vom Namen der Layoutdatei abhängt. Wenn die Datei "martian_data" heißt, lautet der korrekte Name MartianDataBinding.


0

Jeder sagt darüber inflate(), aber was ist, wenn wir es verwenden wollen onViewCreated()?

Sie können die bind(view)Methode der konkreten Bindungsklasse verwenden, um die ViewDataBindingInstanz für die zu erhalten view.


Normalerweise schreiben wir BaseFragment wie folgt (vereinfacht):

// BaseFragment.kt
abstract fun layoutId(): Int

override fun onCreateView(inflater, container, savedInstanceState) = 
    inflater.inflate(layoutId(), container, false)

Und verwenden Sie es in untergeordneten Fragmenten.

// ConcreteFragment.kt
override fun layoutId() = R.layout.fragment_concrete

override fun onViewCreated(view, savedInstanceState) {
    val binding = FragmentConcreteBinding.bind(view)
    // or
    val binding = DataBindingUtil.bind<FragmentConcreteBinding>(view)
}


Wenn alle Fragmente Datenbindung verwenden, können Sie dies mithilfe des Typparameters sogar vereinfachen.

abstract class BaseFragment<B: ViewDataBinding> : Fragment() {
    abstract fun onViewCreated(binding: B, savedInstanceState: Bundle?)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        onViewCreated(DataBindingUtil.bind<B>(view)!!, savedInstanceState)
    }
}

Ich weiß nicht, ob es in Ordnung ist, dort Nicht-Null zu behaupten, aber ... Sie haben die Idee. Wenn Sie möchten, dass es nullbar ist, können Sie es tun.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.