看板 KnucklesNote
作者 標題 [AndroidStudio] 使用 Fragment 切換頁面內容
時間 2016-02-18 Thu. 06:55:27
參考之前這篇: [AndroidStudio] 加上水平捲動選單 HorizontalScrollView - KnucklesNote板 - Disp BBS
做了三個頁面,依照上排水平選單的三個按鈕來切換不同頁面
![[圖]](http://i.imgur.com/c5zheFy.png)
但這樣每個Activity裡都要寫一次三個按鈕的點擊事件
上方的 ToolBar 選單功能也是每頁裡都要再寫一次
比較好的方法是只寫一個 Activty 頁
將列表的部份獨立寫成 Fragment 頁
將水平選單的點擊事件改為切換顯示不同的 Fragment 頁
![[圖]](http://i.imgur.com/xO1Gm8X.png)
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",不能改為其他名稱<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 的話,有預設的資源可以用,不用建立這個檔
不過因為我們要用到 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);
}
也可以不用加這個,就會是用預設的佈局檔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 的一些成員變數移到這裡來ListView mListView;
MainAdapter mAdapter;
ProgressDialog mDialog;
SwipeRefreshLayout mSwipeLayout;
加上成員函式 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() 裡的一些程式移到這裡來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();
}
其中 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);
}
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();
}
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();
}
});
}
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" />
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 載入時if (savedInstanceState == null) { //避免轉方向時重覆載入
getFragmentManager().beginTransaction()
.add(R.id.frameLayout, mHotTextFragment)
.commit();
}
使用 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
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
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;
}
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:
}
}
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
已有一個「登入」的按鈕
![[圖]](http://i.imgur.com/0MSza11.png)
若在 BoardSearchFragment 另外再加上選單 menu_boardsearch.xml
裡面有個「清除已瀏覽看板」的按鈕
則切換到看板搜尋頁時,兩個選單的內容會合併顯示
![[圖]](http://i.imgur.com/vffeoOn.png)
在 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);
}
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);
}
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
回列表(←)
分享