Come utilizzare l'associazione dati con Fragment


182

Sto cercando di seguire l'esempio di associazione dei dati dal documento ufficiale di Google https://developer.android.com/tools/data-binding/guide.html

tranne che sto cercando di applicare la raccolta dati a un frammento, non a un'attività.

l'errore che sto attualmente ricevendo durante la compilazione è

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

onCreate per frammento si presenta così:

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

onCreateView per frammento si presenta così:

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

e parti del mio file di layout per frammento si presentano così:

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

il mio sospetto è che MartianDataBindingnon sappia con quale file di layout debba essere associato, quindi l'errore. Eventuali suggerimenti?

Risposte:


354

L'implementazione onCreateViewdell'associazione dei dati deve essere nel metodo del frammento, eliminare qualsiasi associazione di dati esistente nel OnCreatemetodo, si onCreateViewdovrebbe assomigliare a questo:

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

Ho dovuto aggiungere la chiamata a super per generare la mia classe Binding.
joey_g216,

3
Ho problemi con questo problema per ore. Il problema era che avevo restituito la visione sbagliata. +1
Tharaka Nirmana,

1
View view = binding.getRoot(); Sono stato bloccato per così tanto tempo che sono legittimamente piuttosto arrabbiato che non sono riuscito a trovare alcuna documentazione al riguardo su developer.android.com ... Risolto il problema. Grazie!
Victor Ude

1
Se stai utilizzando LiveData e ViewModel, assicurati di leggere questa risposta .
Big McLarge: enorme

1
che cos'è setMarsdata ()? penso che qui usiamo setViewModel () ??
suv

59

In realtà sei incoraggiato a utilizzare il inflatemetodo del tuo Binding generato e non il DataBindingUtil:

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

Documenti per DataBindingUtil.inflate () :

Utilizzare questa versione solo se layoutId è sconosciuto in anticipo. In caso contrario, utilizzare il metodo di gonfiaggio del Binding generato per garantire un gonfiaggio sicuro.


Purtroppo questo mi sta uccidendo con l' cannot be resolved to a typeerrore di compilazione. Non è affidabile secondo me. Se vado prima con DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);e poi lo cambio FragmentCameraBinding.inflate(inflater, container, false);, funziona, ma dopo la ricostruzione dà di nuovo l'errore.
Alex Burdusel,

Funziona alla grande. In realtà non è necessario specificare il layout res id (cosa che mi stavo chiedendo prima) in quanto preleva automaticamente dal file di bind generato.
eC Droid,

2
dove imposti l'ID del layout del frammento (es. R.layout.fragment_) in questo esempio?
Lenin Raj Rajasekaran,

questa dovrebbe essere la risposta accettata. si consiglia di utilizzare l'associazione generata dal layout, anzichéDataBindingUtil.inflate
mochadwi il

@LeninRajRajasekaran L'ID layout è implicito attraverso l'uso della MainFragmentBindingclasse. Quella classe viene generata dal file di layout in modo che il layout desiderato venga applicato automaticamente.
Emil S.

19

Anche le altre risposte potrebbero funzionare bene, ma voglio dire l'approccio migliore.

Utilizzare Binding class's inflatecome raccomandato nella documentazione di Android .

Un'opzione è quella di gonfiare, DataBindingUtil ma quando solo tu non sai che hai generato la classe di associazione .

- Hai generato automaticamente binding class, usa quella classe invece di usare 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();
}

A Kotlin

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

Nella documentazione della classe DataBindingUtil puoi vedere.

gonfiare

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

Utilizzare questa versione solo se layoutId è sconosciuto in anticipo. In caso contrario, utilizzare il metodo di gonfiaggio del Binding generato per garantire un gonfiaggio sicuro.

Se la classe di biniding del layout non viene generata @ Vedi questa risposta .


perché non usare il inflatemetodo che prende LayoutInflatercome unico argomento?
Florian Walther,

@FlorianWalther funziona senza ViewGroup container?
Khemraj

Bene, non sapevo quando ho scritto questo commento. Ma ho una buona risposta qui: stackoverflow.com/questions/61571381/…
Florian Walther

1
@FlorianWalther okay, ho letto la risposta, che containerè necessaria quando lo attachToRootè true.
Khemraj,

16

Se si utilizza ViewModel e LiveData Questa è la sintassi sufficiente

Sintassi di Kotlin:

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

Prova questo 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

Si può semplicemente recuperare l'oggetto vista come menzionato di seguito

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

Sintassi di Kotlin:

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
}

7

Proprio come molti hanno già detto, ma non dimenticare di impostare LifeCycleOwner
Sample in Java ie

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

lavorando nel mio codice.

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

Un esempio completo di frammenti di associazione dati

FragmentMyProgramsBinding è una classe di legame generata per res / layout / fragment_my_programs

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

Blog molto utile su Databinding: 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)
}

Dichiarare val vincolante come questo in Frammento:

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

Non dimenticare di scriverlo in frammenti

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

1

Un altro esempio 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
}

Si noti che il nome "MartianDataBinding" dipende dal nome del file di layout. Se il file si chiama "martian_data", il nome corretto sarebbe MartianDataBinding.


0

Tutti ne parlano inflate(), ma cosa succede se vogliamo usarlo inonViewCreated() ?

È possibile utilizzare il bind(view)metodo della classe di associazione concreta per ottenere l' ViewDataBindingistanza per view.


Di solito scriviamo BaseFragment in questo modo (semplificato):

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

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

E usalo nel frammento del bambino.

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


Se tutti i frammenti utilizzano l'associazione dei dati, puoi persino renderlo più semplice utilizzando il parametro type.

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

Non so che va bene affermare non null lì, ma ... hai capito. Se vuoi che sia nulla, puoi farlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.