Cách lấy các phân đoạn hiện có khi sử dụng FragmentPagerAdapter


99

Tôi gặp sự cố khi làm cho các phân đoạn của mình giao tiếp với nhau thông qua Activity, đang sử dụng FragmentPagerAdapter, như một lớp trợ giúp triển khai việc quản lý các tab và tất cả các chi tiết của việc kết nối a ViewPagervới được liên kết TabHost. Tôi đã triển khai FragmentPagerAdaptergiống như nó được cung cấp bởi dự án mẫu Android Support4Demos .

Câu hỏi chính là làm thế nào tôi có thể nhận được phân đoạn cụ thể từ FragmentManagerkhi tôi không có Id hoặc Thẻ? FragmentPagerAdapterđang tạo các phân đoạn và tự động tạo Id và Thẻ.



@ jk2K làm sao đây có thể là bản sao của câu hỏi đã được hỏi 1 năm sau
Davi

@Dawit lặp lại như một nhãn, không liên quan đến thời gian, câu hỏi sau có tầm nhìn cao hơn
jk2K

Hầu hết các câu trả lời ở đây không hoạt động trong sản xuất, vì vậy hãy kiểm tra câu trả lời của tôi trong stackoverflow.com/a/54280113/2413303
EpicPandaForce

Câu trả lời:


193

Tóm tắt vấn đề

Lưu ý: Trong câu trả lời này, tôi sẽ tham khảo FragmentPagerAdaptervà 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 Fragmentscho 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 Fragmentssẽ 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 Activitynhu 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ó idhoặc tagcho những thứ này được tạo Fragmentsbở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 Fragmentbằ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 tagcấ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 Fragmentstrả về FragmentPagerAdaptermà không dựa vào tập nội bộ tagstrê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 tagsthay vì các biến / tham chiếu thành viên của lớp, Fragmentsbạn cũng có thể lấy tagstập hợp FragmentPagerAdaptertheo cách tương tự: LƯU Ý: điều này không áp dụng FragmentStatePagerAdaptervì nó không được đặt tagskhi 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 tagtập hợp bên trong FragmentPagerAdaptervà 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 tagthay đổi trong các phiên bản tương lai của SupportLibrarybạ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, Fragmentsmà 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 nullkiể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 Fragmentsvì 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 Fragmenttham chiếu trong WeakReferencecá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...
}

3
Đây là một giải pháp thực sự tốt, nhưng có vẻ như để mất hiệu quả của nó nếu bạn không biết có bao nhiêu mảnh vỡ sẽ được thông qua vào.
Riot Goes Woof

3
@Zorpix bạn có thể lưu trữ các phân đoạn đã tạo trong HashMap: map.put (position, createdFragment);
Tom Bevelander

12
Điều này xứng đáng được đánh dấu! Cách rất thông minh và toàn diện để thực hiện điều này. Bạn đã giúp tôi rất nhiều cảm ơn bạn!
young_souvlaki

2
Lúc đầu, giải pháp này trông quá phức tạp, vì vậy tôi đã bỏ qua nó. Tuy nhiên, cuối cùng tôi đã quay lại với nó, vì những câu trả lời khác không thỏa đáng. Và nó không khó như tôi nghĩ.
Suragch

1
không cần ghi đè bất cứ thứ gì và bạn thực sự không nên ghi đè instantiateItem. cách thích hợp để làm điều này là để gọi instantiateItem trong onCreatephương thức hoạt động của bạn được bao quanh bởi startUpdatefinishUpdate. Xem câu trả lời của tôi để biết chi tiết
morgwai

82

Tôi đã tìm thấy câu trả lời cho câu hỏi của mình dựa trên bài đăng sau: sử dụng lại các đoạn trong một trang web phân mảnh

Vài điều tôi đã học được:

  1. getItem(int position)trong tên FragmentPagerAdapterkhá gây hiểu lầm của những gì phương pháp này thực sự làm. Nó tạo ra các phân đoạn mới, không trả lại các phân đoạn hiện có. Theo nghĩa như vậy, phương thức phải được đổi tên thành một thứ gì đó giống như createItem(int position)trong Android SDK. Vì vậy, phương pháp này không giúp chúng ta nhận được các mảnh vỡ.
  2. Dựa trên giải thích trong bài hỗ trợ FragmentPagerAdapterholds tham chiếu đến các đoạn cũ bạn nên để việc tạo các phân đoạn đó FragmentPagerAdaptervà như vậy có nghĩa là bạn không có tham chiếu đến các Phân đoạn hoặc các thẻ của chúng. Tuy nhiên, nếu bạn có thẻ phân đoạn, bạn có thể dễ dàng truy xuất tham chiếu đến nó từ FragmentManagerbằng cách gọi findFragmentByTag(). Chúng tôi cần một cách để tìm ra thẻ của một phân đoạn ở vị trí trang nhất định.

Giải pháp

Thêm phương thức trợ giúp sau trong lớp của bạn để truy xuất thẻ phân đoạn và gửi nó đến findFragmentByTag() phương thức.

private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

GHI CHÚ! Đây là phương pháp giống hệt nhau được FragmentPagerAdaptersử dụng khi tạo các đoạn mới. Xem liên kết này http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104


Btw, có nhiều hơn về chủ đề trong phần Hỏi và Đáp này: stackoverflow.com/questions/6976027/…
Thomas

1
tham số đầu vào viewId là gì? Quan điểm nào?
Nilzor

@Nilzor viewId là id của ViewPager.
Dr.jacky

Có thể thay vì đoán thẻ, phân đoạn có thể cho thẻ biết hoạt động của nó onAttach()?
lưu vực

4
Đây không phải là cách đúng đắn. Câu trả lời của @Tony Chan là cách tốt nhất và chính xác.
Morteza Rastgoo

16

bạn không cần ghi đè instantiateItemcũng như không dựa vào khả năng tương thích với makeFragmentNamephương pháp nội bộ bằng cách tạo thủ công các thẻ phân đoạn.
instantiateItemlà một phương thức công khai để bạn có thể và thực sự nên gọi nó trong onCreatephương thức hoạt động của bạn được bao quanh bởi các lệnh gọi đến startUpdatefinishUpdatecác phương thức như được mô tả trong PagerAdapter javadoc :

Một lệnh gọi đến startUpdate phương thức PagerAdapter (ViewGroup) chỉ ra rằng nội dung của ViewPager sắp thay đổi. Một hoặc nhiều lệnh gọi tới InstantiateItem (ViewGroup, int) và / hoặc DestItem (ViewGroup, int, Object) sẽ theo sau và việc kết thúc cập nhật sẽ được báo hiệu bằng lệnh gọi finishUpdate (ViewGroup).

Sau đó, bạn có thể bằng cách trên, lưu trữ các tham chiếu đến các bản sao của các mảnh của bạn trên các vars cục bộ nếu bạn cần. Xem ví dụ:

public class MyActivity extends AppCompatActivity {

    Fragment0 tab0; Fragment1 tab1;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        tab0 = (Fragment0) adapter.instantiateItem(viewPager, 0);
        tab1 = (Fragment1) adapter.instantiateItem(viewPager, 1);
        adapter.finishUpdate(viewPager);
    }

    class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
    }
}

instantiateItemtrước tiên sẽ cố gắng lấy tham chiếu đến các trường hợp phân đoạn hiện có từ FragmentManager. Chỉ khi chúng chưa tồn tại, nó sẽ tạo những cái mới bằng getItemphương pháp từ bộ điều hợp của bạn và "lưu trữ" chúng trongFragmentManager để sử dụng trong tương lai.

Điều quan trọng cần lưu ý là ngay cả khi bạn không cần lấy tham chiếu đến các phân đoạn của mình, bạn vẫn nên gọi instantiateItemcho tất cả các tab được bao quanh bởi startUpdate/ finishUpdatetrong onCreatephương thức của bạn như sau:

    adapter.startUpdate(viewPager);
    // ignoring return values of the below 2 calls, just side effects matter:
    adapter.instantiateItem(viewPager, 0);
    adapter.instantiateItem(viewPager, 1);
    adapter.finishUpdate(viewPager);

Nếu bạn không làm như vậy, thì bạn có nguy cơ rằng các phiên bản phân đoạn của bạn sẽ không bao giờ được cam kết để FragmentManager: khi hoạt động của bạn trở nên foreground instantiateItemsẽ được gọi tự động để có được mảnh vỡ của bạn, nhưng startUpdate/ finishUpdate có thể không (tùy thuộc vào chi tiết thực hiện) và những gì họ cơ bản làm là bắt đầu / cam kết a FragmentTransaction.
Điều này có thể dẫn đến việc các tham chiếu đến các trường hợp phân đoạn đã tạo bị mất rất nhanh (ví dụ: khi bạn xoay màn hình) và được tạo lại thường xuyên hơn mức cần thiết. Tùy thuộc vào mức độ "nặng" của các mảnh vỡ của bạn, nó có thể gây ra hậu quả về hiệu suất không đáng kể.
Hơn nữa, trong trường hợp đó, các trường hợp phân mảnh được lưu trữ trên các vars cục bộ có thểtrở nên cũ kỹ: nếu nền tảng android cố gắng lấy chúng từ FragmentManagerbất cứ lý do gì, nó sẽ thất bại và do đó sẽ tạo và sử dụng những cái mới, trong khi các vars của bạn sẽ vẫn tham khảo những cái cũ.


1
Nó có thể là giải pháp tốt nhất trong một số trường hợp. Nhưng điều gì sẽ xảy ra nếu FragmentManger giết mảnh vỡ và tái tạo nó?
woltran

1
@woltran FragmentManagerkhông thể chỉ giết ngẫu nhiên ( tiêu diệt là từ thích hợp ở đây) của bạn Fragment(hãy nghĩ điều gì sẽ xảy ra nếu nó quyết định giết một Fragmentcon hiện đang được hiển thị;)). Nói chung vòng đời của a Fragmentbị ràng buộc với nó Activity(xem github.com/xxv/android-lifecycle để biết thêm chi tiết) -> a Fragmentchỉ có thể bị phá hủy nếu nó Activitybị phá hủy. Trong trường hợp này khi người dùng điều hướng trở lại hành động đã cho, Activityý onCreatemuốn của nó sẽ được gọi lại và một phiên bản mới của di Fragmentchúc sẽ được tạo.
morgwai

Đây là câu trả lời CÓ THẬT
MJ Studio

Bạn có nên thực sự tạo các phân đoạn thay vì dựa vào chúng được tạo ra khi người dùng cuộn ViewPager chẳng hạn?
Yar

@Yar vâng, bạn thực sự nên. Đoạn trích tài liệu tôi đã cung cấp nêu rõ điều đó và phần "Một số thông tin bổ sung" giải thích lý do.
morgwai

11

Cách tôi đã làm là xác định một Hashtable của các tài liệu tham khảo yếu như sau:

protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;

Sau đó, tôi đã viết phương thức getItem () như thế này:

@Override
public Fragment getItem(int position) {

    Fragment fragment;
    switch(position) {
    case 0:
        fragment = new MyFirstFragmentClass();
        break;

    default:
        fragment = new MyOtherFragmentClass();
        break;
    }

    fragmentReferences.put(position, new WeakReference<Fragment>(fragment));

    return fragment;
}

Sau đó, bạn có thể viết một phương thức:

public Fragment getFragment(int fragmentId) {
    WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
    return ref == null ? null : ref.get();
}

Điều này có vẻ hoạt động tốt và tôi thấy nó ít hack hơn một chút so với

"android:switcher:" + viewId + ":" + position

lừa, vì nó không dựa vào cách triển khai FragmentPagerAdapter. Tất nhiên nếu phân mảnh đã được FragmentPagerAdapter phát hành hoặc nếu nó chưa được tạo, getFragment sẽ trả về null.

Nếu ai đó phát hiện ra điều gì đó không ổn với cách tiếp cận này, chúng tôi hoan nghênh các ý kiến ​​đóng góp.


int fragmentIdnên được đổi tên thànhint position
lmaooooo

7
Tôi đã sử dụng một cách tiếp cận rất giống nhau. Nhưng điều này không thành công khi máy nhắn tin được tạo từ một gói SaveState. ví dụ: Activity đi vào nền và quay lại nền trước sau khi onSavedStateInstance () được gọi. Trong trường hợp đó, các phương thức getItem () sẽ không được gọi.
Anoop

Lý do để tạo bản đồ của riêng bạn là gì vì đã có một bản đồ trong FragmentManager luôn cập nhật? Xem câu trả lời của tôi để biết chi tiết.
morgwai 27/12/16

Ngoài ra, thực tế là một mảnh đã bị phá hủy, không đảm bảo rằng không có tham chiếu mạnh mẽ đến nó (mặc dù có thể xảy ra, nhưng KHÔNG được đảm bảo) trong trường hợp đó bản đồ của bạn vẫn chứa các mảnh cũ.
morgwai

1
Điều này sẽ không hoạt động chính xác sau khi hệ thống tạo lại các mảnh.
EpicPandaForce

10

Tôi đã tạo phương thức này đang hoạt động cho tôi để lấy tham chiếu đến phân đoạn hiện tại.

public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
    try {
        Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
        Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
        f.setAccessible(true);
        FragmentManager fm = (FragmentManager) f.get(adapter);
        m.setAccessible(true);
        String tag = null;
        tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
        return fm.findFragmentByTag(tag);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } 
    return null;
}

Đẹp chỉ để nhớ đã tạo phương pháp và Field bên ngoài phương pháp này cho hiệu suất tốt hơn
Marcos Vasconcelos

2

giải pháp được đề xuất bởi @ personne3000 là tốt, nhưng nó có một vấn đề: khi hoạt động ở chế độ nền và bị hệ thống giết (để lấy một số bộ nhớ trống) và sau đó được khôi phục, fragmentReferencessẽ trống, bởi vìgetItem sẽ không gọi là.

Lớp dưới đây xử lý tình huống như vậy:

public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {

    public static final String FRAGMENT_SAVE_PREFIX = "holder";
    private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.

    public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = fm;
    }

    private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();

    protected void holdFragment(F fragment) {
        holdFragment(holder.size(), fragment);
    }

    protected void holdFragment(int position, F fragment) {
        if (fragment != null)
            holder.put(position, new WeakReference<F>(fragment));
    }

    public F getHoldedItem(int position) {
        WeakReference<F> ref = holder.get(position);
        return ref == null ? null : ref.get();
    }

    public int getHolderCount() {
        return holder.size();
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
        super.restoreState(state, loader);
        Bundle bundle = (Bundle) state;
        for (String key : bundle.keySet()) {
            if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
                int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
                Fragment f = fragmentManager.getFragment(bundle, key);
                holdFragment(index, (F) f);
            }
        }
    }

    @Override
    public Parcelable saveState() {
        Bundle state = (Bundle) super.saveState();
        if (state == null)
            state = new Bundle();

        for (int i = 0; i < holder.size(); i++) {
            int id = holder.keyAt(i);
            final F f = getHoldedItem(i);
            String key = FRAGMENT_SAVE_PREFIX + id;
            fragmentManager.putFragment(state, key, f);
        }
        return state;
    }
}

1

Chặn đường chính với việc xử lý các đoạn là bạn không thể dựa vào getItem (). Sau khi thay đổi hướng, các tham chiếu đến các đoạn sẽ rỗng và getItem () không được gọi lại.

Đây là một cách tiếp cận không dựa vào việc triển khai FragmentPagerAdapter để lấy thẻ. Ghi đè InstantiateItem () sẽ trả về phân đoạn được tạo từ getItem () hoặc được tìm thấy từ trình quản lý phân mảnh.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object value =  super.instantiateItem(container, position);

    if (position == 0) {
        someFragment = (SomeFragment) value;
    } else if (position == 1) {
        anotherFragment = (AnotherFragment) value;
    }

    return value;
}

0

Xem bài đăng này về cách trả lại các đoạn từ FragmentPagerAdapter. Dựa vào việc bạn biết chỉ mục của phân đoạn của mình - nhưng điều này sẽ được đặt trong getItem () (chỉ khi khởi tạo)


0

Tôi đã quản lý để giải quyết vấn đề này bằng cách sử dụng id thay vì thẻ. (Tôi đang sử dụng FragmentStatePagerAdapter mà tôi đã định nghĩa sử dụng Fragment tùy chỉnh của tôi, trong đó tôi ghi đè phương thức onAttach, nơi bạn lưu id ở đâu đó:

@Override
public void onAttach(Context context){
    super.onAttach(context);
    MainActivity.fragId = getId();
}

Và sau đó bạn chỉ cần truy cập phân mảnh dễ dàng bên trong hoạt động:

Fragment f = getSupportFragmentManager.findFragmentById(fragId);

0

Tôi không biết đây có phải là cách tốt nhất hay không nhưng không có cách nào khác hiệu quả với tôi. Tất cả các tùy chọn khác bao gồm getActiveFragment đều trả về giá trị rỗng hoặc khiến ứng dụng gặp sự cố.

Tôi nhận thấy rằng khi quay màn hình, phân đoạn đang được đính kèm vì vậy tôi đã sử dụng nó để gửi phân mảnh trở lại hoạt động.

Trong phân đoạn:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnListInteractionListener) activity;
        mListener.setListFrag(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

Sau đó, trong hoạt động:

@Override
public void setListFrag(MyListFragment lf) {
    if (mListFragment == null) {
        mListFragment = lf;
    }
}

Và cuối cùng trong hoạt động onCreate ():

if (savedInstanceState != null) {
    if (mListFragment != null)
        mListFragment.setListItems(items);
}

Cách tiếp cận này gắn đoạn thực tế có thể nhìn thấy vào hoạt động mà không cần tạo một đoạn mới.


0

Không chắc liệu phương pháp của tôi có phải là cách chính xác hoặc tốt nhất để thực hiện việc này hay không vì tôi là người mới bắt đầu với Java / Android, nhưng nó đã hoạt động (tôi chắc chắn rằng nó vi phạm các nguyên tắc hướng đối tượng nhưng không có giải pháp nào khác phù hợp với trường hợp sử dụng của tôi).

Tôi đã có một Hoạt động lưu trữ đang sử dụng ViewPager với FragmentStatePagerAdapter. Để có được các tham chiếu đến các Fragment được tạo bởi FragmentStatePagerAdapter, tôi đã tạo một giao diện gọi lại trong lớp phân mảnh:

public interface Callbacks {
    public void addFragment (Fragment fragment);
    public void removeFragment (Fragment fragment);
}

Trong hoạt động lưu trữ, tôi đã triển khai giao diện và tạo LinkedHasSet để theo dõi các phân đoạn:

public class HostingActivity extends AppCompatActivity implements ViewPagerFragment.Callbacks {

    private LinkedHashSet<Fragment> mFragments = new LinkedHashSet<>();

    @Override
    public void addFragment (Fragment fragment) {
        mFragments.add(fragment);
    }

    @Override
    public void removeFragment (Fragment fragment) {
        mFragments.remove(fragment);
    }
}

Trong lớp ViewPagerFragment, tôi đã thêm các phân đoạn vào danh sách trong onAttach và xóa chúng trong onDetach:

public class ViewPagerFragment extends Fragment {

    private Callbacks mCallbacks;

    public interface Callbacks {
        public void addFragment (Fragment fragment);
        public void removeFragment (Fragment fragment);
    } 

    @Override
    public void onAttach (Context context) {
        super.onAttach(context);
        mCallbacks = (Callbacks) context;
        // Add this fragment to the HashSet in the hosting activity
        mCallbacks.addFragment(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Remove this fragment from the HashSet in the hosting activity
        mCallbacks.removeFragment(this);
        mCallbacks = null;
    }
}

Trong hoạt động lưu trữ, bây giờ bạn sẽ có thể sử dụng mFragment để lặp lại các phân đoạn hiện đang tồn tại trong FragmentStatePagerAdapter.


0

Lớp này thực hiện thủ thuật mà không cần dựa vào các thẻ bên trong. Cảnh báo: Các phân đoạn phải được truy cập bằng phương thức getFragment chứ không phải phương thức getItem.

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private final Map<Integer, Reference<Fragment>> fragments = new HashMap<>();
    private final List<Callable0<Fragment>> initializers = new ArrayList<>();
    private final List<String> titles = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    void addFragment(Callable0<Fragment> initializer, String title) {
        initializers.add(initializer);
        titles.add(title);
    }

    public Optional<Fragment> getFragment(int position) {
        return Optional.ofNullable(fragments.get(position).get());
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment =  initializers.get(position).execute();
        return fragment;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.put(position, new WeakReference<>(fragment));
        return fragment;
    }

    @Override
    public int getCount() {
        return initializers.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}

-5

Chỉ cần tiếp tục thử mã này,

public class MYFragmentPAdp extends FragmentPagerAdapter {

    public MYFragmentPAdp(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 2;
    }

     @Override
     public Fragment getItem(int position) {
         if (position == 0)
             Fragment fragment = new Fragment1();
         else (position == 1)
             Fragment fragment = new Fragment2();
         return fragment;
     }
}

Najib, giống như tôi đã giải thích trong câu trả lời của mình bên dưới getItem () đang tạo phân đoạn mới thay vì trả lại các phân đoạn hiện có như người ta có thể mong đợi đã cho tên get và not create . Xem giải pháp của tôi trong cùng một bài viết.
Ismar Slomic

Phân đoạn phân mảnh = new YourCustomFragmentClass (); viết ở đây kiểm tra cho điều này.
Najib Ahmed Puthawala

Tôi vẫn không hiểu làm thế nào thay đổi này thực tế là bạn đang tạo đoạn mới trong thay vì nhận được trong hiện tại ..
Ismar Slomic

Bạn vẫn chỉ khởi tạo và trả về cho phân đoạn tùy chỉnh của mình, chẳng hạn như Fragment mảnh = new YourFragment (); trả lại phân mảnh;
Najib Ahmed Puthawala 27/12/12
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.