Android Action Bar Tab with scrollview made duplicate view after orientation change

荒凉一梦 提交于 2019-11-30 16:49:51

问题


I have a very simple code where I use Action Bar with tab fragments. It works fine after load, but after orientation change it goes crazy. The old fragment also visible (why?).

Sorry for Hungarian texts on the image, but I hope it doesn't matter.

I attach the code, maybe it helps to solve this problem.

Main activity:

public class Main extends Activity
{
    private static ActionBar actionBar;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // setup action bar for tabs
        actionBar = getActionBar();
        actionBar.removeAllTabs();
        if (actionBar.getTabCount() == 0)
        {
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

            Tab tab = actionBar.newTab().setText(R.string.starter).setTabListener(new TabListener<Starter>(this, "starter", Starter.class));
            actionBar.addTab(tab);

            tab = actionBar.newTab().setText(R.string.newword).setTabListener(new TabListener<NewWord>(this, "newwod", NewWord.class));
            actionBar.addTab(tab);

            tab = actionBar.newTab().setText(R.string.feedback).setTabListener(new TabListener<Feedback>(this, "feedback", Feedback.class));
            actionBar.addTab(tab);
        }

        if (savedInstanceState != null)
        {
            actionBar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
    }
}

TabListener (same as Google example):

public class TabListener<T extends Fragment> implements android.app.ActionBar.TabListener
{
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    /**
     * Constructor used each time a new tab is created.
     * 
     * @param activity
     *            The host Activity, used to instantiate the fragment
     * @param tag
     *            The identifier tag for the fragment
     * @param clz
     *            The fragment's Class, used to instantiate the fragment
     */
    public TabListener(Activity activity, String tag, Class<T> clz)
    {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft)
    {
        // User selected the already selected tab. Usually do nothing.

    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft)
    {
        // Check if the fragment is already initialized
        if (mFragment == null)
        {
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else
        {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }

    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft)
    {
        if (mFragment != null)
        {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

}

Fragment:

public class Starter extends Fragment
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        setRetainInstance(false);
        return inflater.inflate(R.layout.newword, container, false);
    }
}

and the layout XML:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="5dp" >

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="@string/newword"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <EditText
            android:id="@+id/newwordtext"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="@string/wordhint" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="@string/description"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/descriptionwordtext"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="@string/descriptionhint"
            android:inputType="textMultiLine"
            android:minLines="4" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="@string/origin"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/origintext"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="@string/originhint" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="@string/source"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/sourcetext"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="@string/sourcehint" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="@string/name"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/nametext"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:inputType="textPersonName" />

        <Button
            android:id="@+id/sendbutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="@string/send" />
    </LinearLayout>

</ScrollView>

Thank You in Advance!


回答1:


I found a usable answer in other question.

I need to modify my TabListener (I moved it into my Main activity class as inner class):

private class TabListener<T extends Fragment> implements android.app.ActionBar.TabListener
    {
        private Fragment mFragment;
        private final Activity mActivity;
        private final String mTag;
        private final Class<T> mClass;

        /**
         * Constructor used each time a new tab is created.
         * 
         * @param activity
         *            The host Activity, used to instantiate the fragment
         * @param tag
         *            The identifier tag for the fragment
         * @param clz
         *            The fragment's Class, used to instantiate the fragment
         */
        public TabListener(final Activity activity, final String tag, final Class<T> clz)
        {
            mActivity = activity;
            mTag = tag;
            mClass = clz;
        }

        @Override
        public void onTabReselected(final Tab tab, final FragmentTransaction ft)
        {
            // User selected the already selected tab. Usually do nothing.
        }

        @Override
        public void onTabSelected(final Tab tab, final FragmentTransaction ft)
        {
            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment == null)
            {
                mFragment = Fragment.instantiate(mActivity, mClass.getName());
                ft.add(android.R.id.content, mFragment, mTag);
            }
            else
            {
                ft.attach(mFragment);
            }
        }

        @Override
        public void onTabUnselected(final Tab tab, final FragmentTransaction ft)
        {
            if (mFragment != null)
            {
                ft.detach(mFragment);
            }
        }
    }

So before I add fragment(again), I check it existance (and get its reference) and if it exists I attach it only.




回答2:


Try using ft.replace(R.id.content, mFragment) in place of ft.attach(mFragment); in onTabSelected function




回答3:


I found a very simple solution to avoid fragments duplication:

    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        Fragment currentFragment = getFragmentManager().findFragmentByTag(CURRENT_FRAGMENT_TAG);
        if (currentFragment == null || !currentFragment.getClass().equals(mFragment.getClass())) {
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            ft.add(android.R.id.content, mFragment, CURRENT_FRAGMENT_TAG);
        }
    }

    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
        ft.remove(mFragment);
    }

The key of the solution is in the condition:

currentFragment == null || !currentFragment.getClass().equals(mFragment.getClass())

This condition is valid ONLY if the classes of the fragments are different. In case you have different instances of the same class you have to put an extra attribute in your fragments to recognize his function (or the association with and to make the condition !currentFragment.getClass().equals(mFragment.getClass()) true: for example you can use the FragmentTransaction tag feature.

Bye, Alex.




回答4:


public void onTabSelected(Tab tab, FragmentTransaction ft)
    {
        // Check if the fragment is already initialized
        if (mFragment == null)
        {
            if(ft.findFragmentById(android.R.id.content) == null){
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
            }
        } else
        {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }

    }

Something like that, because your problem is that your adding the same fragment twice, we just have to find where...




回答5:


I solved this by just looking up the fragment in the tab listener constructor.

public class TabListener<T extends Fragment> implements ActionBar.TabListener 
{
    private Fragment fragment;
    private final SherlockFragmentActivity activity;
    private final String tag;
    private final Class<T> clazz;

    public TabListener(SherlockFragmentActivity activity, String tag, Class<T> clazz) 
    {
        this.activity = activity;
        this.tag = tag;
        this.clazz = clazz;

        FragmentManager manager = ((SherlockFragmentActivity) activity).getSupportFragmentManager();
        fragment = manager.findFragmentByTag(tag);
    }
...
}


来源:https://stackoverflow.com/questions/8885594/android-action-bar-tab-with-scrollview-made-duplicate-view-after-orientation-cha

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!