web-dev-qa-db-ja.com

FragmentTransactionの非表示/表示が機能しない場合があります

下部のナビゲーションタブでフラグメントを変更するアクティビティがあります。これらのタブを前後にクリックすると、ある時点で機能しなくなります。いくつかのログを入れたので、コードは問題なく実行されます。しかし、フラグメントは切り替えられていません。

コードはkotlinにありますが、かなり単純です

fun showTabFragment(tag: String) {
        val currentFragment: Fragment? = supportFragmentManager.fragments?.lastOrNull()
        var fragment = supportFragmentManager.findFragmentByTag(tag)
        val fragmentExists = fragment != null
        if (fragment == null) {
            when (tag) {
                TAG_LOGBOOK -> fragment = LogbookFragment()
                TAG_RECIPES -> fragment = RecipesFragment()
                TAG_PROFILE -> fragment = ProfileFragment()
                else -> fragment = MeetingPlacesFragment()
            }
        }

        val transaction = supportFragmentManager.beginTransaction()

        if (currentFragment != null) {
            Log.i("jacek", "hiding " + currentFragment.javaClass.simpleName)
            transaction.hide(currentFragment)
        }

        if (fragmentExists) {
            Log.i("jacek", "showing " + fragment.javaClass.simpleName)
            transaction.show(fragment)
        } else {
            Log.i("jacek", "adding " + fragment.javaClass.simpleName)
            transaction.add(R.id.container, fragment, tag)
        }

        transaction.commit()
    }

破片はかなり重いです。私はいくつかの軽量のものを試してみますが、それでも私の意見では問題にはならないはずです。他に試すことができるものはありますか?

最新のサポートライブラリを使用しています-25.2.0また、フラグメントを再作成せずにクロスフェードアニメーションを追加することが重要であるため、フラグメントを置き換えることには興味がありません。

11
Jacek Kwiecień

非表示または表示したいフラグメントの同じインスタンスを再利用する必要があります。

private fun replaceFragment(fragment: Fragment) {
    supportFragmentManager.beginTransaction().apply {
        if (fragment.isAdded) {
            show(fragment)
        } else {
            add(R.id.fmFragmentContainer, fragment)
        }

        supportFragmentManager.fragments.forEach {
            if (it != fragment && it.isAdded) {
                hide(it)
            }
        }
    }.commit()
}
11
dtunctuncer

@ALiの答えは良いですが、5つのフラグメントがあると想像してみてください。これは、フラグメントを表示/非表示にする別の方法です。

    // in BaseFragment
    public abstract String getTAG();

    //in FragmentA, FragmentB and FragmentC
    public String getTAG(){
        return TAG;
    }

    //Activity containing the fragments
    //Android.support.v4.app.Fragment;    
    private FragmentA fragmentA; //inherited BaseFragment
    private FragmentB fragmentB; //inherited BaseFragment
    private FragmentC fragmentC; //inherited BaseFragment
    private ConcurrentHashMap<String,BaseFragment> mapOfAddedFragments = new ConcurrentHashMap<>();


    /**
     * Displays fragment A
     */
    private void displayFragmentA() {
        displayFragment(fragmentA)
    }

    /**
     * Displays  fragment B
     */
    private void displayFragmentB() {
       displayFragment(fragmentB)
    }

    /**
     * Displays  fragment C
     */
    private void displayFragmentC() {
        displayFragment(fragmentC)
    }


     /**
     * Loads a fragment using show a fragment
     * @param fragment
     */
    private void displayFragment(BaseFragment fragment){
        if(!mapOfAddedFragments.containsKey(fragment.getTAG()))
            mapOfAddedFragments.put(fragment.getTAG(), fragment);

        showFragment(fragment.getTAG(), R.id.containerBody);
    }

    /**
     * Displays a fragment and hides all the other ones
     * @param fragmentTag is the tag of the fragment we want to display
     */
    private void showFragment(String fragmentTag, @IdRes int containerViewId){
        FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
        BaseFragment fragment = null;

        fragment = mapOfAddedFragments.get(fragmentTag);
        if(fragment != null) {
            if (fragment.isAdded())
                ft.show(fragment);
            else { //fragment needs to be added to the frame container
                ft.add(containerViewId, fragment, fragment.getTAG());
            }
        }
        else //the chosen fragment doesn't exist
            return;

        //we hide the other fragments
        for (ConcurrentHashMap.Entry<String, BaseFragment> entry : mapOfAddedFragments.entrySet()){
            if(!entry.getKey().equals(fragmentTag)){
                BaseFragment fragmentTemp = entry.getValue();
                // Hide the other fragments
                if(fragmentTemp != null)
                    if(fragmentTemp.isAdded())
                        ft.hide(fragmentTemp);
            }
        }

        //commit changes
        ft.commit();
    }

そして、それらをインスタンス化するには、アクティビティのonCreate()メソッドでこれを行うことができます。

//don't forget to get the .TAG elsewhere before using them here
    //never call them directly
    private void instantiateFragments(Bundle inState) {
        if (inState != null) {
            fragmentA = inState.containsKey(FragmentA.TAG) ?
                    (FragmentA) getSupportFragmentManager().getFragment(inState, FragmentA.TAG):
                    FragmentA.newInstance(FragmentA.TAG,"0");

            fragmentB = inState.containsKey(FragmentB.TAG) ?
                    (FragmentB) getSupportFragmentManager().getFragment(inState, FragmentB.TAG):
                    FragmentB.newInstance(FragmentB.TAG,"1");        

            fragmentc = inState.containsKey(FragmentC.TAG) ?
                    (FragmentC) getSupportFragmentManager().getFragment(inState, FragmentC.TAG):
                    FragmentC.newInstance(FragmentC.TAG,"2");         
        }
        else{
            fragmentA = FragmentA.newInstance(FragmentA.TAG,"0");
            fragmentB = FragmentB.newInstance(FragmentB.TAG,"1");
            fragmentc = FragmentC.newInstance(FragmentC.TAG,"2");
        }
    }

Shujaat ALi Khanの質問に従って編集:

BaseFragmentはsupport4フラグメントを拡張します。

public abstract class BaseFragment extends Fragment {
    public abstract String getTAG();
    //whatever we can add to be inherited
}

FragmentAの例:

public class FragmentA extends BaseFragment {
    // Store instance variables
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    private String mParam1;
    private String mParam2;

    public static final String TAG = "FragmentA";

    // newInstance constructor for creating fragment with arguments
    public static FragmentA newInstance(String param1, String param2) {
        FragmentA fragment = new FragmentA();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    // Store instance variables based on arguments passed
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    // Inflate the view for the fragment based on layout XML
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragmentA, container, false);
        return view;
    }

    //other lifecycle methods

    @Override
    public String getTAG() {
        return TAG;
    }
}

最後にR.id.containerBodyは、これらのフラグメントを含むアクティビティ内のフラグメントを含むFrameLayoutのIDです。

6
Maxime Claude

ここでの問題は、「現在の」フラグメントを非表示にしている場合でも、メモリにロードされている他のフラグメントがあり、一貫性のない動作を与えることです。

表示したいフラグメントを除くすべてのフラグメントを非表示にすることで、これを修正できるはずです。

この答えに感謝します。 Androidで非表示フラグメントを表示

例えば:

private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    fragmentA = FragmentA.newInstance();
    fragmentB = FragmentB.newInstance();
    fragmentC = FragmentC.newInstance();

}

protected void displayFragmentA() {

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    if (fragmentA.isAdded()) { 
        ft.show(fragmentA);
    } else { 
        ft.add(R.id.fragement_container, fragmentA);
    }

    if (fragmentB.isAdded()) { ft.hide(fragmentB); }

    if (fragmentC.isAdded()) { ft.hide(fragmentC); }

    ft.commit();
}

同様に、displayFragmentB()およびdisplayFragmentC()の関数を作成する必要があります

4
Ali