Fragment详细介绍

什么是Fragment ?

Fragment意为碎片,片段。在Google的官方文档介绍,Fragment是一个应用程序的用户界面的一部分或者用来执行某些行为。我们都知道,如果需要跟用户交互,都需要用到Activity,所以,Fragment必须是嵌入到Activity中。

我们可以把多个Fragment放到一个Activity里,也可以把一个Fragment在多个Activity里复用。我们可以理解FragmentActivity的一个模块。它包含自己的生命周期,可以响应它自己的交互事件。我们可以随意的在运行中的Activity中把加入或者移除Fragment。就像我们可以复用多个Activity一样。

为什么会有 Fragment

Fragment 是在 Android 3.0时加入的。Fragment 的出现一方面是为了缓解 Activity任务太重的问题;另一方面是为了处理在不同屏幕上UI组件的布局问题,尤其是大屏幕;而且它还提供了一些Activity不具有的新特性来处理一些在 开发中比较难解决的问题。

在开发中大型的应用时,如果遇到特别复杂的界面,Activity就会显得特别复杂,就会变得臃肿。如果使用FragmentActivity分割,就会很好的缓解这种情况。实际上Fragment不仅仅是界面,不仅仅是View;它实际上干的就是Activity的事情;它负责的是控制View和他们之间的逻辑。

Activity分割成多个Fragment后,Activity就可以脱出身来,不再去处理大量的View了,它只需要管理Fragment或者少量的View

Fragment 不仅仅可以管理View,也可以管理一些不可视的任务。比如在Activity因切换横竖屏而销毁重建时,它仍然可以保留实例。当然它的前提是必须要设置retainInstance属性。

虽然它是在Android 3.0时加入的,Google也提供了support包来支持之前的版本。这说明Google对Fragment的重视,并且提倡使用Fragment

初探 Fragment

上面提到Fragment一方面是为了处理在不同屏幕上UI组件的布局问题,尤其是大屏幕;如下图,是一个新闻app的界面示意图,为了更好的利用屏幕的空间,它要求在比较大的平板上,呈左面的显示方式,左面是一个新闻列表,点击某个新闻标题,在右面显示具体的新闻内容。如果在较小的手机上,就要呈右边的形式,首先显示的一个新闻列表,如果点击某个新闻标题,就在另一屏显示具体的新闻内容。 

如果只用Activity实现,十分的麻烦,但是如果使用Fragment,就显得很简单了。我们可以先创建两个Fragment,一个NewsListFragment用来显示新闻列表,一个NewsDetailFragment用来显示新闻内容。它们的布局分别为 
fragment_news_list 和 fragment_news_detail;我们设置在Activity的布局时,可以根据屏幕的大小来编写两个布局,一个包含是右边的样式,一个是左面的样式。然后根据屏幕的大小设置最小宽度限定符,比如large/sw600dp,在程序加载的时候,会根据屏幕大小动态去选择需要的布局。具体的步骤我们会后面介绍。

Fragment的生命周期

因为Fragment是依附于Activity的,那么它必然会受到Activity生命周期的影响。Google在官网给出了一张图来说明它们两者间生命周期的的关系。 

根据上图我们可以看出,FragmentActivity 多出几个额外的回调方法:

onAttach(Context):

Fragment第一次关联在Context上时调用。这里Context就是指的Activity

onCreateView(LayoutInflater, ViewGroup, Bundle):

看字面意思就可以知道,该方法是创建Fragment自己的视图。如果没有图像界面就可以返回null。该方法跟下面的 onDestroyView()相对应。

onActivityCreated(Bundle):

Fragment所附着的Activity创建完毕之后调用;并且这个Fragment的视图也会被实例化。这个方法可以用来做初始化或者用来恢复界面或者状态。

onDestroyView():

跟上面的 onCreateView()相对应。当Fragment的视图移除时调用。下一次要展示Fragment时,视图会重新创建。

onDetach():

跟上面的onAttach()相对应,当FragmentActivity关联被取消时调用

其它的几个跟Activity回调相同的,意思也跟Activity的回调的用法一样,这里就不一一解释了。

Fragment 的用法

Fragment静态用法:

在初探Fragment中提到的就是Fragment的静态使用方法。下面我们具体实现一下。

所谓静态就是在布局里面写死,不能在程序运行中,动态添加/移除Fragment。 
在初探Fragment时我们需要在不同屏幕大小实现显示不同的布局。

根据需求,我们首先实现一个实现最简单的只有一个新闻列表的布局,当然我们还是使用Fragment实现了。

以下的实现都是使用的 Android 3.0之后提供的Fragment,如果要支持以前的版本请将Activity替换成Support包中的FragmentActivity,导包的时候,要导入import android.support.v4.app.Fragment;

首先我们先创建一个Activity,叫做NewsActivity,继承自Activity,用于显示的布局叫做activity_news:

public class NewsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news);
    }
}

然后创建一个类去继承Fragment,我们暂且叫它NewsTitleListFragment。就像这样:

public class NewsTitleListFragment extends Fragment{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }
}

这个Fragment是用来显示新闻标题列表的。我们还需要创建一个显示新闻列表的布局,我们使用ListView来展示列表。我们给它加个背景便于区别。具体如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:background="#ff11ffff"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <ListView
        android:id="@+id/lv_news_title_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

然后我们回到NewsTitleListFragment,在onCreateView()中我们来返回我们的布局视图。

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_news_list, container, false);
    return view;
}

我们通过LayoutInflater将布局转换为View,作为Fragment的视图。

下面我们准备一些数据,用来填充这个 ListView:

private String[] mTitles;

void initTitles() {
    mTitles = new String[10];
    for (int i = 0; i < 10; i++) {
        mTitles[i] = "News Title " + i;
    }
}

然后在onCreateView()绑定ListView :

mTitleList = (ListView) view.findViewById(R.id.lv_news_title_list);

并且设置适配器和行点击事件。在Fragment获取Activity可以使用getActivity:

ArrayAdapter adapter = new ArrayAdapter(getActivity(),android.R.layout.simple_list_item_1,mTitles);
mTitleList.setAdapter(adapter);
 mTitleList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            startActivity(getActivity(),NewsDetailActivity.class);
        }
    });

这里的NewsDetailActivity,只是一个展示界面就不在具体说了。很简单。

然后将NewsTitleListFragment使用fragment标签设置到NewsActivity的布局里:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <fragment
        android:id="@+id/fragment_news_list"
        android:name="com.liteng.app.fragments.lifecycle.NewsTitleListFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

</LinearLayout>

这里使用 android:name指向新建的NewsTitleListFragment

运行后是这样的: 

但是我们要支持在大屏情况下显示的是两个部分:左边是列表,右边是内容。这就需要使用在我们适配时用到的large限定符。我们在res目录下,新建一个文件夹layout-large,然后在里面新建一个同样的布局activity_news,这样程序运行到大屏幕的设备上时就会自动去加载这个布局。

这样我就需要两个Fragment,我们类似的新建一个NewsDetailFragment,其界面布局为fragment_news_detail。这里我只是模拟一下新闻内容,不再具体做实现。

public class NewsDetailFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_news_detail, container, false);
        return view;
    }
} 

下面是布局内容:

<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".fragments.lifecycle.NewsListFragment">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="20dp"
        android:gravity="center"
        android:text="News Title"
        android:textSize="20sp"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="The Czech Republic sent two fighter jets to escort Xi's plane when it entered the country's airspace. Xi was received by Czech Foreign Minister Lubomir Zaoralek and other senior officials at the airport.Hailing the long-held friendship between the two countries, Xi, in his written remarks upon arrival, extended sincere greetings and best wishes to the Czech people."
        android:textSize="16sp"/>
</LinearLayout>

重点是Activity显示布局activity_news,我们在这里面放两个fragment标签。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <fragment
        android:id="@+id/fragment_news_list"
        android:name="com.liteng.app.fragments.lifecycle.NewsTitleListFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <fragment
        android:id="@+id/fragment_news_detail"
        android:name="com.liteng.app.fragments.lifecycle.NewsDetailFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

这样我们先到平板上运行一下:

正好是符合要求。这样我们只用了一个Activity就实现了两个要求。只是在上面提到的NewsDetailActivity,只会在小屏设备上用到。在大屏设备上就不会用到了。

但是还有一个问题,我们如果在Activity里区分是大屏还是小屏,我们可以去判断有没有加载具体内容的NewsDetailFragment,来做区分,然后再去做具体操作;我们在NewsActivity里通过Fragment的id,来判断是否存在这个Fragment:

 Fragment fragmentNewsDetail = getFragmentManager().findFragmentById(R.id.fragment_news_detail);
    if (fragmentNewsDetail != null) {
        Log.e(TAG,"大屏");
    }else{
        Log.e(TAG,"小屏");
    }

如果大家对这里的 getFragmentManager().findFragmentById(R.id.fragment_news_detail),有疑惑,我们会在下一节具体介绍。

下面附上一张Activity和Fragment的完整生命周期图,如果有兴趣的话,可以看看。这张图是在网上找的,如果涉及到版权,我会及时删除。


FragmentManager

FragmentManager是在Activity里根Fragment交互的接口,主要在Activity对Fragment进行管理。它可以通过Activity中的getFragmentManager()方法获取。如果使用的supprot-v4包中的FragmentActivity,则需要使用 getSupportFragmentManager()

FragmentTransaction

FragmentTransaction保证Fragment操作的原子性,跟数据库中的事务差不多。它可以通过上面的FragmentManager获取。

FragmentTransaction transaction = getFragmentManager().benginTransatcion();//开启一个事务

FragmentTransaction事务有几个很重要和比较常用的方法:

  1. add(int,Fragment,String):往Activity里添加一个Fragment
  2. remove(remove):移除一个已有的Fragment;如果这个Fragment被加入到一个容器中,它的视图也会被移除。这里容器一般指的是Activity。
  3. replace(int,Fragment):将已有的Fragment替换。相当于对Fragment先进行remove(),然后再add()
  4. hide(Fragment):隐藏已有的Fragment,但不会销毁。跟下面的show()对应。
  5. show(Fragment):显示之前使用hide()隐藏的Fragment。
  6. detach(Fragment):将Fragment从UI上移除。相当于把这个Fragment放到回退栈(后面介绍),虽然将它将它从UI移除,但是FragmentManger依然维护着它。
  7. attach(Fragment):如果调用上面的detach(Fragment)方法,将Fragment从UI移除,可以调用这个方法重建视图并将它附着到UI上并且显示。
  8. commit():提交一个事务;如果不调用这个方法之前所做的所有事情都不会执行;这个提交不会立即发生;它在主线程中工作。注意这个方法必须在保存状态(onSaveInstance())之前调用,否则会抛出异常。因为如果Activity需要恢复它的状态,commit()提交后的状态的可能会丢失。如果要解决这个问题可以使用 commitAllowingStateLoss()。也可以控制它的提交时机,我们可以在Activity处于运行状态,并且在onPause()方法和onStop()方法中提交事务。
  9. commitAllowingStateLoss():跟上面的commit()类似,但是它允许在Activity的状态保存之后提交事务;这样做有些危险,因为如果Activity需要恢复它之前的状态,很容易造成提交事务的丢失,所以它只允许在界面状态不在改变的状态下使用。

回退栈

类似Activity,Fragment的事务也有一个任务栈。将事务添加到回退栈之后,提交的事务都会被存储在栈中。我们可以通过出栈操作恢复之前的事务。

addToBackStack(String):

将Fragment的事务添加到栈中,并通过参数提供一个名称

popBackStack()

将添加的事务弹出栈,同样通过点击返回键也可以达到出栈的效果。这个方法是异步的。当出栈的请求进入请求队列之后,只有轮它时才会有用。

popBackStackImmediate()

跟上面的popBackStack()类似,但是它会在调用之后立即执行。在我们点击返回键时,执行的Fragment出栈操作就是使用的这个方法。

Fragment的动态用法

该了解的内容差不多了,下面我们讲一下Fragment的动态用法。

首先我们先创建几个Fragment,以备后面使用,我们分别创建Fragment1,Fragment2,Fragment3;因为它们三个基本一样,所以我们只给出Fragment1的代码:

public class Fragment1 extends Fragment {
    View rootView;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_layout_1, container, false);
        return rootView;
    }
}

然后我们需要一个Activity:

public class BackstackActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_backstack);
    }
}

这个Activity的布局如下,我给它的根布局设置id,用来装Fragment的内容:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/fragment_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</RelativeLayout>

我们新建一个Fragment,并且将它添加到Activity中:

public class BackstackActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_backstack);
        //新建一个Fragment
        Fragment1 fragment1 = new Fragment1();
        //获取FragmentManager,在Support V4包里使用 getSupportFragmentManager()
        FragmentManager manager = getFragmentManager();
        //开始Fragment的事务
        FragmentTransaction transaction = manager.beginTransaction();
        //往Activity里添加Fragment
        transaction.add(R.id.fragment_container, fragment1);
        //提交事务
        transaction.commit();
    }
}

上面的关键代码是这两句:

    //往Activity里添加Fragment
    transaction.add(R.id.fragment_container, fragment1);
    //提交事务
    transaction.commit();

我们将fragment1,添加到Activity中,并且填充id为fragment_container的布局。

类似的,我们也可在Fragment里面调用这句代码,我们可以通过添加新的Fragment来刷新界面。

下面我们分别在Fragment1,Fragment2,Fragment3里面添加TextView文字和按钮,用来添加Fragment和区分各个Fragment;我们这里还是只提供Fragment1的代码: 
这是布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#ffffff"
                android:orientation="vertical">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:text="这是第一个Fragment"
        android:textSize="30sp"/>

    <Button
        android:id="@+id/btn_jump_2_fragment2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="47dp"
        android:text="我要去Fragment2"/>
</RelativeLayout>

这个是代码

public class Fragment1 extends Fragment {
    View rootView;
    Button btnJump2Fragment2;   
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_layout_1, container, false);
        btnJump2Fragment2 = (Button) rootView.findViewById(R.id.btn_jump_2_fragment2);
        btnJump2Fragment2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //新建一个Fragment
            Fragment2 fragment2 = new Fragment2();
            //获取FragmentManager,在Support V4包里使用 getSupportFragmentManager()
            FragmentManager manager = getFragmentManager();
            //开始Fragment的事务
            FragmentTransaction transaction = manager.beginTransaction();
            //往Activity里添加Fragment
            transaction.add(R.id.fragment_container, fragment2);
            //提交事务
            transaction.commit();
            }
        });
        return rootView;
    }
}

这样我就可以通过添加Fragment的方式来实现界面的刷新或者跳转,效果图如下:

同样的我们也可以使用replace()实现上面的功能。这里就不演示了,只需将add()替换成replace()就OK。

既然我们上面讲到了回退栈,那我们是不是也可以使用Fragment的回退栈功能模拟Activity栈呢?当然答案是肯定的。首先明确一些功能,我们还是利用上面的例子。当我们点击回退键我们希望回到上一个Fragment。就类似于点击返回键Activity呈现的效果。其实这个效果很简单,就在添加Fragment事务之后,调用一个addToBackStack()方法,将事务加入到回退栈就OK了,当我们点击返回键后就去检查回退栈里是有Fragment的事务,如果有就会将上一次添加过的Fragment弹出显示。具体代码如下:

    //新建一个Fragment
    Fragment1 fragment1 = new Fragment1();
    //获取FragmentManager,在Support V4包里使用 getSupportFragmentManager()
    FragmentManager manager = getFragmentManager();
    //开始Fragment的事务
    FragmentTransaction transaction = manager.beginTransaction();
    //往Activity里添加Fragment
    transaction.add(R.id.fragment_container, fragment1);
    //将Fragment的事务添加到回退栈中
    transaction.addToBackStack("fragment1");
    //提交事务
    transaction.commit();

效果如下: 
 
前半部分效果跟上面的效果图一样,后面点击返回键,就会将之前添加过的Fragment一个一个移除,最后返回到Activity之前的空界面。


Fragment传递参数

上一节里我们可以实现Fragment之间跳转,但是我们需要在跳转的同时传递参数,要怎么办呢?不要着急,Google的工程师也想到了,所以提供了解决方法。

Fragment有个叫做setArguments(Bundle)的方法,用来传递参数;我们可以通过Bundle,添加我们需要的参数,然后再Fragment里使用getArguments()获取。 
比如:

Fragment2 fragment2 = new Fragment2();
Bundle bundle = new Bundle();
bundle.putString("aaa","bbbb");
fragment2.setArguments(bundle);

然后我们在Fragment里面可以在onCreate()里面获取这些参数:

Bundle bundle = getArguments(); 
String v = bundle.getString("aaa");

在官网上,提供了demo,使用的是静态方法。下面我们通过两个Fragment,来展示一下用法。

第一个Fragment,我们叫做FragmentOne;第二个Fragment我们叫做FragmentTwo,我们需要从FragmentOne传递参数到FragmentTwo.

FragmentOne:

public class FragmentOne extends Fragment {
    public static final String ARGUMENT_KEY = "ARGUMENT_KEY";
    private View rootView;
    private Button btnJump2FragmentTwo;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_layout_one, container, false);
        btnJump2FragmentTwo = (Button) rootView.findViewById(R.id.btn_jump_2_fragment_two);
        btnJump2FragmentTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Bundle bundle = new Bundle();
                bundle.putString(ARGUMENT_KEY,"这个是从FragmentOne传递给FragmentTwo的参数");
                FragmentTwo fragmentTwo = FragmentTwo.getFragmentTwo(bundle);
                getFragmentManager().beginTransaction().add(R.id.ft_container,fragmentTwo).commit();
            }
        });
        return rootView;
    }
}

FragmentTwo:

public class FragmentTwo extends Fragment {

    public static FragmentTwo getFragmentTwo(Bundle argumentBundle) {
        FragmentTwo fragmentTwo = new FragmentTwo();
        fragmentTwo.setArguments(argumentBundle);
        return fragmentTwo;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getArguments();
        if (bundle != null) {
            String argument = bundle.getString(FragmentOne.ARGUMENT_KEY);
            LogUtils.e(argument);
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_layout_two,container,false);
    }
}   

在FragmentTwo里面,我们获取到参数后,只打印了一下。

Fragment 启动 Activity

在Fragment里打开一个新的Activity的方式跟Activity之间的跳转一样都是使用startActivity()这个方法;如果需要传递返回值,需要startActivityForResult()onActivityResult配合使用;如果启动了一个新的Activity里面有一个Fragment,需要在Fragment里面执行 setResult(),但是Fragment里不提供这个方法,要如何办呢?我们可以使用 getActivity().setResult()

关于这些内容就不再代码演示了,跟Activity几乎一模一样,大家就按着Activity的实现方式实现就OK了。

Fragment 如何与Activity 交互

官网提供的方式是使用接口回调。

我们首先创建一个Fragment,并且定义一个回调接口,用来在Activity中回调。

public class BlankFragment extends Fragment {
    //定义一个回调接口
    private OnFragmentInteractionListener mListener;

    public BlankFragment() {
    }

    public static BlankFragment newInstance(Bundle arg) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            Bundle args = getArguments();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_blank, container, false);
        Button button = (Button) view.findViewById(R.id.btn_change_text);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //将下面的这些文字传递给Activity
                onButtonPressed("我要修改Activity中的文字");
            }
        });
        return view;
    }

    public void onButtonPressed(String text) {
        if (mListener != null) {
            mListener.onFragmentInteraction(text);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * 回调的接口,需要在Activity中实现,当我们的Fragment有操作时,会通过里面的方法通知到Activity
     */
    public interface OnFragmentInteractionListener {
        //通知到Activity Fragment 有操作,并且将需要的参数传递给Activity
        void onFragmentInteraction(String text);
    }
}

在Activity中就简单多了,只需要继承Fragment中实现的接口,并将Fragment显示到界面,就Ok了。

public class BlankActivity extends AppCompatActivity implements BlankFragment.OnFragmentInteractionListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blank);
        //获取Fragment的实例,这里我们不需要传递参数
        BlankFragment fragment = BlankFragment.newInstance(null);
        //将Fragment加入到Activity中
        getFragmentManager().beginTransaction().add(R.id.ft_container, fragment).commit();
    }

    @Override
    public void onFragmentInteraction(String text) {
        TextView textView = (TextView) findViewById(R.id.textView);
        textView.setText(text);
    }
}

当我们点击Fragment中的按钮时,Activity中TextView的文字就会发生改变: 

Android提供的常用Fragment

DialogFragment

仅仅从字面上,就知道这是一个类似对话框的Fragment,它可以很容易的实现自定义对话框;当然它还依然是一个Fragment,跟其它的Fragment有一样的生命周期,只是它被封装成了一个专门显示Dialog的Fragment。它没有自己的界面,这需要我们继承它,然后重写它的onCreateView()方法,然后自定义界面就OK。下面我来看代码:

首先我写一下布局:

<LinearLayout 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"
              android:layout_margin="10dp"
              android:orientation="vertical"
              android:padding="15dp"
              tools:context="com.liteng.app.fragments.commonfragment.MyDialogFragment">

    <TextView
        android:id="@+id/textView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="这是Dialog的标题"/>

    <TextView
        android:id="@+id/textView3"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_below="@+id/textView2"
        android:text="这是Dialog的内容"
        android:textSize="28sp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView3"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnOk"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="确定"/>

        <Button
            android:id="@+id/btnCancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"/>

    </LinearLayout>
</LinearLayout>

然后对应的,我们创建一个DialogFragment的子类:

public class MyDialogFragment extends DialogFragment implements View.OnClickListener {
    private View rootView;
    private TestActivity mActivity;

    //在Fragment附着在Activity上时,获取要附着的Activity实例。
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mActivity = (TestActivity) activity;
    }

    //创建Fragment 实例
    public static MyDialogFragment newInstance(String arg) {
        MyDialogFragment f = new MyDialogFragment();
        Bundle args = new Bundle();
        args.putString("arg", arg);
        f.setArguments(args);
        return f;
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_my_dialog, container, false);
        initViews();
        return rootView;
    }

    private void initViews() {
        rootView.findViewById(R.id.btnOk).setOnClickListener(this);
        rootView.findViewById(R.id.btnCancel).setOnClickListener(this);
    }

    //监听Dialog消失
    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        Toast.makeText(getActivity(), "Dialog Dismiss", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnOk:
                //通过Activity的实例来调用Dialog消失的方法
                mActivity.dismissDialog();
                Toast.makeText(getActivity(), "Dialog OK", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btnCancel:
                //取消Dialog的显示,使用返回键也可以达到同样的结果
                mActivity.dismissDialog();
                Toast.makeText(getActivity(), "Dialog Cancel", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

最后我们把DialogFragment显示出来。

public class TestActivity extends AppCompatActivity {
    MyDialogFragment mDialogFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        mDialogFragment = MyDialogFragment.newInstance(null);
    }


    public void showDialog(View view) {
        //点击按钮时,显示Dialog
        mDialogFragment.show(getFragmentManager(), "dialog");
    }

    //Dialog 消失
    public void dismissDialog(){
        mDialogFragment.dismiss();
    }
}

下面我们看显示效果: 

如果不想自己去定义界面,也可以在 DialogFragment 里面实现Android自带的AlertDialog效果,我们只需要在重写DialogFragmentonCreateDialog(),然后在里面是实现AlertDialog就OK了。

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("标题");
    builder.setMessage("消息提示!!!");
    builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Toast.makeText(getActivity(),"确定",Toast.LENGTH_SHORT).show();
        }
    });
    builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Toast.makeText(getActivity(),"取消",Toast.LENGTH_SHORT).show();
        }
    });
    return builder.create();
}

效果图如下: 

ListFragment

这个Fragment里面封装了一个ListView,我们只需要是 setListAdapter方法,给ListView设置一个Adapter就OK。

public class MyListFragment extends ListFragment {
    private List<String> mList;

    public MyListFragment() {
        mList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            mList.add("Title = " + i);
        }
    }

    //创建Fragment 实例
    public static MyListFragment newInstance(String arg) {
        MyListFragment f = new MyListFragment();
        Bundle args = new Bundle();
        args.putString("arg", arg);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mList);
        setListAdapter(adapter);
    }
}

效果如下: 
当然如果我们需要行点击事件,可以重写它的onListItemClick 方法即可:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    Toast.makeText(getActivity(), "点击的是第" + position + "项", Toast.LENGTH_SHORT).show();
}

其它还有 PreferenceFragmentWebViewFragment,这里就不再一一介绍了


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值