Resuelva el problema de que Fragment está en blanco cuando se cambia la pantalla después de reescribir FragmentTabHost

Antecedentes del problema:

La estructura de la página de inicio de la aplicación de la empresa utiliza la estructura FragmentTabHost + Fragment. Para resolver el problema de que Fragment no se recarga cada vez que se cambia la pestaña, se reescribe la clase FragmentTabHost. El código central de la clase reescrita es el siguiente:

 private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
        TabInfo newTab = null;
        for (int i=0; i<mTabs.size(); i++) {
            TabInfo tab = mTabs.get(i);
            if (tab.tag.equals(tabId)) {
                newTab = tab;
            }
        }
        if (newTab == null) {
            throw new IllegalStateException("No tab known for tag " + tabId);
        }

        // 防止画面被回收后Fragment白屏
        if (isRestored) {
            if (mTabs != null && mTabs.size() > 0) {
                for (int i=0; i<mTabs.size(); i++) {
                    TabInfo tab = mTabs.get(i);
                    if (!tab.tag.equals(mLastTab.tag)) {
                        tab.fragment = null;
                    }
                }
            }
            isRestored = false;
        }


        if (mLastTab != newTab) {
            if (ft == null) {
                ft = mFragmentManager.beginTransaction();
            }
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
//                    ft.detach(mLastTab.fragment);
                    ft.hide(mLastTab.fragment);

                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mContext,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
//                    ft.attach(newTab.fragment);
                    ft.show(newTab.fragment);

                }
            }

            mLastTab = newTab;
        }
        return ft;
    }

La esencia es reemplazar ft.detach y ft.attach en el FragmentTabHost original con ft.hide y ft.show.

Esto logra el objetivo de no tener que volver al ciclo de vida del Fragmento al cambiar de pestaña. Pero también aparecieron efectos secundarios relacionados: después de que se recicló la Actividad del host, se volvió a abrir la Aplicación, y después de que se volvió a crear la Actividad del host, cuando se cambió la pestaña, aparecería la pantalla blanca Fragmento.

La razón del problema es que cuando se llama al método ft.show, el método onCreateView de Fragment no se volverá a ejecutar. Una vez que se recicla la Actividad general, los elementos de la pantalla en el Fragmento correspondiente también se reciclan. Si desea reconstruir los elementos de la pantalla, debe llamar al método onCreateView del Fragmento correspondiente.

Entonces, ¿cómo llamar al método onCreateView?

Después de un estudio cuidadoso del código en FragmentTabHost que reescribimos, encontramos que hay dos ideas principales:

La primera es agregar juicio al llamar a ft.show. Si se recicla y se muestra nuevamente, llame a ft.attch, pero hay un problema con esto, es decir, si hay más pestañas, debe marcar la pestañaHost Which Fragments han sido Attch, y los que no han sido Attached, la lógica es más complicada.

Otra forma es decirle a tabHost que borre la dirección de Fragmento almacenada previamente en caché después de que nuestra Actividad de host haya sido reciclada y reconstruida. De esta forma, Fragment se volverá a crear cuando cambiemos de pestaña, y el problema se resolverá de forma natural.

Finalmente, después de pesar y comparar, decidí usar la segunda forma para resolver el problema.

Ideas para realizar:

1. Agregue un campo booleano en nuestra propia clase FragmentTabHost reescrita: isRestored,

2. Establezca este campo en verdadero en el método de reconstrucción y recuperación de pantalla.

3. Utilice este campo para juzgar en el método doTabChanged. Si es verdadero, borre la caché del objeto del fragmento de otras pestañas en la clase tabHost excepto la pestaña que se mostrará.

El código central es el siguiente:

    /**
     * 防止画面回收后重启白屏
     */
    private boolean isRestored;
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());
        setCurrentTabByTag(ss.curTab);
        // 防止画面被回收后Fragment白屏
        isRestored = true;
    }
private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
        TabInfo newTab = null;
        for (int i=0; i<mTabs.size(); i++) {
            TabInfo tab = mTabs.get(i);
            if (tab.tag.equals(tabId)) {
                newTab = tab;
            }
        }
        if (newTab == null) {
            throw new IllegalStateException("No tab known for tag " + tabId);
        }

        // 防止画面被回收后Fragment白屏
        if (isRestored) {
            if (mTabs != null && mTabs.size() > 0) {
                for (int i=0; i<mTabs.size(); i++) {
                    TabInfo tab = mTabs.get(i);
                    if (!tab.tag.equals(mLastTab.tag)) {
                        tab.fragment = null;
                    }
                }
            }
            isRestored = false;
        }


        if (mLastTab != newTab) {
            if (ft == null) {
                ft = mFragmentManager.beginTransaction();
            }
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
//                    ft.detach(mLastTab.fragment);
                    ft.hide(mLastTab.fragment);

                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mContext,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
//                    ft.attach(newTab.fragment);
                    ft.show(newTab.fragment);

                }
            }

            mLastTab = newTab;
        }
        return ft;
    }

El código de clase completo es el siguiente:


import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TabHost;
import android.widget.TabWidget;

import net.proding.module.base.BaseFragment;

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

/**
 * 用于改善切换tab时不用重新加载
 */
public class CommonFragmentTabHost extends TabHost
        implements TabHost.OnTabChangeListener {
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
    private FrameLayout mRealTabContent;
    private Context mContext;
    private FragmentManager mFragmentManager;
    private int mContainerId;
    private OnTabChangeListener mOnTabChangeListener;
    private TabInfo mLastTab;
    private boolean mAttached;
    /**
     * 防止画面回收后重启白屏
     */
    private boolean isRestored;

    static final class TabInfo {
        private final String tag;
        private final Class<?> clss;
        private final Bundle args;
        private Fragment fragment;

        TabInfo(String _tag, Class<?> _class, Bundle _args) {
            tag = _tag;
            clss = _class;
            args = _args;
        }
    }

    static class DummyTabFactory implements TabContentFactory {
        private final Context mContext;

        public DummyTabFactory(Context context) {
            mContext = context;
        }

        @Override
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

    static class SavedState extends BaseSavedState {
        String curTab;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            curTab = in.readString();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeString(curTab);
        }

        @Override
        public String toString() {
            return "FragmentTabHost.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " curTab=" + curTab + "}";
        }

        public static final Creator<SavedState> CREATOR
                = new Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    public CommonFragmentTabHost(Context context) {
        // Note that we call through to the version that takes an AttributeSet,
        // because the simple Context construct can result in a broken object!
        super(context, null);
        initFragmentTabHost(context, null);
    }

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

    private void initFragmentTabHost(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs,
                new int[] { android.R.attr.inflatedId }, 0, 0);
        mContainerId = a.getResourceId(0, 0);
        a.recycle();

        super.setOnTabChangedListener(this);
    }

    private void ensureHierarchy(Context context) {
        // If owner hasn't made its own view hierarchy, then as a convenience
        // we will construct a standard one here.
        if (findViewById(android.R.id.tabs) == null) {
            LinearLayout ll = new LinearLayout(context);
            ll.setOrientation(LinearLayout.VERTICAL);
            addView(ll, new LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.FILL_PARENT));

            TabWidget tw = new TabWidget(context);
            tw.setId(android.R.id.tabs);
            tw.setOrientation(TabWidget.HORIZONTAL);
            ll.addView(tw, new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));

            FrameLayout fl = new FrameLayout(context);
            fl.setId(android.R.id.tabcontent);
            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));

            mRealTabContent = fl = new FrameLayout(context);
            mRealTabContent.setId(mContainerId);
            ll.addView(fl, new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.FILL_PARENT, 0, 1));
        }
    }

    /**
     * @deprecated Don't call the original TabHost setup, you must instead
     * call {@link #setup(Context, FragmentManager)} or
     * {@link #setup(Context, FragmentManager, int)}.
     */
    @Override
    @Deprecated
    public void setup() {
        throw new IllegalStateException(
                "Must call setup() that takes a Context and FragmentManager");
    }

    public void setup(Context context, FragmentManager manager) {
        ensureHierarchy(context);  // Ensure views required by super.setup()
        super.setup();
        mContext = context;
        mFragmentManager = manager;
        ensureContent();
    }

    public void setup(Context context, FragmentManager manager, int containerId) {
        ensureHierarchy(context);  // Ensure views required by super.setup()
        super.setup();
        mContext = context;
        mFragmentManager = manager;
        mContainerId = containerId;
        ensureContent();
        mRealTabContent.setId(containerId);

        // We must have an ID to be able to save/restore our state.  If
        // the owner hasn't set one at this point, we will set it ourself.
        if (getId() == View.NO_ID) {
            setId(android.R.id.tabhost);
        }
    }

    private void ensureContent() {
        if (mRealTabContent == null) {
            mRealTabContent = (FrameLayout)findViewById(mContainerId);
            if (mRealTabContent == null) {
                throw new IllegalStateException(
                        "No tab content FrameLayout found for id " + mContainerId);
            }
        }
    }

    @Override
    public void setOnTabChangedListener(OnTabChangeListener l) {
        mOnTabChangeListener = l;
    }

    public void addTab(TabSpec tabSpec, Class<?> clss, Bundle args) {
        tabSpec.setContent(new DummyTabFactory(mContext));
        String tag = tabSpec.getTag();

        TabInfo info = new TabInfo(tag, clss, args);

        if (mAttached) {
            // If we are already attached to the window, then check to make
            // sure this tab's fragment is inactive if it exists.  This shouldn't
            // normally happen.
            info.fragment = mFragmentManager.findFragmentByTag(tag);
            if (info.fragment != null && !info.fragment.isDetached()) {
                FragmentTransaction ft = mFragmentManager.beginTransaction();
                ft.detach(info.fragment);
                ft.commit();
            }
        }

        mTabs.add(info);
        addTab(tabSpec);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        String currentTab = getCurrentTabTag();

        // Go through all tabs and make sure their fragments match
        // the correct state.
        FragmentTransaction ft = null;
        for (int i=0; i<mTabs.size(); i++) {
            TabInfo tab = mTabs.get(i);
            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
            if (tab.fragment != null && !tab.fragment.isDetached()) {
                if (tab.tag.equals(currentTab)) {
                    // The fragment for this tab is already there and
                    // active, and it is what we really want to have
                    // as the current tab.  Nothing to do.
                    mLastTab = tab;
                } else {
                    // This fragment was restored in the active state,
                    // but is not the current tab.  Deactivate it.
                    if (ft == null) {
                        ft = mFragmentManager.beginTransaction();
                    }
                    ft.detach(tab.fragment);
                }
            }
        }

        // We are now ready to go.  Make sure we are switched to the
        // correct tab.
        mAttached = true;
        ft = doTabChanged(currentTab, ft);
        if (ft != null) {
            ft.commit();
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mAttached = false;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.curTab = getCurrentTabTag();
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());
        setCurrentTabByTag(ss.curTab);
        // 防止画面被回收后Fragment白屏
        isRestored = true;
    }

    @Override
    public void onTabChanged(String tabId) {
        if (mAttached) {
            FragmentTransaction ft = doTabChanged(tabId, null);
            if (ft != null) {
                ft.commit();
            }
        }
        if (mOnTabChangeListener != null) {
            mOnTabChangeListener.onTabChanged(tabId);
        }
    }

    private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
        TabInfo newTab = null;
        for (int i=0; i<mTabs.size(); i++) {
            TabInfo tab = mTabs.get(i);
            if (tab.tag.equals(tabId)) {
                newTab = tab;
            }
        }
        if (newTab == null) {
            throw new IllegalStateException("No tab known for tag " + tabId);
        }

        // 防止画面被回收后Fragment白屏
        if (isRestored) {
            if (mTabs != null && mTabs.size() > 0) {
                for (int i=0; i<mTabs.size(); i++) {
                    TabInfo tab = mTabs.get(i);
                    if (!tab.tag.equals(mLastTab.tag)) {
                        tab.fragment = null;
                    }
                }
            }
            isRestored = false;
        }


        if (mLastTab != newTab) {
            if (ft == null) {
                ft = mFragmentManager.beginTransaction();
            }
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
//                    ft.detach(mLastTab.fragment);
                    ft.hide(mLastTab.fragment);

                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mContext,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
//                    ft.attach(newTab.fragment);
                    ft.show(newTab.fragment);

                }
            }

            mLastTab = newTab;
        }
        return ft;
    }

    public void reloadTabFragment(String tabId) {
        List<Fragment> fragments = mFragmentManager.getFragments();

        if(fragments == null) return;

        for (Fragment f : fragments){

            if(f != null && f.getArguments() != null && tabId.equals(f.getArguments().getString("tabId"))){
                ((BaseFragment) f).reload();
            }
        }
    }

    public Fragment getFragment(String tabId) {
        List<Fragment> fragments = mFragmentManager.getFragments();

        Fragment fragment = null;
        if(fragments == null) return fragment;

        for (Fragment f : fragments){

            if(f != null && f.getArguments() != null && tabId.equals(f.getArguments().getString("tabId"))){
                fragment = f;
                break;
            }
        }

        return fragment;
    }
}

El problema pro-test se ha resuelto. Los amigos que tienen poco tiempo pueden llevar a cabo directamente la doctrina hasta el final.

¡Gracias!


Supongo que te gusta

Origin blog.csdn.net/awy1988/article/details/80590734
Recomendado
Clasificación