本文主要内容

  1. 如何管理Fragment回退栈
  2. Fragment如何与Activity交互
  3. Fragment与Activity交互的最佳实践
  4. 没有视图的Fragment的用处
  5. 使用Fragment创建对话框
  6. 如何与ActionBar,MenuItem集成等

管理Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" > <FrameLayout
android:id="@+id/id_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</FrameLayout> </RelativeLayout>

不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

package com.zhy.zhy_fragments;  

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Window; public class MainActivity extends Activity
{ @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.add(R.id.id_content, new FragmentOne(),"ONE");
tx.commit();
} }

很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button; public class FragmentOne extends Fragment implements OnClickListener
{ private Button mBtn; @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_one, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
mBtn.setOnClickListener(this);
return view;
} @Override
public void onClick(View v)
{
FragmentTwo fTwo = new FragmentTwo();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.id_content, fTwo, "TWO");
tx.addToBackStack(null);
tx.commit(); } }

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button; public class FragmentTwo extends Fragment implements OnClickListener
{ private Button mBtn ;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_two, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
mBtn.setOnClickListener(this);
return view ;
}
@Override
public void onClick(View v)
{
FragmentThree fThree = new FragmentThree();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
tx.add(R.id.id_content , fThree, "THREE");
// tx.replace(R.id.id_content, fThree, "THREE");
tx.addToBackStack(null);
tx.commit();
} }

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast; public class FragmentThree extends Fragment implements OnClickListener
{ private Button mBtn; @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_three, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);
mBtn.setOnClickListener(this);
return view;
} @Override
public void onClick(View v)
{
Toast.makeText(getActivity(), " i am a btn in Fragment three",
Toast.LENGTH_SHORT).show();
} }

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!

Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

Fragment与Activity通信的最佳实践

因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

首先看FragmentOne

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button; public class FragmentOne extends Fragment implements OnClickListener
{
private Button mBtn; /**
* 设置按钮点击的回调
* @author zhy
*
*/
public interface FOneBtnClickListener
{
void onFOneBtnClick();
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_one, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
mBtn.setOnClickListener(this);
return view;
} /**
* 交给宿主Activity处理,如果它希望处理
*/
@Override
public void onClick(View v)
{
if (getActivity() instanceof FOneBtnClickListener)
{
((FOneBtnClickListener) getActivity()).onFOneBtnClick();
}
} }

可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

再看FragmentTwo

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button; public class FragmentTwo extends Fragment implements OnClickListener
{ private Button mBtn ; private FTwoBtnClickListener fTwoBtnClickListener ; public interface FTwoBtnClickListener
{
void onFTwoBtnClick();
}
//设置回调接口
public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
{
this.fTwoBtnClickListener = fTwoBtnClickListener;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_two, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
mBtn.setOnClickListener(this);
return view ;
}
@Override
public void onClick(View v)
{
if(fTwoBtnClickListener != null)
{
fTwoBtnClickListener.onFTwoBtnClick();
}
} }

与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

最后看Activity :

package com.zhy.zhy_fragments;  

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Window; import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener; public class MainActivity extends Activity implements FOneBtnClickListener,
FTwoBtnClickListener
{ private FragmentOne mFOne;
private FragmentTwo mFTwo;
private FragmentThree mFThree; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); mFOne = new FragmentOne();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.add(R.id.id_content, mFOne, "ONE");
tx.commit();
} /**
* FragmentOne 按钮点击时的回调
*/
@Override
public void onFOneBtnClick()
{ if (mFTwo == null)
{
mFTwo = new FragmentTwo();
mFTwo.setfTwoBtnClickListener(this);
}
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.id_content, mFTwo, "TWO");
tx.addToBackStack(null);
tx.commit();
} /**
* FragmentTwo 按钮点击时的回调
*/
@Override
public void onFTwoBtnClick()
{
if (mFThree == null)
{
mFThree = new FragmentThree(); }
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(mFTwo);
tx.add(R.id.id_content, mFThree, "THREE");
// tx.replace(R.id.id_content, fThree, "THREE");
tx.addToBackStack(null);
tx.commit();
} }

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

如何处理运行时配置发生变化

我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

package com.zhy.zhy_fragments;  

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.Window; public class MainActivity extends Activity {
private static final String TAG = "FragmentOne";
private FragmentOne mFOne; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); Log.e(TAG, savedInstanceState+""); if(savedInstanceState == null)
{
mFOne = new FragmentOne();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.add(R.id.id_content, mFOne, "ONE");
tx.commit();
} } }

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

详情请见:

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案 - Hongyang - 博客频道 - CSDN.NET

Fragmeny与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

a、在Fragment的onCreate中调用 setHasOptionsMenu(true);

b、然后在Fragment子类中实现onCreateOptionsMenu

c、如果希望在Fragment中处理MenuItem的点击,也可以实现 onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。

Fragment

package com.zhy.zhy_fragments;  

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast; public class FragmentOne extends Fragment
{ @Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_one, container, false);
return view;
} @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
inflater.inflate(R.menu.fragment_menu, menu);
} @Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.id_menu_fra_test:
Toast.makeText(getActivity(), "FragmentMenuItem1", Toast.LENGTH_SHORT).show();
break;
}
return true;
} }

Activity

package com.zhy.zhy_fragments;  

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.widget.Toast; public class MainActivity extends Activity {
private static final String TAG = "FragmentOne";
private FragmentOne mFOne; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); Log.e(TAG, savedInstanceState + ""); if (savedInstanceState == null)
{
mFOne = new FragmentOne();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.add(R.id.id_content, mFOne, "ONE");
tx.commit();
} } @Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.action_settings:
Toast.makeText(this, "setting", Toast.LENGTH_SHORT).show();
return true;
default:
//如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx
return super.onOptionsItemSelected(item);
}
} }

注意:如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx

效果图:

没有布局的Fragment的作用

没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的

请参考博客:

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案 - Hongyang - 博客频道 - CSDN.NET

7、使用Fragment创建对话框

这是Google推荐的方式,请参考

Android 官方推荐 : DialogFragment 创建对话框 - Hongyang - 博客频道 - CSDN.NET

更多关于Fragment,比如在项目中的最佳实践,请参考

Android Fragment 你应该知道的一切 - Hongyang - 博客频道 - CSDN.NET

完成,参考链接:

Android Fragment 真正的完全解析(下) - Hongyang - 博客频道 - CSDN.NET

Android之Fragment(二)的更多相关文章

  1. Android Fragment (二) 实例2

    由于看客的要求,我就把读者所要的写出来. 由于上一篇是每一个Fragment 实例了同一个layout.xml ,造成了读者的困惑,这篇我就让每一个Fragment 加载一个不同的layout.xml ...

  2. Android Fragment (二) 实例1

    千呼万唤始出来,今天就也写一篇Frament 的简单实例.先看效果: 一看这效果,首先我们的配置资源文件:new -->android xml -->selector --> 四个图 ...

  3. android 学习随笔二十三(动画:Fragment )

    Fragment * 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容 * 在一个Activity中切换多个界面,每个界面就是一个Fragment* Fragmnen ...

  4. Android 进阶 Fragment 介绍和使用 (二)

    管理fragment 因为FragmentManager的API是在Android 3.0,也即API level 11开始引入的,所以对于之前的版本,需要使用support library中的Fra ...

  5. Android开发——Fragment知识整理(二)

    0.  前言 Android开发中的Fragment的应用非常广泛,在Android开发--Fragment知识整理(一)中简单介绍了关于Fragment的生命周期,常用API,回退栈的应用等知识.这 ...

  6. android之Fragment基础详解(一)

      一.Fragment的设计哲学 Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计.平板电脑的屏幕比手机的大得多,有 ...

  7. Android之Fragment学习笔记①

    Android Fragment完全解析,关于碎片你所需知道的一切 一. 什么是FragmentFragment(碎片)就是小型的Activity,它是在Android3.0时出现的.Fragment ...

  8. Android 进阶 Fragment 介绍和使用 (一)

    Fragment概述 Fragment是activity的界面中的一部分或一种行为.你可以把多个Fragment们组合到一个activity中来创建一个多面界面并且你可以在多个activity中重用一 ...

  9. Android 之 Fragment

    一  左侧标题列表 1.1  布局 left_fragment.xml <ListView xmlns:android="http://schemas.android.com/apk/ ...

随机推荐

  1. 人工神经网络(ANN)

    参考资料:http://www.cnblogs.com/subconscious/p/5058741.html 从函数上来看,神经网络是回归方程的级联叠加,用来逼近目标函数的,本质是一种模拟特征与目标 ...

  2. hdu 1201 18岁生日

    #include <stdio.h> int r(int y) { return (y%4==0&&y%100!=0)||(y%400==0); } int f(int y ...

  3. KxMenu下拉菜单

    + (void)createMenu:(id)sender target:(UIViewController *)t { NSArray *menuItems = @[ [KxMenuItem men ...

  4. gdb命令与调试方法

    单线程 http://www.cnblogs.com/lidabo/p/5629830.html 编译程序一定要加-g选项 gcc -g test.c -o test 进入gdb调试:gdb 程序名 ...

  5. Substance风格实例大全javaswing皮肤风格大全(原)

    转载请注明来路:http://www.cnblogs.com/langtianya/ 下图是所支持的设置 下面把上面的每一个选项的子选项进行试验 substance皮肤: AutumnSkin Bus ...

  6. 在命令行中运行eclipse中创建的java项目

    在命令行中运行eclipse中创建的java项目 博客分类: java相关 javaeclipse命令行  由于项目要求,需要对eclipse中的项目进行打包,似的可以在客户机上不装eclipse的情 ...

  7. sprintf()函数的用法

    sprintf(g_strAppName, "%s",pLast+1); ----------------------------------------------------- ...

  8. 百度编辑器Ueditor 初始化加载内容失败解决办法

    项目上有用到百度文本编辑器ueditor,在页面加载的时候初始化编辑器内容时候,使用 $.document.ready(function() { UE.getEditor('editor').setC ...

  9. thinkphp中I方法

    概述 正如你所见到的一样,I方法是ThinkPHP众多单字母函数中的新成员,其命名来自于英文Input(输入),主要用于更加方便和安全的获取系统输入变量,可以用于任何地方,用法格式如下:I('变量类型 ...

  10. memcached工作原理

    1.Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找.对比以及做到尽可能的散列.同时,memcached用的 ...