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之间的耦合度,增加代码的复用率。
新闻热点
疑难解答