web-dev-qa-db-de.com

IllegalArgumentException: Keine Ansicht für ID für Fragment --- ViewPager in ViewPager gefunden

Ich bin dem Problem begegnet, das mich tagelang beunruhigt.

In der Hauptaktivität befindet sich ein ViewPagername__, der 3 Fragmentname__s als Tabulatorfragmente enthält. Im first -Fragment befindet sich ein ListViewname__, der einige Ansichten enthält, und der wichtigste ist ein anderer ViewPagername__. Ich möchte einige Fotos in der Untergruppe ViewPagerspeichern und hier einige weitere Fragmente verwenden.

Jetzt gibt es die Mühe:
Wenn das first Fragmentgestoppt wird (Das third Fragment im übergeordneten ViewPagerwird auf dem Bildschirm angezeigt) und fortgesetzt (der Benutzer wechselt zum second fragment) stürzt die App ab und der Debugger sagt:

Java.lang.IllegalArgumentException: No view found for id 0x7f05008b (com.example.viewpager:id/sub_viewpager) for fragment ScreenSlidePageFragment

Ich habe getChildFragmentManager() bereits verwendet, da dies eine Situation von verschachtelten Fragmenten ist.

Hier ist der Schlüsselcode des Listenadapters, der dem ersten Fragment im übergeordneten ViewPager entspricht:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    int type = getItemViewType(position);
    switch (type) {
        case TYPE_BANNER:
            if (convertView == null) {
                convertView = mBannerView.getBannerView(parent);
            }
            mBannerView.update(convertView);
            break;
        case TYPE_ITEM:
            break;
    }
    return convertView;
}

Hier ist der Code von mBannerViewname__:

public class BannerView {

    private static final DisplayImageOptions IMAGE_OPTIONS_SCALE_STRETCHED =
            new DisplayImageOptions.Builder()
                    .cacheInMemory()
                    .cacheOnDisc()
                    .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
                    .build();

    private FragmentActivity mActivity;
    private Fragment mFragment;
    private List<Banner> mBanners;
    private ScreenSlidePagerAdapter mPagerAdapter;
    private ViewPager mViewPager;

    public BannerView(FragmentActivity activity, Fragment fragment) {
        mActivity = activity;
        mFragment = fragment;
    }

    public void update(View convertView) {
        mViewPager = (ViewPager) convertView;
        if (mBanners != null && !mBanners.isEmpty()) {
            if (mPagerAdapter == null) {
                mPagerAdapter = new ScreenSlidePagerAdapter(mFragment.getChildFragmentManager());
                mViewPager.setAdapter(mPagerAdapter);
            }
        }
        mViewPager.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mOnBannerClickListener != null) {
                    mOnBannerClickListener.onBannerClick();
                }
            }
        });
    }

    class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return new ScreenSlidePageFragment(mBanners.get(position).getImageUrl());
        }

        @Override
        public int getCount() {
            return mBanners == null ? 0 : mBanners.size();
        }
    }

    class ScreenSlidePageFragment extends Fragment {

        private String mUrl;

        ScreenSlidePageFragment(String url) {
            super();
            mUrl = url;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.item_banner, container, false);
            if (view != null) {
                ImageView imageView = (ImageView) view.findViewById(R.id.item_banner_image);
                imageView.setLayoutParams(new LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
                ImageLoader.getInstance().displayImage(mUrl, imageView, IMAGE_OPTIONS_SCALE_STRETCHED);
            }
            return view;
        }
    }
}

Hier ist die detaillierte Fehlerliste:

11-10 18:12:19.217    1444-1444/? E/MessageQueue-JNI﹕ Java.lang.IllegalArgumentException: No view found for id 0x7f05008b (com.example.viewpager:id/sub_viewpager) for fragment ScreenSlidePageFragment{428d8ea0 #0 id=0x7f05008b}
        at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:919)
        at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1104)
        at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:1086)
        at Android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.Java:1884)
        at Android.support.v4.app.Fragment.performActivityCreated(Fragment.Java:1514)
        at Android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.Java:947)
        at Android.support.v4.app.FragmentManagerImpl.attachFragment(FragmentManager.Java:1280)
        at Android.support.v4.app.BackStackRecord.run(BackStackRecord.Java:672)
        at Android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.Java:1467)
        at Android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.Java:472)
        at Android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.Java:141)
        at Android.support.v4.view.ViewPager.populate(ViewPager.Java:1068)
        at Android.support.v4.view.ViewPager.populate(ViewPager.Java:914)
        at Android.support.v4.view.ViewPager$3.run(ViewPager.Java:244)
        at Android.support.v4.view.ViewPager.completeScroll(ViewPager.Java:1761)
        at Android.support.v4.view.ViewPager.onInterceptTouchEvent(ViewPager.Java:1896)
        at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:1854)
        at Android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.Java:2211)
        at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:1912)
        at Android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.Java:2211)
        at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:1912)
        at Android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.Java:2211)
        at Android.view.ViewGroup.dispatchTouchEvent(ViewGroup.Java:1912)
        at com.Android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.Java:2228)
        at com.Android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.Java:1471)
        at Android.app.Activity.dispatchTouchEvent(Activity.Java:2424)
        at com.Android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.Java:2176)
        at Android.view.View.dispatchPointerEvent(View.Java:7571)
        at Android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.Java:3883)
        at Android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.Java:3778)
        at Android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.Java:3379)
        at Android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.Java:3429)
        at Android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.Java:3398)
        at Android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.Java:3483)
        at Android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.Java:3406)
        at Android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.Java:3540)
        at Android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.Java:3379)
        at Android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.Java:3429)
        at Android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.Java:3398)
        at Android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.Java:3406)
        at Android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.Java:3379)
        at Android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.Java:5419)
        at Android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.Java:5399)
        at Android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.Java:5370)
        at Android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.Java:5493)
        at Android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.Java:182)
        at Android.os.MessageQueue.nativePollOnce(Native Method)
        at Android.os.MessageQueue.next(MessageQueue.Java:132)
        at Android.os.Looper.loop(Looper.Java:124)
        at Android.app.ActivityThread.main(ActivityThread.Java:5289)
        at Java.lang
18
Xieyi

Aktualisieren:

Ich habe den Quellcode des FragmentManagers gelesen und schließlich den wahren Grund erhalten: Diese Ausnahme tritt auf, wenn die Fragmente an den Viewpager angehängt werden sollen, bevor der Viewpager an den übergeordneten Element angehängt wird. Mit anderen Worten, bevor die getView () -Methode zurückkehrt, werden die Fragmente aufgeblasen. Dann wird die findViewById () -Methode des Containers des ViewPagers aufgerufen, der ViewPager befindet sich jedoch noch im getrennten Zustand. Daher wird null gefunden und die IllegalArgumentException ausgelöst.

Die Lösung besteht darin, einen benutzerdefinierten ViewPager zu erstellen und den Adapter zu konfigurieren: 

public class BannerViewPager extends ViewPager {
    PagerAdapter mPagerAdapter;

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mPagerAdapter != null) {
            super.setAdapter(mPagerAdapter);
            mPageIndicator.setViewPager(this);
        }
    }

    @Override
    public void setAdapter(PagerAdapter adapter) {
    }

    public void storeAdapter(PagerAdapter pagerAdapter) {
        mPagerAdapter = pagerAdapter;
    }

    public BannerViewPager(Context context) {
        super(context);
    }

    public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

}

Verwenden Sie in der getView () - Methode storeAdapter () anstelle von setAdapter.

Die folgenden Aussagen sind nicht korrekt. Die obigen Worte sind der eigentliche Grund.


Endlich habe ich die Antwort. Es besteht aus zwei Teilen.

  1. Im übergeordneten ViewPager habe ich eine FragmentPagerAdapter verwendet, um Fragmente zu speichern, aber jetzt verwende ich stattdessen eine FragmentStatePagerAdapter. Den Unterschied zwischen diesen beiden finden Sie hier: Unterschied zwischen FragmentPagerAdapter und FragmentStatePagerAdapter .
    Einfach ausgedrückt, FragmentPagerAdapter speichert mehr Informationen, wenn ein Fragment angehalten wird. In dieser Situation wird das erste Fragment im übergeordneten ViewPager angehalten, aber nicht zerstört, während die Ansichten in diesem Fragment zerstört werden. Nach dem Fortsetzen versucht das Fragment, alle Ansichten erneut aufzublasen. Bevor jedoch die getView()-Methode aufgerufen wird und der Sub-ViewPager neu erstellt wird, versucht der untergeordnete FragmentManager, den Sub-ViewPager zu finden, der die zuvor gespeicherten Fragmente enthält. Daher tritt die "Java.lang.IllegalArgumentException: Keine Ansicht für die ID gefunden" auf.

  2. Nachdem ich den FragmentPagerAdapter durch FragmentStatePagerAdapter ersetzt habe, wird ein anderes Problem angezeigt. Der Unter-Viewpager fehlt, wenn das übergeordnete Fragment (das erste Fragment im übergeordneten Viewpager) angehalten, zerstört und wieder aufgenommen wurde. Dies geschieht, wenn das erste Fragment ausgewählt wird, bald darauf das dritte Fragment ausgewählt wird und schließlich das erste Fragment erneut ausgewählt wird.
    Ich glaube, das ist ein Fehler von Android-SDK. Inspiriert von hier und hier benutze ich einige knifflige Methoden, um das Problem zu lösen. Der Punkt ist, wenn ein übergeordnetes Fragment zerstört wird, endet das Feldmitglied --- mChildFragmentManager "mit einem defekten internen Zustand" und wird nicht vollständig bereinigt. Wenn das übergeordnete Fragment erneut erstellt wird, ist mChildFragmentManager nicht null, aber die Unterfragmente wurden bereits zerstört, nachdem das übergeordnete Fragment zerstört wurde, das von mChildFragmentManager verwaltet wurde. Der Sub-ViewPager zeigt daher eine leere Ansicht auf dem Bildschirm an, die auf ein tatsächlich nicht vorhandenes gefälschtes Fragment reagiert. Das Lustige daran ist, dass die Unterfragmente und die Ansichten nach mehrmaligem Wischen auf dem untergeordneten ViewPager erneut angezeigt werden.

Hier sind die Codes:

Elternadapter:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = getBannerView(mParent);
    }
    mViewPager = (ViewPager) convertView;
    if (mBanners != null && !mBanners.isEmpty()) {
        if (mPagerAdapter == null) {
            FragmentManager childFM = mFragment.getChildFragmentManager();
            removeOldFragment(childFM);
            mPagerAdapter = new ScreenSlidePagerAdapter(childFM, mBanners);
            mViewPager.setAdapter(mPagerAdapter);
        }
    }
    return convertView;
}

Die Schlüsselmethode: 

    private void removeOldFragment(FragmentManager fm) {
        try {
            Field added = fm.getClass().getDeclaredField("mAdded");
            added.setAccessible(true);
            added.set(fm, null);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        try {
            Field active = fm.getClass().getDeclaredField("mActive");
            active.setAccessible(true);
            active.set(fm, null);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
49
Xieyi

Bei der Verwendung geschachtelter ViewPagers ..__ trat das gleiche Problem auf. Ich habe das Problem behoben, indem ich die beiden FragmentPagerAdapter durch FragmentStatePagerAdapter s ersetzte. 

5
Simon Raes

Xieyi hat die Gründe für die Ausnahme gut erklärt.

Hier ist die Lösung, die ich gefunden habe:

@Override
public void onPause() {
    super.onPause();

    for ( Fragment f : getChildFragmentManager().getFragments() ) {
        if ( f instanceof MyFragmentType ) {
            getChildFragmentManager().beginTransaction().remove( f ).commit();
        }
    }

}

Ich bekam die Ausnahme, wenn ich zu einem neuen Fragment ging und zurückkam. Stellen Sie sicher, dass Sie die Fragmente in Ihrer "cleaup" -Methode entfernen. Sie können dann Ihre Ansichten erneut laden, wenn Sie zu dem Fragment zurückkehren.

5
zMan

Obwohl die Lösung von Xieyi einwandfrei funktioniert, basiert sie auf einer Reflexion, was nicht wirklich eine gute Sache ist.

Als alternative Antwort gelang es mir, die untergeordneten Fragmente stattdessen mithilfe der Fragment-Transaktion zu entfernen, was meiner Meinung nach der bevorzugte Ansatz hier sein sollte.

Registrieren Sie zunächst einen Fragment-Lebenszyklus-Callback für den untergeordneten Fragment-Manager, damit wir weiterhin auf die untergeordneten Fragmente verweisen können.

private List<Fragment> mFragments = new ArrayList<>();

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    …(DO YOUR STUFF)
    //register a fragment lifecycle callback
    getChildFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {

        @Override
        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
            super.onFragmentCreated(fm, f, savedInstanceState);
            mFragments.add(f);
        }

        @Override
        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
            super.onFragmentDestroyed(fm, f);
            mFragments.remove(f);
        }
    }, false);

Als nächstes müssen wir nur die Fragmentliste durchlaufen, um sie aus dem untergeordneten Fragmentmanager in onCreateView zu entfernen:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    //remove child fragments before inflating view
    Iterator<Fragment> iterator = mFragments.iterator();
    while (iterator.hasNext()) {
        Fragment fragment = iterator.next();
        iterator.remove();
        getChildFragmentManager().beginTransaction()
                .remove(fragment)
                .commitNow();
    }
    return inflater.inflate(R.layout.your_fragment, container, false);
}

Das ist es.

4
Jagoan Neon

Wenn Sie eine Aktivität und ein Fragment haben, die in der Aktivität enthalten wären, haben Sie layout-xml mit einer ID, an der das Fragment platziert werden soll.

Wenn sich die ID nicht in der Aktivität befindet, wird diese Ausnahme angezeigt.

Beispiel:

getSupportFragmentManager().beginTransaction()
            .add(R.id.banner_container, bannerFragment)
            .commit();

wenn R.id.banner_container keine ID in Activity-Layout-xml ist, erhalten Sie die Ausnahme.

4
Skywalker

Ich hatte ein ähnliches Problem. Ich habe ein Fragment (HomeFragment) mit einem ListView und einem ViewPager, das einige Fragmente enthält. Alles funktioniert gut, wenn das Fragment startet. Wenn ich in der ListView auf eine Zeile klicke, wird es zu einem anderen Fragment führen, aber wenn ich will gehe zurück zum vorherigen Fragment (HomeFragment) Ich erhalte den Fehler Keine Ansicht für ID gefunden (Ich habe das Fragment mit addToBackStack).

Die Lösung bestand darin, in der onCreateView von HomeFragment zu überprüfen, ob die Ansicht und andere Komponenten bereits erstellt wurden, z. 

View view;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
  if (view == null) {
     //All the code in the onCreateView
     view = inflater.inflate(R.layout.item_banner, container, false);
     //...
  }
}

Mit dieser Überprüfung, wenn es um das vorherige Fragment geht, funktioniert alles einwandfrei. View, Pager, Liste usw. wurden bereits erstellt, so dass keine erneute Erstellung erforderlich ist. =)

3
ElCharro

Ich habe das gelöst, indem ich vermieden habe, die Instanz beim Erstellen der Ansicht mit setRetainInstance(true) beizubehalten. Sie können die Zeile kommentieren oder in false einfügen

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    setRetainInstance(false);
    return inflater.inflate(R.layout.fragment, container, false);
}
0
CORONEL Braian

Wenn sich Ihre ViewPager auf der Fragment befindet, verwenden Sie nicht getFragmentManager, sondern getChildFragmentManager.

0
Minum