Fragment 表示 Activity 中的行为或用户界面部分。您可以将多个片段组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。
片段必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。 不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除它们。 当您执行此类片段事务时,您也可以将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。 返回栈让用户可以通过按返回按钮撤消片段事务(后退)。
当您将片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为 元素插入您的 Activity 布局中,或者通过将其添加到某个现有 ViewGroup,利用应用代码进行插入。不过,片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的片段用作 Activity 的不可见工作线程。
————转载于Google官方API
一、Fragment的创建与使用
Fragment的使用方式有两种,一种是在xml布局文件中直接静态使用定义好的Fragment;另一种是在Activity的Java代码中动态添加Fragment。
先来讲第一种:
首先我们要为Fragment写一个布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#BEBEBE"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:text="fragment"/>
</LinearLayout>
在使用Fragment前我们必须创建一个继承自Fragment的类(这里有不同包下的Fragment接口,一种是android.app包下的,一种是android.support.v4.app包下的,我们一般使用功能比较全面的android.support.v4.app包下Fragment),然后重写这个Fragment的onCreatView方法。
package com.studio.fragmentdemo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
//注意fragment继承的Fragment类最好是v4包下的
public class FragmentTest extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
//将布局fragment_layout加载到Fragment中,最后一个参数表示是否附加到布局文件的根目录
return inflater.inflate(R.layout.fragment_layout,container,false);
}
之后在要用到这个Fragment的Activity所绑定的布局文件中添加上这个Fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_demo_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.studio.fragmentdemo.DemoFragmentActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="在xml中使用Fragment"/>
<!--
在xml中使用Fragment,即静态添加Fragment
在布局中添加一个已经写好的Fragmemt
name:指定要在布局中实例化的Fragment类
若没有加最后一句tools:layout="@layout/fragment_laYOUT"则无法在预览图中看到,只有启动模拟器才能看到
-->
<fragment
android:name="com.studio.fragmentdemo.FragmentTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_layout"></fragment>
</LinearLayout>
之后我们运行代码,就可以在对应的Activity中看到这个Fragment了。
再来说第二种方法:
还是与之前一样的Fragment的布局文件,这里不再赘述也不再贴上代码。
还是与之前一样的创建继承自Fragment的类,这里也不再赘述不再贴上代码。
然后在要用到这个Fragment的Activity中用如下代码为其添加Fragment,注意为了方便显示Fragment的效果,我在Activity的布局文件(LinearLayout)中为这个Fragment创建了一个子布局(FrameLayout,其id为fl_fragment)专门用来显示Fragment
//要想在Activity中管理(如添加、删除、修改)Fragment,需要使用FragmentManager(即Fragment管理器)
//由于我们在创建Fragment时使用的是support.v4包下的,因此得到FragmentManager对象的方法是getSupportFragmentManager(),若使用的是Activity内置的Fragment(即android.app包下的),那么在这里使用getFragmentManager()就好了
FragmentManager fragmentManager=getSupportFragmentManager();
//获取Fragment的事务对象并利用FragmentManager开启事务
FragmentTransaction transaction=fragmentManager.beginTransaction();
//创建自定义的Fragment对象
FragmentTest fragmentTest=new FragmentTest();
//第一个参数的意思是要将Fragment放到哪一个container中(在这里container就是一个布局),第二个参数就是要添加的Fragment对象,第三个参数是我们给Fragment取的Tag,可有可无,但为了方便之后的删除操作,这里为其添加一个Tag"fragment"
transaction.add(R.id.fl_fragment,fragmentTest,"fragment");
//提交操作,千万不能漏
transaction.commit();
二、Fragment的删除、替换、隐藏、显示
注:之前的代码依然沿用
1、
首先说一说如何动态的删除一个Fragment,首先我们在显示这个Fragment的Activity的布局文件下为其添加一个用来隐藏Fragment的Button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="remove"
android:text="Remove fragment"/>
然后在Activity中完成这个Button的点击事件,注意从这里开始我已将fragmentManager设置为整个Activity的成员变量。
public void remove(View view)
{
FragmentTransaction transaction=fragmentManager.beginTransaction();
//通过findFragmentByTag的方法找到要删除的Fragment并进行删除,当然还有findFragmentById的方法
//remove方法的唯一参数就是要删除的Fragment对象
transaction.remove(fragmentManager.findFragmentByTag("fragment"));
transaction.commit();
}
这时我们运行代码,点击remove按钮,就发现显示的Fragment被删除不见了。
接着说说如何用一个新的Fragment替换旧的Fragment
先为这个新Fragment新建一个布局
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AnotherFragment"/>
</LinearLayout>
首先我们新建一个Fragment命名为AnotherFragmentTest(原来的叫FragmentTest)
package com.studio.fragmentdemo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class AnotherFragmentTest extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
return inflater.inflate(R.layout.anotherfragment_layout,container,false);
}
}
然后在Activity的布局文件中为替换Fragment这个操作新建一个Button命名为replace
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="replace"
android:text="replace"/>
接着在Activity的Java代码中完成这个Button的点击事件
public void replace(View view)
{
FragmentTransaction transaction=fragmentManager.beginTransaction();
AnotherFragmentTest anotherFragmentTest=new AnotherFragmentTest();
/*将ID为fl_container的布局中的Fragment用新的Fragment替换*/
transaction.replace(R.id.fl_container,anotherFragmentTest);
transaction.commit();
}
之后我们运行代码,点击了replace按钮后,就发现原来文本为Fragment的Fragment被新的文本内容为AnotherFragment的Fragment替换了。
2、
接下来是如何隐藏和显示一个Fragment
首先我们为隐藏Fragment和显示Fragment事件添加触发Button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="hide"
android:text="hide"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="show"
android:text="show"/>
然后在Java代码中完成这两个Button的点击事件
public void hide(View view)
{
FragmentTransaction transaction=fragmentManager.beginTransaction();
transaction.hide(fragmentManager.findFragmentByTag("fragment"));
transaction.commit();
}
public void show(View view)
{
FragmentTransaction transaction=fragmentManager.beginTransaction();
transaction.show(fragmentManager.findFragmentByTag("fragment"));
transaction.commit();
}
之后运行代码,点击hide发现文本内容为Fragment的Fragment不见了,再点击show发现Fragment再次出现。
3、
再来讲讲Fragment的数据传递。
[1]、首先说一说一个Activity中只放一个Fragment的情况。
现在在一个Activity中只有一个Fragment,现在开启App默认显示的Fragment是一个EditText和一个Button,要求是当我在EditText输入字符串后,点击Button,输入的字符串将传给另一个Fragment中的TextView,并让另一个Fragment代替这个Activity中的Fragment显示出来,下面说代码。
先在Activity的布局文件中添加一个容器布局来显示Fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_fragment_values"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.studio.fragmentdemo.FragmentValuesActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></FrameLayout>
</LinearLayout>
然后为默认显示的Fragment和最终替换上来的Fragment写布局文件
<?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">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="input"/>
<Button
android:id="@+id/btn_sendValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Value"
android:onClick="send"/>
</LinearLayout>
<?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">
<!--用来接受并显示其他Fragment传递过来的EditText中的文本内容-->
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
接着分别创建这两个Fragment并在onCreatView方法中完成传输数据、接收数据和替换Fragment的方法
package com.studio.fragmentdemo;
import android.os.Bundle;
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;
/**
* Created by xwx on 2017/2/24.
*/
public class AnotherFragment_value extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_value,container,false);
//注意这不是在Activity类或者View类下,无法直接调用findViewById方法,必须通过View的对象调用findViewById方法
final EditText editText= (EditText) view.findViewById(R.id.et);
Button btn_sendValue= (Button) view.findViewById(R.id.btn_sendValue);
btn_sendValue.setOnClickListener(new View.OnClickListener()
{
//点击SendValue按钮时,将EditText中的文本内容传递到另一个Fragment中让另一个Fragment显示出来
@Override
public void onClick(View v)
{
AnotherFragment_values2 anotherFragment_values2=new AnotherFragment_values2();
//通过bundle传递数据
Bundle bundle=new Bundle();
bundle.putString("arg",editText.getText().toString());
anotherFragment_values2.setArguments(bundle);
getFragmentManager().beginTransaction().replace(R.id.container,anotherFragment_values2).commit();
}
});
return view;
}
}
package com.studio.fragmentdemo;
import android.os.Bundle;
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.EditText;
import android.widget.TextView;
/**
* Created by xwx on 2017/2/24.
*/
public class AnotherFragment_values2 extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view=inflater.inflate(R.layout.fragment_value2,container,false);
TextView textView= (TextView) view.findViewById(R.id.tv);
//通过getArguments得到传过来的Bundle对象,再通过Bundle的getString方法和字符串对应的key得到字符串
textView.setText(getArguments().getString("arg"));
return view;
}
}
[2]、
继续学习一种降低耦合的Fragment之间传递数据的方法——接口回调
我们最终要达到的目的是在一个Fragment中有三个Button,文本内容分别为Button1,Button2,Button3。现在我们要做的就是当我们点击其中任何一个Button的时候,位于这个Fragment下面的另一个Fragment的TextView文本内容变成与按钮文本内容一样。
首先我们为两个Fragment写布局文件
<?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">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button1"/>
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button2"/>
<Button
android:id="@+id/btn_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button3"/>
</LinearLayout>
<?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">
<TextView
android:id="@+id/tv_value"
android:text="Waiting for initing..."
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
然后创建两个对应的Fragment
package com.studio.fragmentdemo;
import android.content.Context;
import android.os.Bundle;
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;
/**
* Created by xwx on 2017/2/24.
*/
public class FragmentValuePass1 extends Fragment implements View.OnClickListener
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view=inflater.inflate(R.layout.fragment_valuepass1,container);
Button btn_1= (Button) view.findViewById(R.id.btn_1);
Button btn_2= (Button) view.findViewById(R.id.btn_2);
Button btn_3= (Button) view.findViewById(R.id.btn_3);
//为三个按钮绑定监听器
btn_1.setOnClickListener(this);
btn_2.setOnClickListener(this);
btn_3.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v)
{
}
}
package com.studio.fragmentdemo;
import android.os.Bundle;
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.TextView;
import org.w3c.dom.Text;
/**
* Created by xwx on 2017/2/24.
*/
public class FragmentValuePass2 extends Fragment
{
private TextView textView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view=inflater.inflate(R.layout.fragment_valuepass2,container);
textView= (TextView) view.findViewById(R.id.tv_value);
return view;
}
}
在这里我们换一种方法使用Fragment,我们将两个Fragment定义在Activity的xml布局文件中进行使用,注意所有的fragment都要给其附上id
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<fragment
android:id="@+id/frg_1"
android:name="com.studio.fragmentdemo.FragmentValuePass1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_valuepass1"/>
<fragment
android:id="@+id/frg_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:name="com.studio.fragmentdemo.FragmentValuePass2"
tools:layout="@layout/fragment_valuepass2"/>
</LinearLayout>
然后在要显示文本的Fragment中为其添加一个单独的改变TextView内容的方法。
//改变这个Fragment的TextView内容的方法
public void setFragmentTextValue(String textValue)
{
textView.setText(textValue);
}
之后创建一个回调接口
package com.studio.fragmentdemo;
//回调接口
public interface IButton
{
//回调方法
void onFragmentButtonClick(String text);
}
让Activity实现这个回调接口并重写回调函数
//重写回调方法
@Override
public void onFragmentButtonClick(String text)
{
//当在布局中定义<fragment>标签时使用findFragmentById,否则使用new的形式。二者区别:在布局中定义<fragment>标签后Fragment是固定的,不可替换,推荐使用new的形式动态处理Fragment,使展示效果更加灵活。
FragmentValuePass2 fragmentValuePass2= (FragmentValuePass2) getSupportFragmentManager().findFragmentById(R.id.frg_2);
fragmentValuePass2.setFragmentTextValue(text);
}
回到具有三个Button的Fragment,我们首先为其定义一个全局的成员接口
private IButton mIButton;
并重写onClick方法
//重写监听事件,实现回调方法
@Override
public void onClick(View v)
{
//将Button的文本内容赋值给FragmentValuePass2中的TextView
mIButton.onFragmentButtonClick(((Button)v).getText().toString());
}
注意由于此时mIButton接口变量还未与任何类进行绑定,因此若就此执行代码会发生空指针异常,我们还需要在这个Fragment下重写一个Fragment生命周期中的回调方法让mIButton和这个Fragment所属的Activity绑定
//onAttach方法,在Fragment和Activity建立关联的时候回调
//在这里实例化mIButton
@Override
public void onAttach(Context context)
{
super.onAttach(context);
try
{
mIButton = (IButton) context;
}
catch (Exception e)
{
e.printStackTrace();
}
}
就这样,我们完成了一个以接口回调机制为基础的Fragment之间的数据传递,这种方式可以降低Fragment之间的耦合度,增加代码的复用率。