Як використовувати зв'язування даних з Fragment


182

Я намагаюся наслідувати приклад зв’язування даних з офіційного google doc https://developer.android.com/tools/data-binding/guide.html

окрім того, що я намагаюся застосувати встановлення ставок даних до фрагменту, а не до діяльності.

помилка, яку я отримую в даний час при компілюванні, є

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

onCreate для фрагмента виглядає так:

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

onCreateView для фрагмента виглядає так:

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

і частини мого файлу компонування для фрагмента виглядають так:

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

я підозрюю, що MartianDataBindingне знає, з яким файлом компонування він повинен бути пов'язаний - отже, помилка. Будь-які пропозиції?

Відповіді:


354

Реалізація прив'язки даних повинна бути в onCreateViewметоді фрагмента, видалити будь-які прив'язки даних, які існують у вашому OnCreateметоді, ви onCreateViewповинні виглядати так:

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

Мені довелося додати виклик до супер, щоб створити мій клас прив'язки.
joey_g216

3
Я боюся з цим питанням годинами. Проблема полягала в тому, що я повернув неправильний погляд. +1
TharakaNirmana

1
View view = binding.getRoot(); Я був затриманий на цьому так довго, що я законно дуже засмучений, що не міг знайти жодної документації про це на developer.android.com ... Вирішив проблему. Дякую!
Віктор Уде

1
Якщо ви використовуєте LiveData та ViewModel, обов'язково прочитайте цю відповідь .
Big McLargeHuge

1
що встановленоMarsdata ()? я думаю, тут ми використовуємо setViewModel () ??
сув

59

Насправді вам рекомендується використовувати inflateметод створеного прив'язки, а не 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();
}

Документи для DataBindingUtil.inflate () :

Використовуйте цю версію лише в тому випадку, якщо layoutId заздалегідь невідомий. В іншому випадку використовуйте створений метод роздування Binding для забезпечення безпечної інфляції.


На жаль, це вбиває мене з cannot be resolved to a typeпомилкою при складанні. На мою думку це не є надійним. Якщо я перейду спочатку, DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);а потім поміняю його FragmentCameraBinding.inflate(inflater, container, false);, він працює, але після відновлення він знову видає помилку.
Алекс Бурдузель

Чудово працює. Насправді не потрібно вказувати ідентифікатор res ID (який мені було цікаво раніше), оскільки він автоматично вибирається із створеного файлу прив’язки.
eC Droid

2
де ви встановлюєте ідентифікатор компонування фрагмента (наприклад, R.layout.fragment_) у цьому прикладі?
Ленін Радж Раджасекаран

це має бути прийнятою відповіддю. макет, що генерується, пов'язаний рекомендується використовувати замістьDataBindingUtil.inflate
mochadwi

@LeninRajRajasekaran Ідентифікатор макета мається на увазі через використання MainFragmentBindingкласу. Цей клас генерується з файлу макета, тому бажаний макет автоматично застосовується.
Еміль С.

19

Навіть інші відповіді можуть спрацювати добре, але я хочу сказати найкращий підхід.

Використовуйте, Binding class's inflateяк рекомендовано в Документації на Android .

Один з варіантів - надути до, DataBindingUtil але коли тільки ви не знаєте, що створили клас зв'язування .

--Ви створили автоматичне binding classвикористання, використовуйте цей клас замість DataBindingUtil.

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

У Котліні

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

У документації класу DataBindingUtil ви можете побачити.

надути

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

Використовуйте цю версію лише в тому випадку, якщо layoutId заздалегідь невідомий. В іншому випадку використовуйте створений метод роздування Binding для забезпечення безпечної інфляції.

Якщо ваш клас встановлення бінінгу не створено, @See це відповідь .


чому б не використовувати inflateметод, який вважає LayoutInflaterєдиним аргументом?
Флоріан Уолтер

@FlorianWalther це працює без ViewGroup container?
Хемрай

Ну, я не знав, коли писав цей коментар. Але я отримав тут добру відповідь: stackoverflow.com/questions/61571381/…
Флоріан Уолтер

1
@FlorianWalther добре, я прочитав відповідь, яка containerпотрібна, коли attachToRootє true.
Хемрай

16

Якщо ви використовуєте ViewModel та LiveData, це достатній синтаксис

Синтаксис Котліна:

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

Спробуйте це в 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

Можна просто отримати об’єкт перегляду, як зазначено нижче

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

Синтаксис Котліна:

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

Як і більшість з них сказали, але не забудьте встановити зразок LifeCycleOwner
в Java, тобто

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

працює в моєму коді.

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

Повний приклад у фрагментах зв’язування даних

FragmentMyProgramsBinding - клас прив'язки, згенерований для 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

Дуже корисний блог про прив'язку даних: 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)
}

Оголосити такий прив'язковий val у фрагменті:

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

Не забудьте написати це фрагментом

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

1

Ще один приклад у Котліні:

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
}

Зауважте, що назва "MartianDataBinding" залежить від імені файлу макета. Якщо файл буде названий "martian_data", то правильне ім'я буде MartianDataBinding.


0

Усі говорять про те inflate(), але що робити, якщо ми хочемо це використати onViewCreated()?

Ви можете використовувати bind(view)метод конкретного класу зв'язування, щоб отримати ViewDataBindingекземпляр для view.


Зазвичай ми пишемо BaseFragment щось подібне (спрощено):

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

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

І використовувати його в дитячому фрагменті.

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


Якщо всі фрагменти використовують прив'язку даних, ви навіть можете спростити його за допомогою параметра типу.

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

Я не знаю, що це нормально стверджувати ненулеві, але .. ви розумієте. Якщо ви хочете, щоб це було незмінним, ви можете це зробити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.