Tóm tắt vấn đề
Lưu ý: Trong câu trả lời này, tôi sẽ tham khảo FragmentPagerAdapter
và mã nguồn của nó. Nhưng giải pháp chung cũng nên áp dụng cho FragmentStatePagerAdapter
.
Nếu bạn đang đọc điều này, bạn có thể đã biết rằng FragmentPagerAdapter
/ FragmentStatePagerAdapter
được tạo ra Fragments
cho bạn ViewPager
, nhưng khi hoạt động giải trí (cho dù từ xoay thiết bị hoặc hệ thống giết ứng dụng của bạn để lấy lại bộ nhớ), những thứ này Fragments
sẽ không được tạo lại, mà thay vào đó là các phiên bản được truy xuất từFragmentManager
. Bây giờ hãy nói Activity
nhu cầu của bạn để có được một tham chiếu đến những điều này Fragments
để làm việc với chúng. Bạn không có id
hoặc tag
cho những thứ này được tạo Fragments
bởi vì FragmentPagerAdapter
hãy đặt chúng trong nội bộ . Vì vậy, vấn đề là làm thế nào để có được một tham chiếu đến họ mà không có thông tin đó ...
Vấn đề với các giải pháp hiện tại: dựa vào mã nội bộ
Rất nhiều trong những giải pháp tôi đã nhìn thấy trên này và tương tự như câu hỏi dựa vào nhận được một tham chiếu đến hiện Fragment
bằng cách gọi FragmentManager.findFragmentByTag()
và bắt chước các thẻ nội bộ tạo ra:"android:switcher:" + viewId + ":" + id
. Vấn đề với điều này là bạn đang dựa vào mã nguồn nội bộ, mà như tất cả chúng ta đều biết không được đảm bảo sẽ giữ nguyên mãi mãi. Các kỹ sư Android tại Google có thể dễ dàng quyết định thay đổi tag
cấu trúc sẽ phá vỡ mã của bạn khiến bạn không thể tìm thấy tham chiếu đến cấu trúc hiện có Fragments
.
Giải pháp thay thế mà không cần dựa vào nội bộ tag
Dưới đây là một ví dụ đơn giản về cách lấy tham chiếu đến giá trị được Fragments
trả về FragmentPagerAdapter
mà không dựa vào tập nội bộ tags
trên Fragments
. Điều quan trọng là ghi đè instantiateItem()
và lưu các tham chiếu trong đó thay vì trong getItem()
.
public class SomeActivity extends Activity {
private FragmentA m1stFragment;
private FragmentB m2ndFragment;
// other code in your Activity...
private class CustomPagerAdapter extends FragmentPagerAdapter {
// other code in your custom FragmentPagerAdapter...
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// Do NOT try to save references to the Fragments in getItem(),
// because getItem() is not always called. If the Fragment
// was already created then it will be retrieved from the FragmentManger
// and not here (i.e. getItem() won't be called again).
switch (position) {
case 0:
return new FragmentA();
case 1:
return new FragmentB();
default:
// This should never happen. Always account for each position above
return null;
}
}
// Here we can finally safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger). Simply save the returned Fragment from
// super.instantiateItem() into an appropriate reference depending
// on the ViewPager position.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// save the appropriate reference depending on position
switch (position) {
case 0:
m1stFragment = (FragmentA) createdFragment;
break;
case 1:
m2ndFragment = (FragmentB) createdFragment;
break;
}
return createdFragment;
}
}
public void someMethod() {
// do work on the referenced Fragments, but first check if they
// even exist yet, otherwise you'll get an NPE.
if (m1stFragment != null) {
// m1stFragment.doWork();
}
if (m2ndFragment != null) {
// m2ndFragment.doSomeWorkToo();
}
}
}
hoặc nếu bạn muốn làm việc với tags
thay vì các biến / tham chiếu thành viên của lớp, Fragments
bạn cũng có thể lấy tags
tập hợp FragmentPagerAdapter
theo cách tương tự: LƯU Ý: điều này không áp dụng FragmentStatePagerAdapter
vì nó không được đặt tags
khi tạo Fragments
.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// get the tags set by FragmentPagerAdapter
switch (position) {
case 0:
String firstTag = createdFragment.getTag();
break;
case 1:
String secondTag = createdFragment.getTag();
break;
}
// ... save the tags somewhere so you can reference them later
return createdFragment;
}
Lưu ý rằng phương pháp này KHÔNG dựa vào việc bắt chước tag
tập hợp bên trong FragmentPagerAdapter
và thay vào đó sử dụng các API thích hợp để truy xuất chúng. Bằng cách này, ngay cả khi những tag
thay đổi trong các phiên bản tương lai của SupportLibrary
bạn, bạn vẫn sẽ an toàn.
Đừng quên rằng tùy thuộc vào thiết kế của bạn Activity
, Fragments
mà bạn đang cố gắng làm việc có thể tồn tại hoặc có thể chưa tồn tại, vì vậy bạn phải tính đến điều đó bằng cách null
kiểm tra trước khi sử dụng tài liệu tham khảo của mình.
Ngoài ra, nếu thay vào đó bạn đang làm việc FragmentStatePagerAdapter
, thì bạn không muốn giữ các tham chiếu cứng cho của mình Fragments
vì bạn có thể có nhiều trong số đó và các tham chiếu cứng sẽ lưu chúng trong bộ nhớ một cách không cần thiết. Thay vào đó, hãy lưu các Fragment
tham chiếu trong WeakReference
các biến thay vì các tham chiếu chuẩn. Như thế này:
WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
// reference hasn't been cleared yet; do work...
}