Cara menggunakan pengikatan data dengan Fragment

182

Saya mencoba mengikuti contoh pengikatan data dari google doc resmi https://developer.android.com/tools/data-binding/guide.html

kecuali bahwa saya mencoba menerapkan pengikatan data ke fragmen, bukan aktivitas.

kesalahan yang saya dapatkan saat kompilasi adalah

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

onCreate untuk fragmen terlihat seperti ini:

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

onCreateView untuk fragmen terlihat seperti ini:

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

dan sebagian file tata letak saya untuk fragmen terlihat seperti ini:

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

kecurigaan saya adalah bahwa MartianDataBindingtidak tahu file tata letak mana yang seharusnya diikat - maka kesalahannya. Ada saran?

dark_ruby
sumber

Jawaban:

354

Implementasi pengikatan data harus dalam onCreateViewmetode fragmen, hapus semua data Binding yang ada dalam OnCreatemetode Anda, Anda onCreateViewakan terlihat seperti ini:

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;
}
hdioui abdeljalil
sumber
Saya harus menambahkan panggilan ke super agar kelas Binding saya menghasilkan.
joey_g216
3
Saya berjuang dengan masalah ini selama berjam-jam. Masalahnya adalah saya mengembalikan pandangan yang salah. +1
TharakaNirmana
1
View view = binding.getRoot(); Saya sudah terjebak dalam hal ini begitu lama sehingga saya secara sah cukup kesal karena saya tidak dapat menemukan dokumentasi tentang hal itu di developer.android.com ... Memecahkan masalah. Terima kasih!
Victor Ude
1
Jika Anda menggunakan LiveData dan ViewModel, pastikan untuk membaca jawaban ini .
Big McLargeHuge
1
apa itu setMarsdata ()? saya pikir di sini kita menggunakan setViewModel () ??
suv
59

Anda sebenarnya didorong untuk menggunakan inflatemetode Binding yang Anda hasilkan dan bukan 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();
}

Documents untuk DataBindingUtil.inflate () :

Gunakan versi ini hanya jika layoutId tidak diketahui sebelumnya. Jika tidak, gunakan metode penggembungan Binding yang dihasilkan untuk memastikan inflasi tipe-aman.

Sampai
sumber
Sayangnya ini membunuh saya dengan cannot be resolved to a typekesalahan saat dibangun. Itu tidak bisa diandalkan menurut saya. Jika saya pergi pertama dengan DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);dan kemudian mengubahnya FragmentCameraBinding.inflate(inflater, container, false);, itu berfungsi, tetapi setelah membangun kembali itu memberikan kesalahan lagi.
Alex Burdusel
Bagus sekali. Sebenarnya tidak perlu menentukan tata letak res id (yang saya ingin tahu sebelumnya) karena secara otomatis memilih dari file yang dihasilkan mengikat.
eC Droid
2
di mana Anda mengatur id tata letak fragmen (mis. R.layout.fragment_) dalam contoh ini?
Lenin Raj Rajasekaran
ini harus menjadi jawaban yang diterima. tata letak yang dihasilkan mengikat didorong untuk digunakan, bukannyaDataBindingUtil.inflate
mochadwi
@LeninRajRajasekaran ID tata letak tersirat melalui penggunaan MainFragmentBindingkelas. Kelas itu dihasilkan dari file tata letak sehingga tata letak yang diinginkan diterapkan secara otomatis.
Emil S.
19

Bahkan jawaban lain mungkin bekerja dengan baik, tetapi saya ingin memberi tahu pendekatan terbaik.

Gunakan Binding class's inflatesesuai anjuran dalam Dokumentasi Android .

Satu opsi adalah mengembang dengan DataBindingUtil tetapi ketika hanya Anda yang tidak tahu telah menghasilkan kelas mengikat .

--Anda memiliki hasil otomatis binding class, gunakan kelas itu alih-alih menggunakan DataBindingUtil.

Di jawa

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

Di Kotlin

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

Dalam dokumentasi kelas DataBindingUtil Anda dapat melihat.

memompa

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

Gunakan versi ini hanya jika layoutId tidak diketahui sebelumnya. Jika tidak, gunakan metode penggembungan Binding yang dihasilkan untuk memastikan inflasi tipe-aman.

Jika tata letak kelas biniding Anda tidak dibuat @Lihat jawaban ini .

Khemraj
sumber
mengapa tidak menggunakan inflatemetode yang menjadikan LayoutInflaterargumennya sebagai satu-satunya?
Florian Walther
@FlorianWalther apakah itu berhasil tanpa ViewGroup container?
Khemraj
Yah, saya tidak tahu kapan saya menulis komentar ini. Tapi saya mendapat jawaban yang bagus di sini: stackoverflow.com/questions/61571381/…
Florian Walther
1
@FlorianWalther oke, saya sudah membaca jawabannya, yang containerdiperlukan saat attachToRootini true.
Khemraj
16

Jika Anda menggunakan ViewModel dan LiveData Ini adalah sintaks yang memadai

Sintaks 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
}
Saman Sattari
sumber
10

Coba ini di 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;
}
Jirawat Harnsiriwatanakit
sumber
7

Seseorang dapat dengan mudah mengambil objek tampilan seperti yang disebutkan di bawah ini

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;

}
Imran Solanki - GSLab
sumber
7

Sintaks 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
}
muneikh
sumber
7

Seperti yang dikatakan sebagian besar orang, tetapi jangan lupa untuk mengatur Sampel LifeCycleOwner
di Jawa yaitu

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();
}
Orang kidal
sumber
5

bekerja di kode saya.

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();
}
UJWAL GHONGADE
sumber
5

Contoh lengkap dalam fragmen pengikatan data

FragmentMyProgramsBinding adalah kelas mengikat yang dihasilkan untuk 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);

    }
}
vivek yadav
sumber
2

Blog yang sangat membantu tentang Penyatuan Data: 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)
}

Nyatakan val yang mengikat seperti ini di Fragmen:

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

Jangan lupa untuk menulis ini dalam fragmen

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

Contoh lain di 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
}

Perhatikan bahwa nama "MartianDataBinding" tergantung pada nama file tata letak. Jika file tersebut bernama "martian_data" maka nama yang benar adalah MartianDataBinding.

akohout
sumber
0

Semua orang bilang inflate(), tapi bagaimana kalau kita ingin menggunakannya onViewCreated()?

Anda dapat menggunakan bind(view)metode kelas penjilidan konkret untuk mendapatkan ViewDataBindinginstance untuk view.


Biasanya kami menulis BaseFragment seperti ini (disederhanakan):

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

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

Dan gunakan dalam fragmen anak.

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


Jika semua Fragmen menggunakan pengikatan data, Anda bahkan dapat membuatnya lebih mudah menggunakan parameter tipe.

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

Saya tidak tahu tidak apa-apa untuk menyatakan bukan-nol di sana, tapi .. Anda mengerti. Jika Anda ingin dibatalkan, Anda dapat melakukannya.

Tura
sumber