Comment utiliser la liaison de données avec Fragment


182

J'essaie de suivre l'exemple de liaison de données du document officiel google https://developer.android.com/tools/data-binding/guide.html

sauf que j'essaye d'appliquer la soumission de données à un fragment, pas à une activité.

l'erreur que j'obtiens actuellement lors de la compilation est

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

onCreate pour fragment ressemble à ceci:

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

onCreateView pour fragment ressemble à ceci:

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

et certaines parties de mon fichier de mise en page pour fragment ressemblent à ceci:

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

mon soupçon est que MartianDataBindingne sait pas avec quel fichier de mise en page il est censé être lié - d'où l'erreur. Aucune suggestion?

Réponses:


354

L'implémentation de la liaison de données doit être dans la onCreateViewméthode du fragment, supprimez toutes les liaisons de données qui existent dans votre OnCreateméthode, vous onCreateViewdevriez ressembler à ceci:

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

J'ai dû ajouter l'appel à super pour que ma classe Binding soit générée.
joey_g216

3
Je lutte avec ce problème pendant des heures. Le problème était que je retournais la mauvaise vue. +1
TharakaNirmana

1
View view = binding.getRoot(); Je suis coincé là-dessus depuis si longtemps que je suis légitimement assez contrarié de ne pas trouver de documentation à ce sujet sur developer.android.com ... Résolution du problème. Je vous remercie!
Victor Ude

1
Si vous utilisez LiveData et ViewModel, assurez-vous de lire cette réponse .
Big McLarge Énorme

1
qu'est-ce que setMarsdata ()? je pense qu'ici nous utilisons setViewModel () ??
suv

59

Vous êtes en fait encouragé à utiliser la inflateméthode de votre liaison générée et non le 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();
}

Docs pour DataBindingUtil.inflate () :

Utilisez cette version uniquement si layoutId est inconnu à l'avance. Sinon, utilisez la méthode de gonflage de la liaison générée pour assurer un gonflage sans risque.


Malheureusement, cela me tue avec l' cannot be resolved to a typeerreur de construction. Ce n'est pas fiable à mon avis. Si je vais d'abord avec DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);et que je le change ensuite FragmentCameraBinding.inflate(inflater, container, false);, cela fonctionne, mais après la reconstruction, cela donne à nouveau l'erreur.
Alex Burdusel

Fonctionne très bien. En fait, pas besoin de spécifier l'ID de la mise en page (ce que je me demandais auparavant) car il sélectionne automatiquement le fichier de liaison généré.
eC Droid

2
où définissez-vous l'ID de mise en page du fragment (par exemple, R.layout.fragment_) dans cet exemple?
Lenin Raj Rajasekaran

cela devrait être la réponse acceptée. la liaison générée par la mise en page est encouragée à être utilisée, au lieu deDataBindingUtil.inflate
mochadwi

@LeninRajRajasekaran L'identifiant de mise en page est implicite via l'utilisation de la MainFragmentBindingclasse. Cette classe est générée à partir du fichier de mise en page afin que la mise en page souhaitée soit automatiquement appliquée.
Emil S.

19

Même les autres réponses peuvent bien fonctionner, mais je veux dire la meilleure approche.

Utilisez Binding class's inflatecomme recommandé dans la documentation Android .

Une option consiste à gonfler DataBindingUtil mais lorsque vous ne savez pas avoir généré la classe de liaison .

- Vous avez généré automatiquement binding class, utilisez cette classe au lieu d'utiliser DataBindingUtil.

En 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();
}

À Kotlin

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

Dans la documentation de la classe DataBindingUtil , vous pouvez voir.

gonfler

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

Utilisez cette version uniquement si layoutId est inconnu à l'avance. Sinon, utilisez la méthode de gonflage de la liaison générée pour assurer un gonflage sans risque.

Si votre classe de mise en page n'est pas générée @Voir cette réponse .


pourquoi ne pas utiliser la inflateméthode qui prend le LayoutInflatercomme seul argument?
Florian Walther

@FlorianWalther est-ce que ça marche sans ViewGroup container?
Khemraj

Eh bien, je ne savais pas quand j'ai écrit ce commentaire. Mais j'ai une bonne réponse ici: stackoverflow.com/questions/61571381/…
Florian Walther

1
@FlorianWalther d'accord, j'ai lu la réponse, c'est containernécessaire quand attachToRootc'est true.
Khemraj

16

Si vous utilisez ViewModel et LiveData C'est la syntaxe suffisante

Syntaxe de 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

Essayez ceci dans 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

On peut simplement récupérer l'objet de vue comme mentionné ci-dessous

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

Syntaxe de 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

Tout comme la plupart l'ont dit, mais n'oubliez pas de définir LifeCycleOwner
Sample en 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

travaillant dans mon code.

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 exemple complet de fragments de liaison de données

FragmentMyProgramsBinding est une classe de liaison générée pour 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 très utile sur la liaison de données: 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)
}

Déclarez la valeur de liaison comme ceci dans Fragment:

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

N'oubliez pas d'écrire ceci en fragment

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

1

Un autre exemple à 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
}

Notez que le nom "MartianDataBinding" dépend du nom du fichier de mise en page. Si le fichier est nommé "martian_data" alors le nom correct serait MartianDataBinding.


0

Tout le monde en parle inflate(), mais que faire si nous voulons l'utiliser onViewCreated()?

Vous pouvez utiliser la bind(view)méthode de la classe de liaison concrète pour obtenir l' ViewDataBindinginstance du view.


Habituellement, nous écrivons BaseFragment quelque chose comme ceci (simplifié):

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

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

Et utilisez-le dans un fragment enfant.

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


Si tous les fragments utilisent la liaison de données, vous pouvez même la simplifier en utilisant le paramètre de 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)
    }
}

Je ne sais pas qu'il est correct d'affirmer une valeur non nulle ici, mais ... vous voyez l'idée. Si vous voulez qu'il soit nullable, vous pouvez le faire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.