fragment重叠的完美解决方案

Fragment的应用场景有很多,比如我们底部一个导航栏,点击导航项显示不同的fragment,或者和ViewPager配合使用等.

比如这样:

package com.example.yzq.testfragment;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;

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

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {


    private FrameLayout contentLayout;//容器
    private BottomNavigationView mainBottomView;//底部导航


    private HomeFragment homeFragment;
    private DashboardFragment dashboardFragment;
    private NoticeFragment noticeFragment;


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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        L.i("onCreate");
        initView();
        initFragment();

    }

    private void initView() {
        contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
        mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);

        mainBottomView.setOnNavigationItemSelectedListener(this);
    }

    private void initFragment() {
       /* 默认显示home  fragment*/
        homeFragment = new HomeFragment();
        addFragment(homeFragment);
        showFragment(homeFragment);


    }

    /*添加fragment*/
    private void addFragment(Fragment fragment) {

        /*判断该fragment是否已经被添加过  如果没有被添加  则添加*/
        if (!fragment.isAdded()) {
            getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
        /*添加到 fragmentList*/
            fragmentList.add(fragment);
        }


    }


    /*显示fragment*/
    private void showFragment(Fragment fragment) {


        for (Fragment frag : fragmentList) {

            if (frag != fragment) {
                /*先隐藏其他fragment*/
                getSupportFragmentManager().beginTransaction().hide(frag).commit();
            }
        }
        getSupportFragmentManager().beginTransaction().show(fragment).commit();

    }


    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {
            case R.id.navigation_home:

                if (homeFragment == null) {
                    homeFragment = new HomeFragment();
                }


                addFragment(homeFragment);
                showFragment(homeFragment);
                break;

            case R.id.navigation_dashboard:
                if (dashboardFragment == null) {
                    dashboardFragment = new DashboardFragment();
                }
                addFragment(dashboardFragment);
                showFragment(dashboardFragment);
                break;

            case R.id.navigation_notifications:

                if (noticeFragment == null) {
                    noticeFragment = new NoticeFragment();
                }
                addFragment(noticeFragment);
                showFragment(noticeFragment);
                break;


        }
        return true;
    }
}

上面的代码很简单,就是一个对fragment的添加,隐藏,显示的操作。

示例图:

这里写图片描述

但是当我们横竖屏切换时,或者锁屏过段时间再进入app时,很可能会遇见重叠的问题.

比如这样:

这里写图片描述

旋转了个屏幕,发现就已经重叠了。

出现这种问题的原因是:当我们旋转屏幕的时候,activity会被销毁并重新创建,并且在销毁之前执行了onSaveInstanceState(Bundle outState)这个方法。这个方法会保存activity的一些信息,其中就包括添加过的fragment,当activity被重新创建时,会初始化其中的变量,这个时候点击底部导航的话会重新去添加fragment,也就导致了重叠的问题。

首先,我们来打印一下生命周期方法的log,看看是不是这样的。

package com.example.yzq.testfragment;

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;

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

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {


    private FrameLayout contentLayout;//容器
    private BottomNavigationView mainBottomView;//底部导航


    private HomeFragment homeFragment;
    private DashboardFragment dashboardFragment;
    private NoticeFragment noticeFragment;


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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        L.i("MainActivity  onCreate");
        initView();
        initFragment();

    }

    private void initView() {
        contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
        mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);

        mainBottomView.setOnNavigationItemSelectedListener(this);
    }

    private void initFragment() {
       /* 默认显示home  fragment*/
        homeFragment = new HomeFragment();
        addFragment(homeFragment);
        showFragment(homeFragment);


    }

    /*添加fragment*/
    private void addFragment(Fragment fragment) {

        /*判断该fragment是否已经被添加过  如果没有被添加  则添加*/
        if (!fragment.isAdded()) {
            getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
        /*添加到 fragmentList*/
            fragmentList.add(fragment);
        }


    }


    /*显示fragment*/
    private void showFragment(Fragment fragment) {


        L.i("fragmentList數量:" + fragmentList.size());
        for (Fragment frag : fragmentList) {

            if (frag != fragment) {
                /*先隐藏其他fragment*/
                L.i("隱藏" + fragment);
                getSupportFragmentManager().beginTransaction().hide(frag).commit();
            }
        }
        getSupportFragmentManager().beginTransaction().show(fragment).commit();

    }


    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {
            case R.id.navigation_home:

                if (homeFragment == null) {
                    L.i("homeFragment 为空  创建");
                    homeFragment = new HomeFragment();
                }


                addFragment(homeFragment);
                showFragment(homeFragment);
                break;

            case R.id.navigation_dashboard:
                if (dashboardFragment == null) {
                    L.i("dashboardFragment 为空  创建");
                    dashboardFragment = new DashboardFragment();
                }
                addFragment(dashboardFragment);
                showFragment(dashboardFragment);
                break;

            case R.id.navigation_notifications:

                if (noticeFragment == null) {
                    L.i("noticeFragment 为空  创建");
                    noticeFragment = new NoticeFragment();
                }
                addFragment(noticeFragment);
                showFragment(noticeFragment);
                break;


        }
        return true;
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        L.i("MainActivity onSaveInstanceState");
        super.onSaveInstanceState(outState);
    }


    @Override
    protected void onStart() {
        super.onStart();
        L.i("MainActivity onStart");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        L.i("MainActivity onRestart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        L.i("MainActivity onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        L.i("MainActivity onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        L.i("MainActivity onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        L.i("MainActivity onDestroy");
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {

        super.onConfigurationChanged(newConfig);
        L.i("MainActivity onConfigurationChanged");
    }
}

上面的代码只是添加了打印的log。我们来看看打印的日志。

这里写图片描述

动图有点看不清,我们来看看截屏。

一开始进去时走的就是正常的oncreate到onresume方法。

这里写图片描述

旋转屏幕时,activity是会被onDestory的,在onDestory之前执行了onSaveInstanceState(Bundle outState)这个方法,这个方法保存了activity的一些状态信息,其中就包括所添加过的fragment。

这里写图片描述

然后,activity会被重新创建,同样的,之前所添加过的fragment也会被重新创建,而且是默认显示的,当我们点击底部导航的时候,由于activity重新创建了,所以其中的fragment变量是为空的,此时点击导航的时候还会创建新的fragment,这样 ,就导致了重叠的问题。

这里写图片描述

解决的办法:

其实解决办法很简单,上面的一堆只是让我们了解为什么会出现这种问题。
1.想办法不让activity保存信息。(不推荐)


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        L.i("MainActivity onSaveInstanceState");
        //super.onSaveInstanceState(outState);
    }

这里写图片描述

我们可以直接注释掉super.onSaveInstanceState(outState);
这样一来activity就不会保存信息了,但是,从图片上也可以看到,当屏幕旋转时,每次都会跟重新打开一样。如果你旋转屏幕之前显示的是noticeFragment时,屏幕切换后就会自动切换到默认的homeFragment上。这并不是我们想要的结果。

2。旋转屏幕时不让activity走生命周期方法(推荐)

这个方法最简单也最省事,只需要在相应的activity中声明
android:configChanges=”keyboardHidden|orientation|screenSize”> 即可。

        <activity
            android:name=".MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

声明这个属性后,当我们切换屏幕时,也就不会在走activity的生命周期方法了,也就不会造成fragment重叠的问题了。

还有一种可能也会造成fragment重叠的问题,就是当内存不足时activity被系统回收时,再次进入也会造成重叠的问题,原因也是因为onSaveInstanceState(outState);方法保存了activity的一些数据。

因为现在的手机内存已经很大了,可以说出现这种问题的几率也不是很大了,我们就模拟一下,将手机开发者选项中的不保留活动打开,这样一来,当我们按home键时,activity就会被回收掉。

进入开发者选项很简单, 设置–>关于手机–>连续点击版本号六七次,即可。

这里写图片描述

再次运行我们来看看结果:

这里写图片描述

可以看到,但我们点击home键再重新进入app时,就出现了重叠的问题。

因为是系统回收的activity,所以,我们就没法去控制activity不让他走生命周期方法,我们可以从另一个方面着手去解决。

解决办法:
在onSaveInstanceState(outState);中去保存fragment,当activity被恢复时,取出这些fragment即可。

代码如下:

package com.example.yzq.testfragment;

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;

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

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {


    private FrameLayout contentLayout;//容器
    private BottomNavigationView mainBottomView;//底部导航


    private HomeFragment homeFragment;
    private DashboardFragment dashboardFragment;
    private NoticeFragment noticeFragment;


    private static final String HOME_FRAGMENT_KEY = "homeFragment";
    private static final String DASHBOARD_FRAGMENT_KEY = "DashboardFragment";
    private static final String NOTICE_FRAGMENT_KEY = "NoticeFragment";


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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        L.i("MainActivity  onCreate");
        initView();


        if (savedInstanceState != null) {

            /*获取保存的fragment  没有的话返回null*/
            homeFragment = (HomeFragment) getSupportFragmentManager().getFragment(savedInstanceState, HOME_FRAGMENT_KEY);
            dashboardFragment = (DashboardFragment) getSupportFragmentManager().getFragment(savedInstanceState, DASHBOARD_FRAGMENT_KEY);
            noticeFragment = (NoticeFragment) getSupportFragmentManager().getFragment(savedInstanceState, NOTICE_FRAGMENT_KEY);


            addToList(homeFragment);
            addToList(dashboardFragment);
            addToList(noticeFragment);


        } else {
            initFragment();
        }


    }

    private void addToList(Fragment fragment) {
        if (fragment != null) {
            fragmentList.add(fragment);
        }

        L.i("fragmentList数量" + fragmentList.size());
    }

    private void initView() {
        contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
        mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);

        mainBottomView.setOnNavigationItemSelectedListener(this);
    }

    private void initFragment() {
       /* 默认显示home  fragment*/
        homeFragment = new HomeFragment();
        addFragment(homeFragment);
        showFragment(homeFragment);


    }

    /*添加fragment*/
    private void addFragment(Fragment fragment) {

        /*判断该fragment是否已经被添加过  如果没有被添加  则添加*/
        if (!fragment.isAdded()) {
            getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
        /*添加到 fragmentList*/
            fragmentList.add(fragment);
        }


    }


    /*显示fragment*/
    private void showFragment(Fragment fragment) {


        L.i("fragmentList數量:" + fragmentList.size());
        for (Fragment frag : fragmentList) {

            if (frag != fragment) {
                /*先隐藏其他fragment*/
                L.i("隱藏" + fragment);
                getSupportFragmentManager().beginTransaction().hide(frag).commit();
            }
        }
        getSupportFragmentManager().beginTransaction().show(fragment).commit();

    }


    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {

        switch (item.getItemId()) {
            case R.id.navigation_home:

                if (homeFragment == null) {
                    L.i("homeFragment 为空  创建");
                    homeFragment = new HomeFragment();
                }


                addFragment(homeFragment);
                showFragment(homeFragment);
                break;

            case R.id.navigation_dashboard:
                if (dashboardFragment == null) {
                    L.i("dashboardFragment 为空  创建");
                    dashboardFragment = new DashboardFragment();
                }
                addFragment(dashboardFragment);
                showFragment(dashboardFragment);
                break;

            case R.id.navigation_notifications:

                if (noticeFragment == null) {
                    L.i("noticeFragment 为空  创建");
                    noticeFragment = new NoticeFragment();
                }
                addFragment(noticeFragment);
                showFragment(noticeFragment);
                break;


        }
        return true;
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        L.i("MainActivity onSaveInstanceState");
        /*fragment不为空时 保存*/
        if (homeFragment != null) {
            getSupportFragmentManager().putFragment(outState, HOME_FRAGMENT_KEY, homeFragment);
        }
        if (dashboardFragment != null) {
            getSupportFragmentManager().putFragment(outState, DASHBOARD_FRAGMENT_KEY, dashboardFragment);
        }
        if (noticeFragment != null) {
            getSupportFragmentManager().putFragment(outState, NOTICE_FRAGMENT_KEY, noticeFragment);
        }


        super.onSaveInstanceState(outState);
    }


    @Override
    protected void onStart() {
        super.onStart();
        L.i("MainActivity onStart");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        L.i("MainActivity onRestart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        L.i("MainActivity onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        L.i("MainActivity onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        L.i("MainActivity onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        L.i("MainActivity onDestroy");
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {

        super.onConfigurationChanged(newConfig);
        L.i("MainActivity onConfigurationChanged");
    }
}

主要就是在onSaveInstanceState中将fragment保存起来。

这里写图片描述

在oncreate的时候判断 一下savedInstanceState 是为空,不为空的话就是有保存的fragment信息,然后将保存的fragment取出来赋给对您的变量即可。

这里写图片描述

我们再来看看效果图(依旧是在不保留活动的情况下运行):

这里写图片描述

可以看到,既保留了activity视图状态,也完美解决了fragment的重叠问题。


下面是Demo,有详细注释。需要的话可以下载。
Fragment重叠解决方案Demo

好了,希望能帮到你,如果你觉得对你有帮助,麻烦动动手指顶一下,算是对我的一个鼓励,谢谢!

阅读更多

扫码向博主提问

朽木_不折

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • Android
  • Web前端
去开通我的Chat快问
版权声明:本文为博主原创文章,转载请注明地址。如果文中有什么纰漏或错误的话,请留言指正,我会及时更正。如果您觉得本文还不错的话,记得点个赞呦,希望能帮到你,谢谢。 https://blog.csdn.net/yuzhiqiang_1993/article/details/75014591
个人分类: Android
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭