編輯:關於Android編程
Fragment是Android API中的一個類,它代表Activity中的一部分界面;你可以在一個Activity界面中使用多個Fragment,或者在多個Activity中重用某一個Fragment。本文將會介紹Fragment的基本使用、Fragment參數傳遞、與宿主Activity交互、以及Fragment與Toolbar繼承。
第一、編寫類ExampleFragment繼承自Fragment
public class ExampleFragment extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
onCreateView()為Fragment生命周期中的一個方法,當第一次在Fragment上繪制UI時,系統回調這個方法。
下圖為Fragment生命周期
第二、R.layout.example_fragment為ExampleFragment需要繪制UI的布局文件,這裡很簡單,是一個TextView。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
第三、在Activity的布局文件中使用這個ExampleFragment
有個需要注意的地方:每個fragment都需要一個唯一的標識。系統在資源緊缺或者橫屏、豎屏切換都會發生宿主Activity重構,那麼在重構的過程中會去恢復宿主Activity所管理的Fragment隊列,這個過程需要用到每個Fragment的唯一標識。
有三種方法為fragment設置唯一標識:
通過android:id屬性為fragment指定唯一ID 通過android:tag屬性為fragment指定唯一字符串標識 若上述兩種都未指定,則該fragment的標識為其父容器控件的IDOK, 當系統加載Activiy的Layout視圖時,同時加載Fragment綁定的視圖,並回調Fragment的onCreateView()方法,系統將fragment標簽替換為onCreateView()方法返回的view。
首先,Activity的Layout文件修改如下:
這裡給LinearLayout添加了一個ID,用作Fragment的容器ID。
下面是Activity動態添加Fragment代碼
public class MainActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
ft.add(R.id.fragment_container, fragment);
ft.commit();
}
}
FragmentManager:
為了在Activity中管理Fragment,我們需要FragmentManager實例,通過getFragmentManager()方法獲取。可以通過FragmentManager完成如下操作:
調用findFragmentById()方法獲取由Activity管轄的綁定了UI的Fragment實例;調用findFragmentByTag()方法獲取由Activity管轄的未綁定UI的Fragment實例; 調用popBackStack()方法將Fragment從後退棧中彈出; 調用addOnBackStackChangedListener()注冊監聽器,用於監聽後退棧的變化;FragmentTransaction:
Fragment最大的好處就是可以動態的添加, 刪除, 替換等操作。每一組向Activity提交的變化稱為事務,也就是FragmentTransaction對應的一些API。
FragmentTransaction常用API有:add(), remove(), replace()(就是先remove再add),最後為了使事務在Activity生效,需調用commit()方法。
在調用commit()方法之前,可以調用addToBackStack() 方法將事務添加到宿主Activity管轄Fragment後退棧中。這樣用戶通過點擊後退鍵對Fragment進行導航。比如,使用remove()方法移除了Fragment,如果沒有執行addToBackStack()方法,那麼這個Fragment實例就會被銷毀,用戶無法通過後退鍵導航到這個Fragment。
回退棧舉例如下:
//創建一個新的Fragment,並開啟事務
ExampleFragment fragment = new ExampleFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
//將容器中替換成當前fragment
//並把當前事務添加到回退棧
ft.replace(R.id.fragment_container, fragment);
ft.addToBackStack(null);
//提交事務
ft.commit();
防止Activity重構導致多個Fragment重疊:
Activity重構時,系統會自動重建該Activity所管轄的Fragment。如果按照上面的代碼,當Activity發生重構時,多個Fragment就會發生重疊。
需要對代碼做如下改進:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
//沒有為Fragment明確指明ID或者Tag,系統使用fragment容器的LayoutID來唯一標識Fragment
ExampleFragment fragment = (ExampleFragment) fm.findFragmentById(R.id.fragment_container);
//非空判斷,防止出現多個Fragment重疊
if(fragment == null) {
FragmentTransaction ft = fm.beginTransaction();
fragment = new ExampleFragment();
ft.add(R.id.fragment_container, fragment);
ft.commit();
}
}
在啟動Activity的時候,通過Intent可以給Activity傳遞參數。啟動Fragment,如何傳遞參數呢?
通過Fragment.setArguments(Bundle)設置參數,通過Bundle bundle = getArguments()獲取參數;
下面看代碼:
public class ExampleFragment extends Fragment
{
public static final String KEY = "ARGUMENT";
private String mArg;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//獲取參數
Bundle bundle = getArguments();
if(bundle != null) {
mArg = bundle.getString(KEY);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View contentView = inflater.inflate(R.layout.example_fragment, container, false);
TextView tv = (TextView) contentView.findViewById(R.id.tv);
tv.setText(mArg);
return contentView;
}
public static ExampleFragment createFragment(String argument)
{
ExampleFragment fragment = new ExampleFragment();
Bundle args = new Bundle();
args.putString(KEY, argument);
//為fragment傳遞參數
fragment.setArguments(args);
return fragment;
}
}
靜態方法createFragment(String argument)用來在外部創建Fragment實例,在方法中首先創建了一個Bundle對象,將需要傳遞的參數存儲到Bundle對象中,最後通過fragment.setArguments()方法將Bundle對象傳遞給Fragment。
在ExampleFragment 的onCreate()回調函數中使用getArguments()獲取Bundle對象,之後通過Bundle對象獲取參數內容。
下面是Activity中的代碼:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
ExampleFragment fragment = (ExampleFragment) fm.findFragmentById(R.id.fragment_container);
if(fragment == null) {
FragmentTransaction ft = fm.beginTransaction();
//給Fragment傳遞參數
ft.add(R.id.fragment_container, ExampleFragment.createFragment("argument"));
ft.commit();
}
}
注意:setArguments()必須在添加給Activity之前完成。也就是說必須在ft.add()方法之前調用Fragment的setArguments()方法。
向上一個Activity回傳數據通過startActivityForResult(intent, requestCode)方法,以及onActivityResult()回調實現。向上一個Fragment回傳數據也是通過類似的方法實現。
新的場景:
依然新聞的例子,ArticleListFragment中是新聞列表,ArticleContentFragment中是新聞內容。兩個Fragment分別在兩個Activity中。需求:當我們從ArticleContentFragment返回到ArticleListFragment時,需要帶上數據。
先貼上效果圖:
下面分析代碼:
首先是列表Acitivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
mListFragment = (ArticleListFragment) fm.findFragmentById(R.id.fragment_container);
if(mListFragment == null) {
mListFragment = new ArticleListFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, mListFragment);
ft.commit();
}
}
就是簡單的把ArticleListFragment添加到Activity容器當中。
下面是新聞列表Fragment的代碼:
public class ArticleListFragment extends ListFragment
{
public static final int REQUESTCODE = 0x110;
private List mTitles = Arrays.asList("item1", "item2", "item3");
private ArrayAdapter mAdapter;
private int mCurPos;
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
mAdapter = new ArrayAdapter(getActivity(),
android.R.layout.simple_list_item_1, mTitles);
setListAdapter(mAdapter);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
mCurPos = position;
//構建Intent,以帶返回結果的方式啟動新聞內容的Activity
Intent intent = new Intent(getActivity(), ContentActivity.class);
intent.putExtra(ArticleContentFragment.KEY, mTitles.get(position));
startActivityForResult(intent, REQUESTCODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUESTCODE)
{
mTitles.set(mCurPos, mTitles.get(mCurPos) + "---" + data.getStringExtra(ArticleContentFragment.RESPONSE));
mAdapter.notifyDataSetChanged();
}
}
}
ArticleListFragment 繼承自ListFragment,在onActivityCreated()回調函數中設置了adapter,在列表項的點擊回調函數onListItemClick()中使用startActivityForResult()來啟動新的Activity,當從下一個Activity返回到當前Activity就回調onActivityResult()方法。
新聞內容的Activity代碼:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
Intent intent = getIntent();
FragmentManager fm = getFragmentManager();
mContentFragment = (ArticleContentFragment) fm.findFragmentById(R.id.fragment_container);
if(mContentFragment == null) {
mContentFragment = ArticleContentFragment.createFragment(intent.getStringExtra(ArticleContentFragment.KEY));
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, mContentFragment);
ft.commit();
}
}
下面是新聞內容Fragment代碼:
public class ArticleContentFragment extends Fragment
{
public static final String KEY = "KEY";
public static final String RESPONSE = "RESPONSE";
private String mArg;
private TextView mTv;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//獲取參數
Bundle bundle = getArguments();
if(bundle != null) {
mArg = bundle.getString(KEY);
Intent intent = new Intent();
intent.putExtra(RESPONSE, "good");
//Fragment本身沒有setResult()方法,必須通過宿主Activity實現
getActivity().setResult(ArticleListFragment.REQUESTCODE, intent);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View contentView = inflater.inflate(R.layout.example_fragment, container, false);
mTv = (TextView) contentView.findViewById(R.id.tv);
mTv.setText(mArg);
return contentView;
}
public static ArticleContentFragment createFragment(String argument)
{
ArticleContentFragment fragment = new ArticleContentFragment();
if(argument != null) {
Bundle args = new Bundle();
args.putString(KEY, argument);
//為fragment傳遞參數
fragment.setArguments(args);
}
return fragment;
}
}
就一個地方需要注意,第24行。由於Fragment本身沒有setResult()方法,所以我們需要通過getActivity().setResult()來設置返回數據。這也就是說:Fragment能夠從Activity接收返回結果,但Fragment自身無法產生返回結果,需要借助宿主Activity實現。
Fragment可以通過getActivity()方法獲取宿主Activity的對象引用,通過該引用,可以調用Activity中的findViewById()方法獲得布局中的視圖控件。
如下所示:
TextView tv = (TextView)getActivity().findViewById(R.id.tv);
類似地,也可以在Activity中獲取Fragment實例:
FragmentManager fm = getFragmentManager();
ExampleFragment fragment = (ExampleFragment) fm.findFragmentById(R.id.fragment_container);
由於Fragment和宿主Activity都可以獲取對方的實例對象,就可訪問到對方內部所有的public類型的方法。
新的場景:
宿主Activity需要對Fragment中的UI控件的事件進行響應。好的做法就是在Fragment中定義添加回調接口,讓宿主Activity去實現這個接口。
舉例來說:一個新聞應用的Activity包含兩個Fragment,Fragment A展示新聞標題,Fragment B顯示新聞內容。Fragment A必須告訴Activity它的列表項何時被點擊,這樣Activity可以控制Fragment B的顯示內容。
下面給出示例代碼:
public class FragmentA extends ListFragment
{
...
//宿主Activity必須實現這個接口
public interface OnArticleSelectedListener
{
public void onArticleSelected(Uri articleUri);
}
...
}
接著在宿主Activity中實現這個接口,重寫onArticleSelected()方法,並通知FragmentB來自於FragmentA的點擊事件。為了保證宿主Activity實現該接口,需要在FragmentA中的onAttach()回調方法中做如下工作(當Fragment添加至Activity,系統回調onAttach()方法):
public class FragmentA extends ListFragment
{
private OnArticleSelectedListener mActivity;
...
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try {
mActivity = (OnArticleSelectedListener)activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() +
"must implement OnArticleSelectedListener");
}
}
...
}
這樣FragmentA的成員變量mActivity持有了實現OnArticleSelectedListener 接口的引用,也就是FragmentA可以向宿主Activity傳遞點擊事件,宿主Activity重寫了onArticleSelected()方法,在該方法中執行具體邏輯操作,比如:控制FragmentB顯示被點擊的列表項所對應的新聞內容。
為了直觀,寫了一個簡單的demo
這個demo的代碼就不貼了。從gif圖中可以看出,宿主Activity有上下兩個Fragment:上面的Fragment稱為FragmentA,下面的叫FragmentB。FragmentA的UI布局包含兩個Button,FragmentB的UI布局包含一個TextView。點擊FragmentA中的Button,FragmentB中的TextView的內容發生變化。
這個Demo就是按照上面新聞場景進行編寫。宿主Activity負責維系兩個Fragment的通信,宿主Activity起到一個橋梁的作用。FragmentA中的按鈕點擊之後,並不會直接操作FragmentB中的TextView,而是通過接口回調的方式傳遞給宿主Activity,宿主Activity根據FragmentA中的點擊事件去改變FragmentB中的內容。
如果對Toolbar的用法還不清楚的,請點擊Android常用UI之Toolbar 。所以關於Toolbar的細節就不講了,下面主要講一下Fragment與Toolbar的集成。
其實,在Fragment中使用Toolbar的方式幾乎和在Activity中使用是一致的。在Fragment也有onCreateOptionsMenu()回調和onOptionsItemSelected()回調,但是在Fragment中需要使能菜單功能,通過setHasOptionsMenu(true);方法實現。
下面看代碼,首先是Activity的布局文件:
<framelayout android:id="@+id/fragment_container" android:layout_height="match_parent" android:layout_width="match_parent">
</framelayout>
添加了v7包中的Toolbar布局;下面的FrameLayout是Fragment的容器。
下面看styles.xml
下面是Activity的代碼:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//使用Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//添加Fragment到容器中
FragmentManager fm = getFragmentManager();
mToolbarFragment = (ToolbarFragment) fm.findFragmentById(R.id.fragment_container);
if(mToolbarFragment == null) {
mToolbarFragment = new ToolbarFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, mToolbarFragment);
ft.commit();
}
}
下面是Fragment的代碼:
public class ToolbarFragment extends Fragment
{
private TextView mTv;
private AppCompatActivity mActivity;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//在Fragment中使用菜單,必須先使能
setHasOptionsMenu(true);
ActionBar ab = mActivity.getSupportActionBar();
ab.setTitle("Fragment");
//添加Toolbar的導航按鈕
ab.setDisplayHomeAsUpEnabled(true);
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try {
mActivity = (AppCompatActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException("activity must be AppCompatActivity");
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View contentView = inflater.inflate(R.layout.example_fragment, container, false);
mTv = (TextView) contentView.findViewById(R.id.tv);
mTv.setText("hello world!!!");
return contentView;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
inflater.inflate(R.menu.menu_main, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.action_search:
Toast.makeText(getActivity(), "search", Toast.LENGTH_SHORT).show();
break;
case R.id.action_setting:
Toast.makeText(getActivity(), "setting", Toast.LENGTH_SHORT).show();
break;
case android.R.id.home:
mActivity.finish();
break;
}
return true;
}
}
OK,在onCreate()回調中調用setHasOptionsMenu(true)來在Fragment中使能菜單,通過onCreateOptionsMenu()回調來加載菜單資源(res/menu/menu_main.xml),通過onOptionsItemSelected(),來響應菜單項點擊事件。
最後別忘了在manifest文件中使用android:theme=”@style/AppTheme.NoActionBar”主題,目的是阻止系統使用自帶的ActionBar。
最後,貼運行效果圖:
好的,本篇文章到這就結束了,總算寫完了~~~
1、JDK:Java Development Kit,java開發工具包。http://www.oracle.com/technetwork/java/javase/do
我們這片博文就來聊聊這個反響很不錯的OkHttp了,標題是我惡搞的,本篇將著重詳細的分析,探索OkHttp這個框架的使用和封裝一.追其原理 Android系統提供了兩種
有些列表信息需要手動去更新,此時比較常用的就是下拉刷新列表,在這裡就使用下拉列表來刷新當前Wifi信息 目錄結構 界面 關鍵代碼 下拉列表類 p
手機qq怎麼安裝?,安裝目錄哪裡找,下面小編就簡單介紹下。 手機qq安裝教程 方式一:手機下載(Android Market 電子市場)安裝 啟動程序列表的