BottomNavigationView下Fragment的兩種切換方式

這個(gè)文章比較“膚淺”昭齐,但是其實(shí)網(wǎng)上對(duì)于Fragment切換這么膚淺的事情也甚少有文章說(shuō)的清楚,所以稍微介紹下矾柜。

BottomNavigationView

網(wǎng)上有好多關(guān)于BottomNavigationView的教程阱驾,講的挺詳細(xì)的就谜,本文這里沒(méi)有細(xì)講這個(gè)的意向,但是下面用到BottomNavigationView的監(jiān)聽(tīng)事件:

 bottomNavi.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){
                    case R.id.home:
                        switchFragment(0);
                        return true;
                    case R.id.found:
                        switchFragment(1);
                        return true;
                    case R.id.account:
                        switchFragment(2);
                        return true;
                }

                return false;
            }
        });

注意在case里面要return true里覆,要不切換的時(shí)候會(huì)沒(méi)有動(dòng)畫效果的丧荐。
switchFragment 是我切換顯示fragment的方法。

初始化fragment的列表

private static final String TAG_HOME = "home";
private static final String TAG_FOUND = "found";
private static final String TAG_ACCOUNT = "account"
private static final String[] TAGS = {"home","found","account"};
private void buildFragmentList() {
        BdHomeFragment homeFragment = new BdHomeFragment();
        BdFoundFragment foundragment = new BdFoundFragment();
        BdAccountFragment accountFragment = new BdAccountFragment();
        fragments.add(homeFragment);
        fragments.add(foundragment);
        fragments.add(accountFragment);
    }

fragment的切換方式一:replace

private void switchFragment(int pos, String tag) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragmentholder, fragments.get(pos), tag)
                .commit();
    }

R.id.fragmentholder是fragment的容器喧枷,上面有提及過(guò)虹统,在這里使用,為顯示的fragment指定容器隧甚。
這種方式比較簡(jiǎn)單车荔,直接初始化fragment的list和寫好對(duì)應(yīng)的tag后,切換一次直接replace就好了戚扳。

fragment的切換方式二忧便,hide,show帽借,重點(diǎn)說(shuō)這個(gè)珠增。

因?yàn)閔ide,show的使用方式不當(dāng)?shù)脑捒嘲瑫?huì)導(dǎo)致很多bug蒂教。
比如說(shuō)重疊問(wèn)題,重疊問(wèn)題這個(gè)在android 23版本上被修復(fù)了辐董。
但是在使用23版本上有時(shí)候還是會(huì)遇到回收內(nèi)存后界面重疊的情況悴品,那就是你的打開(kāi)方式不對(duì)了。
看下面:
在onCreate里面調(diào)用的設(shè)置默認(rèn)界面简烘,比如說(shuō)三個(gè)fragment苔严,我讓第二個(gè)為默認(rèn),就設(shè)置1孤澎,這很簡(jiǎn)單届氢,沒(méi)有問(wèn)題。
//設(shè)置默認(rèn)

    prePos  = 0
    setDefaultFragment(prePos  )覆旭;

    private void setDefaultFragment(int pos){
        Fragment now = fragments.get(pos);
        if(!now.isAdded()){
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.fragmentholder,fragments.get(prePos),TAGS[pos])
                    .commit();
        }else{
            getSupportFragmentManager()
                    .beginTransaction()
                    .show(now)
                    .commit();
        }
    }

buildFragmentList 跟上面切換的一樣退子。

switchFragment: 在判斷to是否add進(jìn)去過(guò)了來(lái)判斷是add還是show,這個(gè)也很簡(jiǎn)單型将。
prePos 是記錄了當(dāng)前顯示的fragment在list中的位置寂祥。
為了

private void switchFragment(int pos) {
        //Toast.makeText(this,prePos+" -> "+pos,Toast.LENGTH_LONG).show();
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();
        Fragment from = fragments.get(prePos);
        Fragment to = fragments.get(pos);
        if(!to.isAdded()){
            transaction.hide(from)
                    .add(R.id.fragmentholder,fragments.get(pos),TAGS[pos])
                    .commit();
        }else{
            transaction.hide(from)
                    .show(to)
                    .commit();
        }
        prePos = pos;
    }

好了,代碼都這么簡(jiǎn)單而且沒(méi)有問(wèn)題七兜,然后發(fā)生重疊了丸凭。這不是打臉嗎,而且翻過(guò)好多文章都說(shuō)23以上修復(fù)了bug...Android不是在耍我們吧。
思考下重疊原因惜犀,肯定是內(nèi)存回收機(jī)制的原因铛碑。
我們可以在android studio上通過(guò)一系列的騷操作來(lái)復(fù)現(xiàn)內(nèi)存回收的情況:

Paste_Image.png

打開(kāi)Android Device Monitor
你可以看到你的應(yīng)用在你的手機(jī)(真機(jī)也是可以的)上運(yùn)行的線程,以包名顯示虽界,比如說(shuō)是com.test.fragment 汽烦。
你要模擬內(nèi)存回收,運(yùn)行應(yīng)用后按home鍵回到桌面莉御,然后在Android Device Monitor把com.test.fragment給stop了撇吞。
然后再按進(jìn)應(yīng)用,內(nèi)存回收又重啟進(jìn)入應(yīng)用的一波騷操作你就完成了颈将。在開(kāi)發(fā)還是挺有用的梢夯。

好了言疗,繼續(xù)分析晴圾,從生命周期說(shuō)起。
應(yīng)用內(nèi)存回收后會(huì)執(zhí)行onSaveInstanceState這個(gè)方法噪奄,而且全局變量的會(huì)被清空掉死姚,都被回收了,全局變量算什么勤篮,application都照樣null了都毒。
所以我們還是要在onSaveInstanceState保存下我們珍貴的prePos,位置信息碰缔。
因?yàn)锽ottomNavigationView比較靈活账劲,比如說(shuō)你滑到第二個(gè)界面,內(nèi)存被回收了重啟進(jìn)去金抡,切換狀態(tài)還是在第二個(gè)的狀態(tài)瀑焦,只是我們這里上面的fragment顯示重疊了。
這樣保存

@Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //保存上一個(gè)位置
        outState.putInt(PRE,prePos);
    }

然后在onCreate 中當(dāng)savedInstanceState梗肝!=null時(shí)重新賦值榛瓮。
這樣應(yīng)該沒(méi)問(wèn)題了吧,位置信息對(duì)了巫击,hide禀晓,show應(yīng)該就不會(huì)有毛病了吧。
然而并不是坝锰。
還是重疊粹懒,仔細(xì)觀察下。
該show的Fragment是顯示了顷级,但是該消失的沒(méi)有消失凫乖。。。
看下切換代碼拣凹,消失的是如何實(shí)現(xiàn)的

 FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();
        Fragment from = fragments.get(prePos);
        Fragment to = fragments.get(pos);
        if(!to.isAdded()){
            transaction.hide(from)
                    .add(R.id.fragmentholder,fragments.get(pos),TAGS[pos])
                    .commit();
        }else{
            transaction.hide(from)
                    .show(to)
                    .commit();
        }
        prePos = pos;
 
 Fragment from = fragments.get(prePos);
 transaction.hide(from)

恍然大悟森爽,內(nèi)存回收后,此from和彼from看上去一樣嚣镜,實(shí)際上爬迟,內(nèi)存上已經(jīng)不一樣了。
你hide錯(cuò)fragment了菊匿,hide了個(gè)新的fragment付呕,舊的還是show出來(lái)了。

解決

所以應(yīng)該這么做跌捆。
在onCreate 中

        if(savedInstanceState==null){
            //默認(rèn)為0
            prePos = 0;
            fragments = new ArrayList<>(3);
            buildFragmentList();
        }else{
            //內(nèi)存被回收了徽职,fragments的list也被回收了,重新add進(jìn)去
            prePos = savedInstanceState.getInt(PRE);
            fragments = new ArrayList<>(3);
            BdHomeFragment homeFragment = (BdHomeFragment) getSupportFragmentManager().findFragmentByTag(TAGS[0]);
            BdFoundFragment foundragment = (BdFoundFragment) getSupportFragmentManager().findFragmentByTag(TAGS[1]);
            BdAccountFragment accountFragment = (BdAccountFragment) getSupportFragmentManager().findFragmentByTag(TAGS[2]);
            //加上判斷fragment是否為空佩厚,為空要new一個(gè)
            fragments.add(homeFragment!=null?homeFragment:new HomeFragment());
            fragments.add(foundragment!=null?foundragment:new BdFoundFragment ());
            fragments.add(accountFragment!=null?accountFragment:new BdAccountFragment () );
        }

通過(guò)findFragmentByTag來(lái)保證內(nèi)存回收前后的fragment是一樣的就ok了姆钉。

上面懶得看,直接看hide show代碼的

public class BdMainActivity extends BaseActivity {

    @BindView(R.id.bottom_navi)
    BottomNavigationView bottomNavi;

    private ArrayList<Fragment> fragments ;
    private static final String TAG_HOME = "home";
    private static final String TAG_FOUND = "found";
    private static final String TAG_ACCOUNT = "account";
    private static final String[] TAGS = {"home","found","account"};
    private int prePos;
    private String PRE = "PREPOS";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bd_main);
        ButterKnife.bind(this); //初始化所有fragment

        //切換的點(diǎn)擊事件
        bottomNavi.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.home:
                        switchFragment(0);
                        return true;
                    case R.id.found:
                        switchFragment(1);
                        return true;
                    case R.id.account:
                        switchFragment(2);
                        return true;
                }

                return false;
            }
        });

        if(savedInstanceState==null){
            //默認(rèn)為0
            prePos = 0;
            fragments = new ArrayList<>(3);
            buildFragmentList();
        }else{
            //內(nèi)存被回收了抄瓦,fragments的list也被回收了潮瓶,重新add進(jìn)去
            prePos = savedInstanceState.getInt(PRE);
            fragments = new ArrayList<>(3);
            BdHomeFragment homeFragment = (BdHomeFragment) getSupportFragmentManager().findFragmentByTag(TAGS[0]);
            BdFoundFragment foundragment = (BdFoundFragment) getSupportFragmentManager().findFragmentByTag(TAGS[1]);
            BdAccountFragment accountFragment = (BdAccountFragment) getSupportFragmentManager().findFragmentByTag(TAGS[2]);
             //加上判斷fragment是否為空,為空要new一個(gè)
            fragments.add(homeFragment!=null?homeFragment:new HomeFragment());
            fragments.add(foundragment!=null?foundragment:new BdFoundFragment ());
            fragments.add(accountFragment!=null?accountFragment:new BdAccountFragment () );
        }

        //設(shè)置默認(rèn)
        setDefaultFragment(prePos);
    }
    //設(shè)置默認(rèn)
    private void setDefaultFragment(int pos){
        Fragment now = fragments.get(pos);
        if(!now.isAdded()){
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.fragmentholder,fragments.get(prePos),TAGS[pos])
                    .commit();
        }else{
            getSupportFragmentManager()
                    .beginTransaction()
                    .show(now)
                    .commit();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //保存上一個(gè)位置
        outState.putInt(PRE,prePos);
    }

    private void buildFragmentList() {
        BdHomeFragment homeFragment = new BdHomeFragment();
        BdFoundFragment foundragment = new BdFoundFragment();
        BdAccountFragment accountFragment = new BdAccountFragment();
        fragments.add(homeFragment);
        fragments.add(foundragment);
        fragments.add(accountFragment);
    }

    private void switchFragment(int pos) {
        //Toast.makeText(this,prePos+" -> "+pos,Toast.LENGTH_LONG).show();
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();
        Fragment from = fragments.get(prePos);
        Fragment to = fragments.get(pos);
        if(!to.isAdded()){
            transaction.hide(from)
                    .add(R.id.fragmentholder,fragments.get(pos),TAGS[pos])
                    .commit();
        }else{
            transaction.hide(from)
                    .show(to)
                    .commit();
        }
        prePos = pos;
    }
}

雖然很簡(jiǎn)單钙姊,又說(shuō)的很啰嗦毯辅,但是感覺(jué)還是挺實(shí)用的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末煞额,一起剝皮案震驚了整個(gè)濱河市思恐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膊毁,老刑警劉巖胀莹,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異媚媒,居然都是意外死亡嗜逻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門缭召,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)栈顷,“玉大人,你說(shuō)我怎么就攤上這事嵌巷√逊铮” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵搪哪,是天一觀的道長(zhǎng)靡努。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么惑朦? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任兽泄,我火速辦了婚禮,結(jié)果婚禮上漾月,老公的妹妹穿的比我還像新娘病梢。我一直安慰自己,他們只是感情好梁肿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布蜓陌。 她就那樣靜靜地躺著,像睡著了一般吩蔑。 火紅的嫁衣襯著肌膚如雪钮热。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天烛芬,我揣著相機(jī)與錄音隧期,去河邊找鬼。 笑死蛀骇,一個(gè)胖子當(dāng)著我的面吹牛厌秒,可吹牛的內(nèi)容都是我干的读拆。 我是一名探鬼主播擅憔,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼檐晕!你這毒婦竟也來(lái)了暑诸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辟灰,失蹤者是張志新(化名)和其女友劉穎个榕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芥喇,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡西采,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了继控。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片械馆。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖武通,靈堂內(nèi)的尸體忽然破棺而出霹崎,到底是詐尸還是另有隱情,我是刑警寧澤冶忱,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布尾菇,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏派诬。R本人自食惡果不足惜劳淆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望默赂。 院中可真熱鬧憔儿,春花似錦、人聲如沸放可。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耀里。三九已至蜈缤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冯挎,已是汗流浹背底哥。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留房官,地道東北人趾徽。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像翰守,于是被迫代替她去往敵國(guó)和親孵奶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咆贬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android有一個(gè)回收機(jī)制爷速,當(dāng)內(nèi)存不足時(shí)沃于,會(huì)自動(dòng)回收相關(guān)內(nèi)存幸海。 我們使用FragmentActivity放入Fr...
    簡(jiǎn)單Liml閱讀 1,602評(píng)論 0 0
  • Fragment繁扎,俗稱碎片辙培,自 Android 3.0 開(kāi)始被引進(jìn)并大量使用油航。然而就是這樣耳熟能詳?shù)囊粋€(gè)東西崭庸,在開(kāi)...
    亦楓閱讀 23,905評(píng)論 9 84
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法谊囚,內(nèi)部類的語(yǔ)法怕享,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法秒啦,線程的語(yǔ)...
    子非魚_t_閱讀 31,581評(píng)論 18 399
  • 保持著執(zhí)拗 有些執(zhí)著 我并非是好人 也未必是壞人 現(xiàn)在的我不缺什么突然害怕了閉眼之后的什么 現(xiàn)在的你不要什么經(jīng)歷的...
    克塞爾閱讀 155評(píng)論 0 2
  • 田蛙哭夏逝熬粗,夜夜泣無(wú)休。 陌上呱呱咒余境,孰憐萬(wàn)物憂驻呐?
    童姥閱讀 244評(píng)論 0 2