Cách sử dụng liên kết dữ liệu với Fragment


182

Tôi đang cố gắng làm theo ví dụ liên kết dữ liệu từ tài liệu chính thức của google https://developer.android.com/tools/data-binding/guide.html

ngoại trừ việc tôi đang cố gắng áp dụng dữ liệu vào một đoạn, không phải là một hoạt động.

lỗi tôi hiện đang gặp phải khi biên dịch là

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

onCreate cho mảnh vỡ trông như thế này:

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

onCreateView cho mảnh vỡ trông như thế này:

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

và các phần của tệp bố cục của tôi cho đoạn này trông như thế này:

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

nghi ngờ của tôi là MartianDataBindingkhông biết tập tin bố trí nào bị ràng buộc với - do đó là lỗi. Bất kỳ đề xuất?

Câu trả lời:


354

Việc thực hiện ràng buộc dữ liệu phải theo onCreateViewphương pháp của đoạn, xóa mọi dữ liệu Liên kết tồn tại trong OnCreatephương thức của bạn, bạn onCreateViewsẽ trông như thế này:

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

Tôi đã phải thêm cuộc gọi đến siêu để tạo lớp Binding của mình.
joey_g216

3
Tôi đấu tranh với vấn đề này trong nhiều giờ. Vấn đề là tôi đã trả lại quan điểm sai lầm. +1
TharakaNirmana

1
View view = binding.getRoot(); Tôi đã bị mắc kẹt trong vấn đề này quá lâu đến nỗi tôi khá buồn vì tôi không thể tìm thấy bất kỳ tài liệu nào về nó trên developer.android.com ... Đã giải quyết vấn đề. Cảm ơn bạn!
Victor Ude

1
Nếu bạn đang sử dụng LiveData và ViewModel, hãy nhớ đọc câu trả lời này .
Big McLUNDHuge

1
setMarsdata () là gì? tôi nghĩ ở đây chúng tôi sử dụng setViewModel () ??
suv

59

Bạn thực sự được khuyến khích sử dụng inflatephương pháp Binding được tạo của bạn chứ không phải 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();
}

Tài liệu cho DataBindingUtil.inflate () :

Chỉ sử dụng phiên bản này nếu layoutId không xác định trước. Mặt khác, sử dụng phương pháp lạm phát của Binding được tạo để đảm bảo lạm phát loại an toàn.


Thật không may, điều này đang giết chết tôi với cannot be resolved to a typelỗi xây dựng. Nó không đáng tin cậy theo ý kiến ​​của tôi. Nếu tôi đi trước DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);và sau đó thay đổi nó FragmentCameraBinding.inflate(inflater, container, false);, nó hoạt động, nhưng sau khi xây dựng lại, nó lại báo lỗi.
Alex Burdusel

Công trình tuyệt vời. Trên thực tế không cần chỉ định bố cục res id (mà tôi đã tự hỏi trước đây) vì nó tự động chọn từ tệp ràng buộc được tạo.
eC Droid

2
nơi nào bạn đặt id bố cục phân đoạn (ví dụ: R.layout.fragment_) trong ví dụ này?
Lenin Raj Rajasekaran

đây sẽ là câu trả lời được chấp nhận liên kết được tạo ra được khuyến khích sử dụng, thay vìDataBindingUtil.inflate
mochadwi

@LeninRajRajasekaran Id bố cục được ngụ ý thông qua việc sử dụng MainFragmentBindinglớp. Lớp đó được tạo từ tệp bố cục để bố cục mong muốn được tự động áp dụng.
Emil S.

19

Ngay cả các câu trả lời khác có thể hoạt động tốt, nhưng tôi muốn nói cách tiếp cận tốt nhất.

Sử dụng Binding class's inflatetheo khuyến nghị trong Tài liệu Android .

Một lựa chọn là tăng cao DataBindingUtil nhưng chỉ khi bạn không biết đã tạo lớp ràng buộc .

- Bạn đã tự động tạo binding class, sử dụng lớp đó thay vì sử dụng DataBindingUtil.

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

Trong tài liệu lớp DataBindingUtil bạn có thể thấy.

thổi phồng

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

Chỉ sử dụng phiên bản này nếu layoutId không xác định trước. Mặt khác, sử dụng phương pháp lạm phát của Binding được tạo để đảm bảo lạm phát loại an toàn.

Nếu lớp tạo bố cục của bạn không được tạo @ Hãy xem câu trả lời này .


tại sao không sử dụng inflatephương thức lấy LayoutInflaterđối số duy nhất của nó?
Florian Walther

@FlorianWalther có hoạt động mà không có ViewGroup container?
Khemraj

Chà, tôi không biết khi tôi viết bình luận này. Nhưng tôi đã nhận được một số câu trả lời tốt ở đây: stackoverflow.com/questions/61571381/ trộm
Florian Walther

1
@FlorianWalther okay, tôi đã đọc câu trả lời, điều đó containerlà cần thiết khi attachToRoottrue.
Khemraj

16

Nếu bạn đang sử dụng ViewModelLiveData Đây là cú pháp đầy đủ

Cú pháp của 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

Hãy thử điều này trong 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

Người ta có thể chỉ cần lấy đối tượng xem như được đề cập dưới đây

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

Cú pháp của 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

Như hầu hết đã nói, nhưng đừng quên thiết lập Mẫu LifeCyclOwner
trong Java tức là

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

làm việc trong mã của tôi.

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

Một ví dụ hoàn chỉnh trong các đoạn liên kết dữ liệu

FragmentMyProgramsBinding là lớp liên kết được tạo cho 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 rất hữu ích về 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)
}

Khai báo ràng buộc val như thế này trong Fragment:

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

Đừng quên viết điều này thành từng mảnh

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

1

Một ví dụ khác trong 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
}

Lưu ý rằng tên "MartianDataBinding" phụ thuộc vào tên của tệp bố cục. Nếu tệp được đặt tên là "martian_data" thì tên chính xác sẽ là MartianDataBinding.


0

Mọi người đều nói về inflate(), nhưng nếu chúng ta muốn sử dụng nó thì onViewCreated()sao?

Bạn có thể sử dụng bind(view)phương thức của lớp liên kết cụ thể để lấy ViewDataBindingví dụ cho view.


Thông thường chúng ta viết BaseFragment một cái gì đó như thế này (đơn giản hóa):

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

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

Và sử dụng nó trong mảnh con.

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


Nếu tất cả các mảnh sử dụng liên kết dữ liệu, bạn thậm chí có thể làm cho nó đơn giản hơn bằng cách sử dụng tham số loại.

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

Tôi không biết có ổn không khi khẳng định không có giá trị ở đó, nhưng .. bạn hiểu ý. Nếu bạn muốn nó là nullable, bạn có thể làm điều đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.