1. 程式人生 > >死磕 Fragment 的生命週期

死磕 Fragment 的生命週期

死磕 Fragment 的生命週期

在做Fragement與Activity傳值的過程中,需要利用生命週期來做自動化管理

本文例子中 github 地址:

曾經在北京擁擠的13號線地鐵上,一名揹著雙肩包穿著格子衫帶著鴨舌帽腳踏帆布鞋的程式設計師講了一句:
“我覺得 Fragment 真的太難用了”。從而引起一陣躁動激烈的討論。

正方觀點:

Fragment 真的太好用了。要知道因為 Activity 的啟動,涉及到 Android 系統對 ActivityManager 的排程,會關聯許多資源和進行諸多複雜運算,在一些高階手機上,這個排程的時長甚至會超過 100 ms。反觀 Fragment,啟動如巧克力入口般順滑,輕量不消耗手機資源。還可以做成一個個模組,方便 Activity 複用。並且如果要涉及平板的適配,Fragment 更是必不可少。

反方觀點:

Fragment 難用,屬於坑多難填。Fragment 本質上是一個有生命週期的 View,生命週期繁多並且異常難管理,多個 Fragment 巢狀更是坑中之坑(我也遇到過...),連 square 和 FaceBook 都摒棄了 Fragment,更何況我們呢!

好吧,不要吵了,用或者不用,遇到問題如何解決,相信大家心裡都有一個自己的答案。結合我自己開發時候遇到的問題,下面我們來總結一下 Fragment 的生命週期管理方式,以及一些技巧和建議。

hide & show

先結合一張專案截圖,來直觀地看看目前我是如何管理 Fragment 的。

專案截圖

因為我們的專案架構是一 Activity 多 Fragment,並且把所有的 Fragment 都裝載到了一個 ViewPager 裡面,啟動一個新的 Fragment 的過程也是 ViewPager 滑動翻頁的過程,未來會考慮把這種管理方式總結成文,整理給大家。總之你目前看到的上圖介面,都是 Fragment 呈現的,並且點選按鈕什麼的,也會跳轉到下一個 Fragment,這就涉及到了 Fragment 巢狀。

我也寫了一個 Demo,去模擬了這個頁面的搭建。

這裡想多說幾句

通過點選下方 Tab 管理頁面的方式目前非常主流,這種佈局方式事實上是從 iOS 上面借鑑過來的。(反正現在兩大系統都是相互學習)在前一陣 google 的 support 25 包也終於推出了官方的 BottomNavigationView ,不過我的 Android Studio 安裝 support 25 總是失敗,所以在專案中我選用了一個還不錯的開源庫來做下方的底部導航。

BottomNavigationView 本身有一套 Material Design 的設計規範如下:

Material Design Navigation Bottom

感興趣的去閱讀一下,以後對產品、設計開撕是很有幫助的。其中有這麼一條很有意思,是說 BottomNavigationView 並不建議把它設計成橫向滑動的形式,也就是用 ViewPager 去裝載 Fragment。為什麼說這句很有意思呢?事實上市面上很多主流的 app,它們的 BottomNavigationView 確實是不可以橫向滑動的,而我們每個人都在用的,月活8億的國民軟體微信,就恰恰把它的主頁面做成了可以橫向滑動的。

這裡我想說下我的個人看法,首先規範未必需要嚴格遵守,做什麼樣的功能實現什麼樣的效果,要結合自己專案的架構和產品做一個合理的需求。拿 360手機助手 這個 app 舉例,它底部的每一個 tab 都搭建了一個非常“重量級”的模組,並且每個 tab 下介面的內部還有許多負責的 View 層級和巢狀滑動的 ViewPager,所以試想一下,這樣的頁面要是做成微信那個樣子,不卡頓就怪了~反觀微信,首先我認為它的每個介面層級和互動都不復雜,邏輯也都在頁面內,所以做成橫向滑動的反而能提升使用者的體驗。

好了,前面說了好多無關緊要的話,趕緊來看看 demo 中通過 hide 和 show 的方式如何來管理 Fragment。
MainActivity

    private SearchFragment searchFragment;
    private MusicFragment musicFragment;
    private CarFragment carFragment;
    private SettingFragment settingFragment;

    private BaseFragment currentFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        setSupportActionBar(toolbar);
        initView();
        initData();
        initListener();
    }

    private void initData() {
        if (searchFragment == null) {
            searchFragment = SearchFragment.newInstance(getString(R.string.tab_1));
        }

        if (!searchFragment.isAdded()) {
            // 提交事務
            getSupportFragmentManager().beginTransaction().add(R.id.fl_content, searchFragment).commit();

            // 記錄當前Fragment
            currentFragment = searchFragment;
        }
    }

    private void initListener() {
        bottomNavigation.setOnTabSelectedListener(new AHBottomNavigation.OnTabSelectedListener() {
            @Override
            public boolean onTabSelected(int position, boolean wasSelected) {

                Log.d(TAG, "onTabSelected: position:" + position + ",wasSelected:" + wasSelected);

                if (position == 0) {// 導航
                    clickSearchLayout();
                } else if (position == 1) {// 音樂
                    clickMusicLayout();
                } else if (position == 2) {// 車輛
                    clickCarLayout();
                } else if (position == 3) {
                    clickSettingLayout();
                } else if (position == 4) {
                    clickToysLayout();
                }
                return true;
            }
        });
    }
    private void clickSearchLayout() {
        if (searchFragment == null) {
            searchFragment = SearchFragment.newInstance(getString(R.string.tab_1));
        }
        addOrShowFragment(getSupportFragmentManager().beginTransaction(), searchFragment);
    }

    private void clickMusicLayout() {
        if (musicFragment == null) {
            musicFragment = MusicFragment.newInstance(getString(R.string.tab_2));
        }
        addOrShowFragment(getSupportFragmentManager().beginTransaction(), musicFragment);
    }

    private void clickCarLayout() {
        if (carFragment == null) {
            carFragment = CarFragment.newInstance(getString(R.string.tab_3));
        }
        addOrShowFragment(getSupportFragmentManager().beginTransaction(), carFragment);
    }

    private void clickSettingLayout() {
        if (settingFragment == null) {
            settingFragment = SettingFragment.newInstance(getString(R.string.tab_4));
        }
        addOrShowFragment(getSupportFragmentManager().beginTransaction(), settingFragment);
    }


    /**
     * 新增或者顯示 fragment
     *
     * @param transaction
     * @param fragment
     */
    private void addOrShowFragment(FragmentTransaction transaction, Fragment fragment) {
        if (currentFragment == fragment)
            return;

        if (!fragment.isAdded()) { // 如果當前fragment未被新增,則新增到Fragment管理器中
            transaction.hide(currentFragment).add(R.id.fl_content, fragment).commit();
        } else {
            transaction.hide(currentFragment).show(fragment).commit();
        }
        currentFragment = (BaseFragment) fragment;
    }

程式碼理解起來非常容易,我通過 add & show / hide 的方式來管理底部的四個 tab 相互切換,並且列印了這四個 Fragment 的所有生命週期方法,包括onHiddenChangedsetUserVisibleHint

當第一次進入某個頁面時:

首次進入某個頁面時

可以看到,當我依次點選下方四個 tab,介面第一次載入的時,會走 Fragment 的建立週期 onAttach -- onResume,也許你會問,上面我執行相互切換操作,從第一個頁面切換到第二個的時候,為什麼第一個 Fragment 頁面不可見了,不會呼叫 onPause 和 onStop 呢?

這是在你瞭解過 Activity 生命週期並且剛接觸 Fragment 的生命週期時,第一個容易陷入的誤區,事實上 Fragment 的生命週期,除了第一次它建立或銷燬之外,統統都是由 Activity 所驅動的,正常來說,Fragment 的生命週期應該是以 add 到 FragmentTransation 開始,從 FragmentTransation remove 掉結束。 舉個例子,當我點選 home 鍵回到桌面時:

點選 home 鍵

可以看到我已經載入了的幾個 Fragment 齊刷刷的呼叫了 onPause 和 onStop,如此得步調一致是因為這些 Fragment attach 的 Activity 回調了 onPause 和 onStop。相信肯定會有人說“不對啊,我要是用 replace 的方式去切換 Fragment,我打包票 Fragment 會像 Activity 一樣,完整得走完生命週期“

你說的沒錯,因為 replace 這種切換方式就是始終上面我總結的那句“首次建立或銷燬“,並且在 BottomNavigation 這樣的使用場景中,沒人會用這種 replace 的方式,因為每次切換都要重新建立 Fragment,使用者看了下流量估計會打 12315 了。

(如果 Fragment 代表前男/女友,據說男人是用 add 儲存,女人使用 replace 替換 hhh...)

當底部的四個 Fragment 都已經載入完成之後咧?再一起看下 log:

onHiddenChanged 回撥

當底部四個 Fragment 全部建立入棧之後,show 和 hide 來管理 Fragment,此時只有 onHiddenChanged 方法回撥,這時候顯然你可以在 hide = false 時,做一些重新整理操作,在 hide = true 時,做一些資源回收操作。

在剛才我們列印的方法中,好像有一個一直沒出現,沒錯就是 setUserVisibleHint,如果你做過 Fragment+ViewPager 的懶載入(下文我們會講這個),相信你對它就比較熟悉了,通過名字就能猜測出來,這個方法是我們主動設定給 Fragment 的,那我們就來試試看:

改造 addOrShowFragment

    /**
     * 新增或者顯示 fragment
     *
     * @param transaction
     * @param fragment
     */
    private void addOrShowFragment(FragmentTransaction transaction, Fragment fragment) {
        if (currentFragment == fragment)
            return;

        if (!fragment.isAdded()) { // 如果當前fragment未被新增,則新增到Fragment管理器中
            transaction.hide(currentFragment).add(R.id.fl_content, fragment).commit();
        } else {
            transaction.hide(currentFragment).show(fragment).commit();
        }

        currentFragment.setUserVisibleHint(false);

        currentFragment = (BaseFragment) fragment;

        currentFragment.setUserVisibleHint(true);

    }

在切換時,我們對上一個 fragment setUserVisibleHint設定為 false,要展現的 Fragment setUserVisibleHint 設定為 true,列印 log 看看:

setUserVisibleHint

可以看到目前我們用 setUserVisibleHint 也達到了與 onHiddenChanged 一樣的效果。

文章寫到現在,我們來做一個總結,上文的這種方式就是主流通過 BottomNavigation 管理 Fragment 的方式,這種方式有什麼好處呢?毫無疑問會節省資源,不點開的介面不去建立它(這一點 ViewPager 做不到),只建立一次,未來僅僅更新介面資料就可以了。

ViewPaager & Fragment

ViewPager 和 Fragment 配合使用相信大多數人都很熟悉了,所以來快速地給大家總結一下我認為需要梳理清楚的幾個知識點,先來看我搭建的頁面:

TabLayout+ViewPager

我在導航的這個模組中,搭建了一個 TabLayout+ViewPager+Fragment 的頁面結構,當啟動 app,進入首頁,各個 Fragment 的生命週期方法是怎樣的呢?

首頁和 Tab 頁生命週期方法

可以看到,當我進入 app 的時候,所有 TabLayout 所在的父容器 SearchFragment 建立,呼叫了 onAttach --> onResume 這當然是我們預料之中的,我們 ViewPager 第一個裝載並展示的是 ScienceFragment,ScienceFragment 建立沒有問題,可是第二個 tab GameFragment 為什麼載入了呢?

沒錯這就是 ViewPager 的預載入機制。

ViewPager 出於優化體驗的好心,預設去載入相鄰兩頁,來儘可能保證滑動的流暢性,可是假如我們這是一個新聞資訊類的 app,每一個 tab 涉及了複雜的頁面和大量的網路請求,這種預載入的機制帶來的可能就是麻煩了。所以我們尋找一些辦法試圖去掉 ViewPager 的預載入。

ViewPager 自身提供了一個方法,mViewPager.setOffscreenPageLimit(),這個方法的官方文件的解釋:

setOffscreenPageLimit 官方文件

它的意思就是設定 ViewPager 左右兩側快取頁的數量,預設是1,那我們給它設定為0,是不是就能取消預載入了呢?再看看這段蜜汁原始碼:

setOffscreenPageLimit 原始碼

總之原始碼的意思就是你設定為小於1的值就預設為1,反正這條路目前行不通了。

當然還有一個辦法,你直接修改原始碼以後重新打一個 v4 包,不過非常不建議這樣做,未來會產生一些相容問題。

好吧,你應該知道馬上就要說 ViewPager 的懶載入了, 就是要用到上文我們提到的 setUserVisibleHint 方法,當我左右滑動時,來看看列印的 log:

滑動時 log

從 log 中可以分析到兩個問題,首先 setUserVisibleHint 這個方法可能會在 onAttach 之前就呼叫,其次在滑動中設定快取頁數之外的頁確實是銷燬了。

回顧下前文, 明明說 setUserVisibleHint 這個方法需要主動呼叫,那在 ViewPager 中,Fragment 的 setUserVisibleHint 方法是誰在何時呼叫的呢?

我的 ViewPager Adapter 在這裡:

public class MainTabAdapter extends FragmentStatePagerAdapter {

    private List<Fragment> mList;

    private String mTabTitle[] = new String[]{"科技", "遊戲", "裝備", "創業", "想法"};

    public MainTabAdapter(FragmentManager fm, List<Fragment> list) {
        super(fm);
        mList = list;
    }

    @Override
    public Fragment getItem(int position) {
        return mList.get(position);
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTabTitle[position];
    }

}

看來看去也就 FragmentStatePagerAdapter 能做這件事了,點進去看看:

FragmentStatePagerAdapter 這個類其實就是一個對 PagerAdapter 的一個封裝類,不到200行的程式碼,果真找到了 setUserVisibleHint 。

有兩處位置呼叫了 setUserVisibleHint ,第一個位置:

instantiateItem

第二個位置:

setPrimaryItem

這裡原始碼處理的邏輯是這樣子的:

在 instantiateItem 方法中,在我的資料集合中取出對應 positon 的 Fragment,直接給它的 setUserVisibleHint 設定為 false,然後才把它 add 進 FragmentTransaction 中,這恰恰解釋了為什麼 setUserVisibleHint 的第一次呼叫是在 onAttach 之前。

下一步 setUserVisibleHint 的設定是在 setPrimaryItem中,setPrimaryItem這個方法可以得到當前 ViewPager 正在展示的 Fragment,並且將上一個 Fragment 的 setUserVisibleHint 置為 false,將要展示的 setUserVisibleHint 置為 true。

通過閱讀原始碼,我們明白了原理,所以直接給大家上在 BaseFragment 實現懶載入程式碼:

public class BaseFragment extends Fragment {

    protected boolean isViewCreated = false;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            lazyLoadData();
        }
    }

    /**
     * 懶載入方法
     */
    protected void lazyLoadData() {
        
    }
}

在具體的 Fragment 中實現懶載入

public class ScienceFragment extends BaseFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_science, container, false);
        Log.e(TAG, "onCreateView");
        isViewCreated = true;
        return view;
    }

    @Override
    protected void lazyLoadData() {
        super.lazyLoadData();
        if (isViewCreated) {
            Log.e(TAG, "lazyLoadData...");
        }
    }
    
}

看下 log:

懶載入 log

可以看到,懶載入就這樣實現了。

這裡我們再做一個階段總結,首先大家心裡要清楚 setUserVisibleHint 這個是 ViewPager 的行為,它始終都是先行與 Fragment 的生命週期呼叫的。我們之所以能用懶載入這種辦法,主要是因為預載入的 Fragment 已經建立完成一路呼叫了 onAttach --> onPause,也就是說這個 Fragment 此時可用的,懶載入才有理由生效。不知道這樣描述是否難懂,但是跑一下本文的例子就肯定能明白上面這段話了。

所以當 Fragment 第一次建立時,懶載入不會同時呼叫,所以我們來繼續優化優化,我們讓 ViewPager 一起載入這五個 Fragment 的佈局,然後懶載入就全程可用啦~

  mViewPager.setOffscreenPageLimit(5);

此時無論是我滑動還是點選上方 tab 跳轉到任意一個頁面,lazyLoadData 方法都會呼叫,我們可以先載入佈局出來,然後可見時重新整理資料就 OK 了。

關於 Fragment 的管理,主要就是上文的兩種方式,一是 add 和 hide 管理,二是 ViewPager + Fragment,當然具體到每個人自己的專案時,需要分析需求和產品思路找到一個適合自己的方式。當我們知道原理時,做操作就心裡有底多了,出了問題也可以快速定位。

上面我們 ViewPager 的 adapter 使用的是 FragmentStatePagerAdapter,還有一個 FragmentPagerAdapter,因為本文的篇幅有些過長,下次會總結出它們在原始碼角度的區別,以及使用過程中踩到的一些坑。(如果有一些奇怪的問題無法解決,建議先使用 FragmentStatePagerAdapter)。

寫在最後:

我們回過頭來看開始那個辯題, Fragment 到底用不用?對於大多數開發者來說,當然要用,我個人其實還非常喜歡 Fragment,使用 Fragment 能體現 Android 元件化的思想,其帶給開發者的便利遠大於麻煩。雖然其生命週期複雜,棧又奇怪難管理,不過當正確的姿勢使用 Fragment(不要巢狀 Fragment 使用),趟過一個個坑時,對 Fragment 自然也有信心了。

最後再上一張 Fragment 的生命週期圖吧~

fragment 生命週期

Fragment程式碼:

package com.findai.xkk.ai_interviewer.resume_fragment;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.findai.xkk.ai_interviewer.R;
import com.findai.xkk.ai_interviewer.domain.EducationExperience;
import com.findai.xkk.ai_interviewer.domain.Resume;
import com.findai.xkk.ai_interviewer.domain.WorkExperience;

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

@SuppressLint("ValidFragment")
public class Resume_Workexperience_Fragment extends Fragment{


    Button btn_next_resume;
    EditText et_companyname;
    EditText et_jobname;
    EditText et_workyear_start;
    EditText et_workyear_end;
    EditText et_jobdescription;
    Resume resume = new Resume();
    ResumeInterface ri;


    public Resume_Workexperience_Fragment(ResumeInterface cb){
        this.ri = cb;
    }
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.item_workexperience_resume_fragment, container, false);
        Bundle bundle = getArguments();
        resume = (Resume)bundle.getSerializable("resume");
        et_companyname = view.findViewById(R.id.et_companyname);
        et_jobname = view.findViewById(R.id.et_jobname);
        et_workyear_start = view.findViewById(R.id.et_workyear_start);
        et_workyear_end = view.findViewById(R.id.et_workyear_end);
        et_jobdescription = view.findViewById(R.id.et_jobdescription);

//        btn_next_resume = view.findViewById(R.id.btn_next_resume);
//        btn_next_resume.setOnClickListener(this);


        et_companyname.setText(resume.getWorkexperience().get(0).getCompanyname());
        et_jobname.setText(resume.getWorkexperience().get(0).getJobname());
        et_workyear_start.setText(resume.getWorkexperience().get(0).getWorkyear_start());
        et_workyear_end.setText(resume.getWorkexperience().get(0).getWorkyear_end());
        et_jobdescription.setText(resume.getWorkexperience().get(0).getJobdescription());

        return view;

    }

//    @Override
//    public void onClick(View v) {
//        String set_companyname = et_companyname.getText().toString();
//        String set_jobname = et_jobname.getText().toString();
//        String set_workyear_start = et_workyear_start.getText().toString();
//        String set_workyear_end = et_workyear_end.getText().toString();
//        String set_jobdescription = et_jobdescription.getText().toString();
//        WorkExperience we = new WorkExperience();
//        List<WorkExperience> list = new ArrayList<>();
//        we.setCompanyname(set_companyname);
//        we.setJobdescription(set_jobdescription);
//        we.setJobname(set_jobname);
//        we.setWorkyear_start(set_workyear_start);
//        we.setWorkyear_end(set_workyear_end);
//        list.add(we);
//
//
////        String[] ss = new String[]{set_companyname,set_jobname,set_workyear,set_jobdescription};
//        if(set_companyname==null||set_companyname.equals("")
//                ||set_jobname==null||set_jobname.equals("")
//                ||set_workyear_start==null||set_workyear_start.equals("")
//                ||set_workyear_end==null||set_workyear_end.equals("")
//                ||set_jobdescription==null||set_jobdescription.equals("")
//                ){
//            Toast.makeText(getContext(),"請填寫內容哦",Toast.LENGTH_LONG).show();
//            return;
//        }
//        ri.resume_workexperience_interface_impl(list);
//    }


    @Override
    public void onHiddenChanged(boolean hidden) {
        String set_companyname = et_companyname.getText().toString();
        String set_jobname = et_jobname.getText().toString();
        String set_workyear_start = et_workyear_start.getText().toString();
        String set_workyear_end = et_workyear_end.getText().toString();
        String set_jobdescription = et_jobdescription.getText().toString();
        WorkExperience we = new WorkExperience();
        List<WorkExperience> list = new ArrayList<>();
        we.setCompanyname(set_companyname);
        we.setJobdescription(set_jobdescription);
        we.setJobname(set_jobname);
        we.setWorkyear_start(set_workyear_start);
        we.setWorkyear_end(set_workyear_end);
        list.add(we);


//        String[] ss = new String[]{set_companyname,set_jobname,set_workyear,set_jobdescription};
        if(set_companyname==null||set_companyname.equals("")
                ||set_jobname==null||set_jobname.equals("")
                ||set_workyear_start==null||set_workyear_start.equals("")
                ||set_workyear_end==null||set_workyear_end.equals("")
                ||set_jobdescription==null||set_jobdescription.equals("")
                ){
            Toast.makeText(getContext(),"請填寫內容哦",Toast.LENGTH_LONG).show();
            return;
        }
        ri.resume_workexperience_interface_impl(list);
    }
}

主Activity程式碼:

package com.findai.xkk.ai_interviewer;

import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.findai.xkk.ai_interviewer.Dao.Question_Data_Exe;
import com.findai.xkk.ai_interviewer.Http.Commiuncate_Server;
import com.findai.xkk.ai_interviewer.Utils.ACache;
import com.findai.xkk.ai_interviewer.Utils.GlobalParams;
import com.findai.xkk.ai_interviewer.domain.EducationExperience;
import com.findai.xkk.ai_interviewer.domain.QuestionList;
import com.findai.xkk.ai_interviewer.domain.Resume;
import com.findai.xkk.ai_interviewer.domain.User;
import com.findai.xkk.ai_interviewer.domain.WorkExperience;
import com.findai.xkk.ai_interviewer.resume_fragment.ResumeInterface;
import com.findai.xkk.ai_interviewer.resume_fragment.Resume_Basicinfo_Fragment;
import com.findai.xkk.ai_interviewer.resume_fragment.Resume_Education_Fragment;
import com.findai.xkk.ai_interviewer.resume_fragment.Resume_Myevaluate_Fragment;
import com.findai.xkk.ai_interviewer.resume_fragment.Resume_Workexperience_Fragment;
import com.google.gson.Gson;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class WriteResumeActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener,ResumeInterface {

    Bundle bundle;
    Resume resume = new Resume();
    RadioGroup rg_resume_navigation;
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    Object Current_Fragment;
    Resume_Basicinfo_Fragment resume_basicinfo_fragment;
    Resume_Myevaluate_Fragment resume_myevaluate_fragment;
    Resume_Workexperience_Fragment resume_workexperience_fragment;
    Resume_Education_Fragment resume_education_fragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.write_resume_activity);
        User user = (User)ACache.get(getBaseContext()).getAsObject(GlobalParams.Para_USER);

        if(user == null){
            Toast.makeText(getBaseContext(),"您尚未登入,請進行登入",Toast.LENGTH_LONG).show();
            Intent intent = new Intent(getBaseContext(),LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

        resume.setUid(user.getUid());

        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();
        rg_resume_navigation = findViewById(R.id.rg_resume_navigation);
        rg_resume_navigation.setOnCheckedChangeListener(this);

        bundle = new Bundle();
        bundle.putSerializable("resume", resume);
        resume_basicinfo_fragment = new Resume_Basicinfo_Fragment(this);
//        resume_myevaluate_fragment = new Resume_Myevaluate_Fragment();
//        resume_workexperience_fragment = new Resume_Workexperience_Fragment();
//        resume_education_fragment = new Resume_Education_Fragment();
        ft = fm.beginTransaction();
        resume_basicinfo_fragment = new Resume_Basicinfo_Fragment(this);
        Current_Fragment = resume_basicinfo_fragment;
        ((Resume_Basicinfo_Fragment) Current_Fragment).setArguments(bundle);
        ft.replace(R.id.framelayout_resume,(Fragment)Current_Fragment);
        ft.commit();
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        ft = fm.beginTransaction();


        switch (checkedId){
            case R.id.rd_resume_basicinfo:
                ft.hide((Fragment) Current_Fragment);
                Current_Fragment = resume_basicinfo_fragment;
//                ft.replace(R.id.framelayout_resume,resume_basicinfo_fragment);
                if (Current_Fragment==null){

                    Current_Fragment =resume_basicinfo_fragment = new Resume_Basicinfo_Fragment(this);
                    ((Resume_Basicinfo_Fragment) Current_Fragment).setArguments(bundle);
//                    Current_Fragment);
                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                }else {
//                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                    ft.show((Fragment)Current_Fragment);
                }
                break;


            case R.id.rd_myevaluate_basicinfo:
                ft.hide((Fragment) Current_Fragment);
                Current_Fragment = resume_myevaluate_fragment;
//                ft.replace(R.id.framelayout_resume,resume_myevaluate_fragment);

                if (Current_Fragment==null){
                    Current_Fragment =resume_myevaluate_fragment = new Resume_Myevaluate_Fragment(this);
                    ((Resume_Myevaluate_Fragment) Current_Fragment).setArguments(bundle);
                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                }else {
//                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                    ft.show((Fragment)Current_Fragment);
                }
                break;
            case R.id.rd_workexperience_basicinfo:
                ft.hide((Fragment) Current_Fragment);
                Current_Fragment = resume_workexperience_fragment;
//                ft.replace(R.id.framelayout_resume,resume_workexperience_fragment);

                if (Current_Fragment==null){
                    Current_Fragment =resume_workexperience_fragment = new Resume_Workexperience_Fragment(this);
                    ((Resume_Workexperience_Fragment) Current_Fragment).setArguments(bundle);
                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                }else {
//                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                    ft.show((Fragment)Current_Fragment);
                }
                break;
            case R.id.rd_education_basicinfo:
                ft.hide((Fragment) Current_Fragment);
                Current_Fragment = resume_education_fragment;
//                ft.replace(R.id.framelayout_resume,resume_education_fragment);

                if (Current_Fragment==null){
                    Current_Fragment =resume_education_fragment = new Resume_Education_Fragment(this);
                    ((Resume_Education_Fragment) Current_Fragment).setArguments(bundle);
                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                }else {
//                    ft.add(R.id.framelayout_resume, (Fragment)Current_Fragment);
                    ft.show((Fragment)Current_Fragment);
                }
                break;
        }
//        ft.show((Fragment)Current_Fragment);
//        ft.addToBackStack(null);
        ft.commit();
    }

    @Override
    public String resume_evalution_interface_impl(String str) {
        resume.setSelfdescription(str);
//        Gson gson = new Gson();

        final Commiuncate_Server cs = new Commiuncate_Server();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    System.out.println("簡歷要開始post請求了");
                    String json = cs.post_resume(resume);
                    System.out.println(json);
                }catch (Exception ex){
                    ex.printStackTrace();
                }
            }
        });
        thread.start();

        return null;
    }

    @Override
    public String resume_basicinfo_interface_impl(String[] str) {

//        String[] ss = new String[]{set_birthday,set_ideal,set_email,set_tele,set_gender,set_nativeplace,set_name};
        resume.setBirthday(str[0]);
        resume.setIdeal(str[1]);
        resume.setEmail(str[2]);
        resume.setTele(str[3]);
        resume.setGender(str[4]);
        resume.setNativeplace(str[5]);
        resume.setName(str[6]);

        return null;

    }

    @Override
    public String resume_workexperience_interface_impl(List<WorkExperience> list) {
        resume.setWorkexperience(list);
        return null;
    }

    @Override
    public String resume_eduexperience_interface_impl(List<EducationExperience> list) {
        resume.setEduexperience(list);
        return null;
    }
//    FragmentManager fm = getSupportFragmentManager();
//    FragmentTransaction ft = fm.beginTransaction();
//ft.hide(firstStepFragment);
//if (secondStepFragment==null){
//        ft.add(R.id.fl_content, secondStepFragment);
//    }else {
//        ft.show(secondStepFragment);
//    }
}