顯示廣告
隱藏 ✕
看板 KnucklesNote
作者 Knuckles (站長 那克斯)
標題 [AndroidStudio] 使用 Fragment 切換頁面內容
時間 2016-02-18 Thu. 06:55:27


參考之前這篇: [AndroidStudio] 加上水平捲動選單 HorizontalScrollView - KnucklesNote板 - Disp BBS
做了三個頁面,依照上排水平選單的三個按鈕來切換不同頁面
[圖]


但這樣每個Activity裡都要寫一次三個按鈕的點擊事件
上方的 ToolBar 選單功能也是每頁裡都要再寫一次

比較好的方法是只寫一個 Activty 頁
將列表的部份獨立寫成 Fragment 頁
將水平選單的點擊事件改為切換顯示不同的 Fragment 頁
[圖]


Fragment 就像是一個子頁面,功能類似 Activity
有自己的佈局檔,有自己的生命週期,有自己的輸入事件
一個 Fragment 可以用在不同的 Activity 裡
一個 Activity 裡也可以同時顯示多個 Fragment

關於 Fragment 的說明:
http://developer.android.com/intl/zh-tw/guide/components/fragments.html


首先將之前寫的熱門文章頁改為使用 Fragment

新增佈局檔 fragment_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <ListView
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="#555"
            android:dividerHeight="1px"
        />
        <TextView
            android:id="@android:id/empty"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </TextView>
    </android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
注意 <ListView> 的 id 必需是 "@android:id/list",不能改為其他名稱
如果只有要用 ListView 的話,有預設的資源可以用,不用建立這個檔
不過因為我們要用到 SwiperefreshLayout 下拉更新功能,所以要自己建立


新增類別程式檔 HotTextFragment.java
因為我們要用的子頁面就是一個ListView,所以繼承專門給列表用的 ListFragment
public class HotTextFragment extends ListFragment{
}

加上成員函式 onCreateView() 輸入佈局檔的名稱
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_list, container, false);
    }
也可以不用加這個,就會是用預設的佈局檔

加上成員變數
    Activity mContext;
    ListView mListView;
    MainAdapter mAdapter;
    ProgressDialog mDialog;
    SwipeRefreshLayout mSwipeLayout;
將原本寫在 MainActivity 的一些成員變數移到這裡來

加上成員函式 onActivityCreated()
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mContext = getActivity();
        mListView = getListView();

        mAdapter = new MainAdapter(mContext);
        setListAdapter(mAdapter);
        mListView.setOnItemClickListener(this);

        mSwipeLayout = (SwipeRefreshLayout) mContext.findViewById(R.id.swiperefresh);
        mSwipeLayout.setOnRefreshListener(this);
        mSwipeLayout.setColorSchemeColors(Color.BLUE);

        mDialog = new ProgressDialog(mContext);
        mDialog.setMessage("Loading Data...");

        loadData();
    }
將原本寫在 MainActivity 的 onCreate() 裡的一些程式移到這裡來

其中 mContext = getActivity(); 就是取得 MainActivity
之後的程式若要用到 context 參數,原本是使用 this,現在要改為 mContext
原本在 Activity 可以直接呼叫的函數,現在要再前面加上 mContext. 才行

列表 mListView 要使用 getListView(); 來取得,而不是用 id 來取得

mListView.setOnItemClickListener(this);
在綁定列表點擊事件,傳入參數還是用 this,代表事件傳至 Fragment 上
對 this 按 alt+Enter 加上 implements AdapterView.OnItemClickListener
以及成員函式 onItemClick()
將原本寫在 MainActivity 的 onItemClick() 裡的內容移到這來
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        JSONObject jsonObject = (JSONObject) parent.getAdapter().getItem(position);
        int bi = jsonObject.optInt("bi", 0);
        String ti = jsonObject.optString("ti", "");

        Intent intent = new Intent(mContext, TextActivity.class);
        intent.putExtra("bi", bi);
        intent.putExtra("ti", ti);

        startActivity(intent);
        mContext.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    }

mSwipeLayout.setOnRefreshListener(this);
綁定下拉更新事件,也是在 this 上按 alt+Enter
加上 implements SwipeRefreshLayout.OnRefreshListener
以及成員函式 onRefresh()
    @Override
    public void onRefresh() {
        loadData();
    }

最後再將原本寫在 MainActivity 中的 loadData() 移至這邊
    private void loadData(){
        String urlString = "http://disp.cc/api/hot_text.json";
        mDialog.show();

        AsyncHttpClient client = new AsyncHttpClient();
        client.get(urlString, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                mDialog.dismiss();
                mSwipeLayout.setRefreshing(false); //結束更新動畫
                JSONArray list = response.optJSONArray("list");
                if (list == null) {
                    Toast.makeText(mContext, "Data error", Toast.LENGTH_LONG).show();
                    return;
                }
                mAdapter.updateData(list);
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, Throwable e, JSONObject error) {
                mDialog.dismiss();
                mSwipeLayout.setRefreshing(false); //結束更新動畫
                Toast.makeText(mContext, "Error: " + statusCode + " " + e.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }


修改 activity_main.xml
刪掉 SwipeRefreshLayout 和 ListView
改為用來放 Fragment 的 <FrameLayout>
    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


修改 MainActivity.java
加上成員變數
Fragment mHotTextFragment;

在 onCreate() 加上
        mHotTextFragment = new HotTextFragment();
        if (savedInstanceState == null) { //避免轉方向時重覆載入
            getFragmentManager().beginTransaction()
                .add(R.id.frameLayout, mHotTextFragment)
                .commit();
        }
當 MainActivity 載入時
使用 FragmentManager 將 HotTextFragment 加進 <FrameLayout>

(若import的是 support.v4 的 Fragment,
 getFragmentManager() 要改為 getSupportFragmentManager() )


這樣就已經將熱門文章列表的部份從 MainActivity 中獨立成 HotTextFragment 了
執行看看有沒有問題

沒問題的話用一樣的方法,建立熱門看板頁和搜尋看板頁的 Fragment
BoardListFragment.java 和 BoardSearchFragment.java

接著要修改水平選單按鈕的點擊事件
將切換 Activity 改為切換<FrameLayout>裡的 Fragment
以及修改 Button 為以選取和未選取的顏色

修改 MainActivity.java

加上成員變數
    Button mHotTextBtn, mBoardListBtn, mBoardSearchBtn;
    Fragment mHotTextFragment, mBoardListFragment, mBoardSearchFragment;
    final int TAB_HOTTEXT=0, TAB_BOARDLIST=1, TAB_BOARDSEARCH=2;
    int mTabSelect; //記錄目前選取的是哪個TAB

在 onCreate() 裡加上
        //綁定 Button 的點擊事件
        mHotTextBtn = (Button) findViewById(R.id.button_HotText);
        mHotTextBtn.setOnClickListener(this);
        mBoardListBtn = (Button) findViewById(R.id.button_BoardList);
        mBoardListBtn.setOnClickListener(this);
        mBoardSearchBtn = (Button) findViewById(R.id.button_BoardSearch);
        mBoardSearchBtn.setOnClickListener(this);

        //建立3個Fragment,一開始先載入 HotTextFragment
        mHotTextFragment = new HotTextFragment();
        mBoardListFragment = new BoardListFragment();
        mBoardSearchFragment = new BoardSearchFragment();
        if (savedInstanceState == null) { //避免轉方向時重覆載入
            getFragmentManager().beginTransaction()
                .add(R.id.frameLayout, mHotTextFragment).commit();
        }
        mTabSelect = TAB_HOTTEXT; //一開始選取的TAB

加上切換Tab頁的成員函式 changeTab()
    private void changeTab(int tabNew){
        Button btnSelect = mHotTextBtn;
        switch(mTabSelect){
            case TAB_BOARDLIST:
                btnSelect = mBoardListBtn;
                break;
            case TAB_BOARDSEARCH:
                btnSelect = mBoardSearchBtn;
                break;
        }
        //將原本選取的Tab按鈕改為未選取的顏色
        btnSelect.setBackgroundColor(Color.BLACK);
        btnSelect.setTextColor(Color.parseColor("#99CCFF"));

        Button btnNew = mHotTextBtn;
        Fragment fragmentNew = mHotTextFragment;
        switch(tabNew){
            case TAB_BOARDLIST:
                btnNew = mBoardListBtn;
                fragmentNew = mBoardListFragment;
                break;
            case TAB_BOARDSEARCH:
                btnNew = mBoardSearchBtn;
                fragmentNew = mBoardSearchFragment;
        }
        //將新選取的Tab按鈕改為已選取的顏色
        btnNew.setBackgroundColor(Color.LTGRAY);
        btnNew.setTextColor(Color.BLACK);

        if(mTabSelect==tabNew){ //點擊的相同的Tab按鈕時,重整 Fragment
            getFragmentManager().beginTransaction()
                .detach(fragmentNew).attach(fragmentNew).commit();
        }else { //切換<FrameLayout>的 Fragment
            getFragmentManager().beginTransaction()
                .replace(R.id.frameLayout, fragmentNew).commit();
        }
        mTabSelect = tabNew;
    }

修改按鈕的點擊事件 onClick()
    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.button_HotText:
                changeTab(TAB_HOTTEXT);
                return;
            case R.id.button_BoardList:
                changeTab(TAB_BOARDLIST);
                return;
            case R.id.button_BoardSearch:
                changeTab(TAB_BOARDSEARCH);
                return;
            default:
        }
    }

修改 AndroidManifest.xml
在 <activity android:name=".MainActivity" 加上
            android:configChanges="screenSize|orientation"
避免轉方向時重覆執行 onCreate()
造成點選的 Tab 顯示錯誤


每個 Frgment 可以有自己的 ToolBar 選單

例如在 MainActivity 的 ToolBar 選單 menu_main.xml
已有一個「登入」的按鈕
[圖]


若在 BoardSearchFragment 另外再加上選單 menu_boardsearch.xml
裡面有個「清除已瀏覽看板」的按鈕
則切換到看板搜尋頁時,兩個選單的內容會合併顯示
[圖]



在 Fragment 加入選單的方法

修改 BoardSearchFragment.java
在 onCreateView() 裡加上
        setHasOptionsMenu(true);

加上成員函式 onCreateOptionsMenu()
輸入 menu 的資源名稱 R.menu.menu_boardsearch
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_boardsearch, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

新增成員函式 onOptionsItemSelected()
將點擊「清除瀏覽過的看板」後要做的事寫在這
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
            case R.id.action_clear: //點了清除瀏覽過的看板
                BoardHistoryDB boardHistoryDB = new BoardHistoryDB(mContext);
                boardHistoryDB.clear();
                boardHistoryDB.close();
                mHistoryAdapter.clear();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }



參考:
http://www.tutorialspoint.com/android/android_fragments.htm
http://androidstation.pixnet.net/blog/post/227190520-fragment基本入門教學



--
※ 作者: Knuckles 時間: 2016-02-18 06:55:27
※ 編輯: Knuckles 時間: 2017-01-12 08:45:22
※ 看板: KnucklesNote 文章推薦值: 1 目前人氣: 0 累積人氣: 4567 
分享網址: 複製 已複製
( ̄︶ ̄)b smallcare0 說讚!
r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇