简体   繁体   English

使用片段时出现ViewPager错误,“指定的子代已具有父代”

[英]ViewPager error when using fragments, “The specified child already has a parent”

I'm making an intro section to my Android app using a ViewPager containing a series of fragments. 我正在使用包含一系列片段的ViewPager对我的Android应用进行简介。 The fragment code is: 片段代码为:

package testing.testmusic;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

import butterknife.Bind;
import butterknife.ButterKnife;

public class IntroPageFragment extends Fragment {
    /**
     * Used during logging to identify this class.
     */
    private static final String TAG = "[IntroPageFragment]";

    /**
     * Used to prevent premature attempts to update the UI.
     */
    private boolean viewsBoundToInstanceVariables = false;

    /**
     * Holds and displays a {@code Bitmap} at the top of the screen. This {@code View} is drawn in
     * front of {@code backImageHolder} but behind {@code contentHolder} by default.
     */
    @Bind(R.id.intro_fragment_imageFront) protected ImageView frontImageHolder;

    /**
     * Holds and displays a {@code Bitmap} at the top of the screen. This {@code View} is drawn
     * behind both {@code backImageHolder} and {@code contentHolder} by default.
     */
    @Bind(R.id.intro_fragment_imageBack) protected ImageView backImageHolder;

    /**
     * Displays custom content at the centre of the screen. This {@code View} is drawn in front of
     * both {@code backImageHolder} and {@code contentHolder} by default.
     */
    @Bind(R.id.intro_fragment_content) protected FrameLayout contentHolder;

    /**
     * The image to display in {@code frontImageHolder}.
     */
    private Bitmap frontImage = null;

    /**
     * The image to display in {@code backImageHolder}.
     */
    private Bitmap backImage = null;

    /**
     * The content to display in {@code contentHolder}.
     */
    private View content = null;

    /**
     * Constructs a new IntroPageFragment instance. Avoid calling this method directly, instead
     * call {@link #newInstance()}.
     */
    public IntroPageFragment() {}

    /**
     * @return a new IntroPageFragment instance.
     */
    public static IntroPageFragment newInstance() {
        return new IntroPageFragment();
    }

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
            final Bundle savedInstanceState) {

        final View fragmentRoot = inflater.inflate(R.layout.fragment_intro_page, container, false);
        ButterKnife.bind(this, fragmentRoot);
        viewsBoundToInstanceVariables = true;
        updateViewsToReflectData();
        return fragmentRoot;
    }

    /**
     * Displays the provided image at the top of the screen. The image will be drawn in front of
     * any image supplied to {@link #setBackImage(Bitmap)}, but behind any {@code Views} supplied
     * to {@link #setContent(View)} (by default).
     *
     * @param frontImage the image to display
     * @return this {@code IntroPageFragment} to allow method chaining
     */
    public IntroPageFragment setFrontImage(final Bitmap frontImage) {
        this.frontImage = frontImage;
        updateViewsToReflectData();
        return this;
    }

    /**
     * @return the ImageView which holds the image supplied to {@link #setFrontImage(Bitmap)}
     */
    public ImageView getFrontImageHolder() {
        return frontImageHolder;
    }

    /**
     * Displays the provided image at the top of the screen. The image will be drawn in behind of
     * any image supplied to {@link #setBackImage(Bitmap)}, and behind any {@code Views} supplied
     * to {@link #setContent(View)} (by default).
     *
     * @param backImage the image to display
     * @return this {@code IntroPageFragment} to allow method chaining
     */
    public IntroPageFragment setBackImage(final Bitmap backImage) {
        this.backImage = backImage;
        updateViewsToReflectData();
        return this;
    }

    /**
     * @return the ImageView which holds the image supplied to {@link #setBackImage(Bitmap)}
     */
    public ImageView getBackImageHolder() {
        return backImageHolder;
    }

    /**
     * Adds the provided content to a FrameLayout at the centre of the screen. The content will be
     * drawn in front of any images supplied to {@link #setFrontImage(Bitmap)} and {@link
     * #setBackImage (Bitmap)} (by default).
     *
     * @param content the content to display on this page
     * @return this {@code IntroPageFragment} to allow method chaining
     */
    public IntroPageFragment setContent(final View content) {
        this.content = content;
        updateViewsToReflectData();
        return this;
    }

    /**
     * @return the FrameLayout which displays the content supplied to {@link #setContent(View)}
     */
    public FrameLayout getContentHolder() {
        return contentHolder;
    }

    /**
     * Updates all views to use the images and content supplied to {@link #setFrontImage(Bitmap)},
     * {@link #setBackImage(Bitmap)} and {@link #setContent(View)}.
     */
    public void updateViewsToReflectData() {
        if (viewsBoundToInstanceVariables) {
            frontImageHolder.setImageBitmap(frontImage);
            backImageHolder.setImageBitmap(backImage);
            contentHolder.removeAllViews();

            if (content != null) {
                contentHolder.addView(content);
                contentHolder.invalidate();
            }
        }
    }
}

The fragment layout is: 片段布局为:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
        android:id="@+id/intro_activity_viewpager"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="false"
        tools:context="testing.testmusic.IntroActivity">
</android.support.v4.view.ViewPager>

The app launches and displays fragments as expected, however when scrolling through pages, the third scroll (either forward or back, doesn't make a difference) causes the following error: 该应用程序将启动并按预期显示碎片,但是在滚动页面时,第三次滚动(向前或向后没有区别)会导致以下错误:

FATAL EXCEPTION: main
Process: testing.testmusic, PID: 12404
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4309)
at android.view.ViewGroup.addView(ViewGroup.java:4145)
at android.view.ViewGroup.addView(ViewGroup.java:4086)
at android.view.ViewGroup.addView(ViewGroup.java:4059)
at testing.testmusic.IntroPageFragment.updateViewsToReflectData(IntroPageFragment.java:158)
at testing.testmusic.IntroPageFragment.onCreateView(IntroPageFragment.java:80)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1962)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
at android.support.v4.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1426)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:728)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:570)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1106)
at android.support.v4.view.ViewPager.populate(ViewPager.java:952)
at android.support.v4.view.ViewPager$3.run(ViewPager.java:251)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

The trace leads to method updateViewsToReflectData in IntroPageFragment , specifically at contentHolder.addView(content) . 跟踪导致updateViewsToReflectData中的IntroPageFragment方法,特别是在contentHolder.addView(content) I don't understand why this error is occurring since contentHolder.removeAllViews() is always called directly prior. 我不明白为什么会发生此错误,因为总是总是直接调用contentHolder.removeAllViews() Why is this error occurring? 为什么会发生此错误?

In case it helps, here is my activity code: 如果有帮助,这是我的活动代码:

package testing.testmusic;

        import android.graphics.Bitmap;
        import android.os.Bundle;
        import android.support.v4.view.ViewPager;
        import android.support.v7.app.AppCompatActivity;
        import android.widget.ImageView;

        import butterknife.Bind;
        import butterknife.ButterKnife;

        public class IntroActivity extends AppCompatActivity {
            /**
             * Used during logging to identify this class.
             */
            private static final String TAG = "[IntroActivity]";

            /**
             * Displays an interactive multi-page intro screen to the user.
             */
            @Bind(R.id.intro_activity_viewpager) protected ViewPager viewPager;

            /**
             * Adapts the elements of {@code fragments} to views in {@code viewPager}.
             */
            private IntroPageAdapter adapter = new IntroPageAdapter(getSupportFragmentManager());

            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_introduction);
                ButterKnife.bind(this);
                viewPager.setAdapter(adapter);
                createPages();
            }

            /**
             * Populates {@code fragments} with the pages to display in {@code viewPager}.
             */
            private void createPages() {
                Bitmap rainbow = BitmapHelper
                        .decodeSampledBitmapFromResource(getResources(), R.raw.rainbow, 100, 100);
                Bitmap frontDots = BitmapHelper
                        .decodeSampledBitmapFromResource(getResources(), R.raw.front, 1000, 1000);
                Bitmap backDots = BitmapHelper
                        .decodeSampledBitmapFromResource(getResources(), R.raw.back, 1000, 1000);

                for (int i = 0; i < 10; i++) {
                    IntroPageFragment page = IntroPageFragment.newInstance();
                    page.setFrontImage(frontDots);
                    page.setBackImage(backDots);
                    ImageView content = new ImageView(this);
                    content.setImageBitmap(rainbow);
                    page.setContent(content);
                    adapter.addPage(page);
                }
            }
        }

and here is my adapter code: 这是我的适配器代码:

package testing.testmusic;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.ArrayList;
import java.util.List;

public class IntroPageAdapter extends FragmentPagerAdapter {
    List<IntroPageFragment> fragments;

    public IntroPageAdapter(FragmentManager fm) {
        super(fm);
        this.fragments = new ArrayList<>();
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

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

    public void addPage(IntroPageFragment page) {
        if (page != null) {
            fragments.add(page);
            notifyDataSetChanged();
        }
    }

    public void removePage(IntroPageFragment page) {
        fragments.remove(page);
        notifyDataSetChanged();
    }
}

Changing method updateViewsToReflectData to the following fixed the problem: 将方法updateViewsToReflectData更改为以下内容可解决此问题:

public void updateViewsToReflectData() {
        if (viewsBoundToInstanceVariables) {
            frontImageHolder.setImageBitmap(frontImage);
            backImageHolder.setImageBitmap(backImage);
            contentHolder.removeAllViewsInLayout();

            if (content != null) {
                if (content.getParent() == null) {
                    contentHolder.addView(content);
                    contentHolder.invalidate();
                }
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 错误:指定的孩子已经有一个父母 - Error: The specified child already has a parent 指定的子代已具有父代异常错误 - The specified child already has a parent Exception Error 指定的孩子已经有父母 - Specified child already has parent 指定的孩子已经有一个父母。 你必须首先对孩子的父母打电话removeView()(与片段和ViewPager活动) - The specified child already has a parent. You must call removeView() on the child's parent first (Activity with fragment and ViewPager) FrameLayout IllegalStateException:指定的子代已经有一个父代 - FrameLayout IllegalStateException: The specified child already has a parent 应用程式当机(指定的子代已经有父母) - App crash (The Specified child already has a parent) Android IllegalStateException:指定的子代已经有一个父代 - Android IllegalStateException: The specified child already has a parent 指定的子代已经有一个父代-实现FloatingActionMenu - The specified child already has a parent - implementing FloatingActionMenu 指定的子级已有父级异常 - The specified child already has a parent Exception 自定义对话框 - 指定的孩子已经有一个父母 - custom dialog - specified child already has a parent
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM