Chapter 12. Fragment

2018-10-12  本文已影响16人  GeekGray

阅读原文

12.1 Fragment简介

是从Android3.0开始新增的概念, 意为碎片。为了更好的运用越来越大的屏幕空间而生,目前已经成为流行的开发方式。
Fragment是一种可以嵌入在Activity当中的UI片段.用来组建Activity界面的局部模块, 也可以说一个Actiivty界面可以由多个Fragment组成;其行为与Activity很相似, 有自己对应的布局(包含具体的View), 它有自己的生命周期,接收自己的输入事件,并且可以从运行中的activity中添加或移除.

一个fragment必须总是嵌入在一个activity中,同时fragment的生命周期受activity的影响;本质上会产生一个FrameLayout,它加载的布局为其子布局

Fragment需要包含在Activity中,一个Activity里面可以包含一个或者多个Fragment,而且一个Activity可以同时展示多个Fragment。同时,Fragment也具有自己的布局。


12.2 相关API

Fragment

View onCreateView()

onActivityCreated()//当Activity创建好

getActivity()//宿主

setArguments(), getArgument()

ListFragment

setListAdapter(adapter)

onListItemClick()

getFragmentManager()

FragmentActivity

getSupportFragmentManager()

FragmentManager

beginTransaction()

FragmentTransaction

add(), replace(), remove()

show() <-->hide()

commit()

addToBackStack()

12. 3 使用

Fragment的静态加载

1. 定义Fragment的子类, 并在onCreateView()中加载一个布局文件

2. 在布局文件中通过<fragment>指定自定义Fragment

3. 我们的Activity必须继承于FragmentActivity

Fragment的动态加载

1. 定义Fragment的子类, 并在onCreateView()中加载一个布局文件

2. 我们的Activity必须继承于FragmentActivity

3. 在代码中, 动态创建Fragment对象, 并添加到指定的ViewGroup中

    add,replace,remove,hide,show

12.4 Fragment的生命周期

image

12.4.1 生命周期状态

12.4.2 生命周期方法


12.5 创建Fragment

与创建Activity类似,要创建一个Fragment必须创建一个类继承自Fragment。需要注意的是,Android系统提供了两个Fragment类,分别是android.app.Fragment和android.support.v4.app.Fragment。继承android.app.Fragment类则程序只能兼容Android4.0上的系统,继承android.support.v4.app.Fragment类可以兼容低版本的Android系统。

12.5.1 创建Fragment

重写Fragment的onCreateView()方法,并在该方法中调用了LayoutInflater的inflate()方法将Fragment布局动态加载进来

public class NewsListFragment extends Fragment
{
    @override 
    public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstaceState)
    {
        View v=inflater.inflate(R.layout.fragment,container,false);
        return v;
    }
}

12.5.2 添加Fragment

Fragment创建完成不能单独使用,还需要将Fragment添加到Activity中。在Activity中添加Fragment有两种方式,一种是直接在布局文件中添加,将Fragment作为整个Activity布局的一部分;另一种是当程序运行时,动态地将Fragment添加到Activity中。

布局文件中添加Fragment时,可以使用<fragment></fragment>标签,该标签与其他控件的标签类似,但必须要指定android:name属性,其属性值为Fragment的全路径名称。布局文件fragment.xml中的代码如下所示:

...

<fragment
    android:name="cn.itcast.fragment.NewsListFragment"
    android:id="@+id/newslist"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

....

当Activity运行时,也可以将Fragment动态加载到Activity的布局中,这种添加方式首先要获取Fragment的实例对象,然后获取FragmentManager对象,调用FragmentManager的beginTransaction()方法开启事务并得到FragmentTransaction对象,最后调用FragmentTransaction的add()方法将Fragment添加到Activity,并通过commit()方法提交事务。动态添加Fragment的示例代码如下:

public class MainActivity extends Activity
{
    @SuppressLInt("NewApi")
    @override
    protected void onCreate(BUndle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化Fragment对象
        NewsListFragment fragment=new NewsListFragment();
        //获取FragmentManager实例
        FragmentTranscation beginTransaction=fm.beginTransaction();
        //添加一个Fragment
        beginTransaction.add(R.id.ll,fragment);
        //提交
        beginTransaction.commit();
    }
}

需要注意的是FragmentTransaction的add(int containerViewId,Fragment fragment)方法,它的第一个参数表示Fragment要放入ViewGroup的资源id,第二个参数是要添加的Fragment。例如,上述代码中的R.id.ll是一个线性布局,那么beginTransaction.add(R.id.ll,fragment);的意思就是将Fragment添加到该线性布局中。

12.5.3 Fragment与Activity间通信

虽然Fragment是嵌套在Activity中显示的,但是Fragment和Activity都是各自存在于一个独立的类中,它们之间并没有明显的方式来直接进行通信。在实际开发中,经常会遇到需要在Activity中获取Fragment实例和FragmentActivity实例的情况。

1. 在Activity中获取Fragment实例

FragmentManager提供了一个findFragmentById()方法,该方法是专门用于从布局文件中获取Fragment实例的。

findFragmentById()方法有一个参数,该参数代表Fragment在Activity布局中的id。例如在Activity的布局中添加了SettingListFragment,并在布局中指定SettingListFragment的id为R.id.settingcontent,这时就可以使用getFragmentManager().findFragmentById(R.id.settingcontent);得到SettingListFragment实例。具体代码如下所示:

SettingListFragment fListFragemnt=(SettingListFragment)getFragmentManager().findFragmentById(R.id.settingcontent);

2. 在Fragment中获取Activity实例

在Fragment中调用getActivity()方法可以获取到当前Fragment相关联的Activity实例对象。例如,在MainAcivity中添加了SettingListFragment,那么在SettingListFragment中可以调用getActivity()方法得到MainActiviy实例,具体代码如下所示:

MainActivity activtiy=(MainActivity)getActivity();

获取到Activity实例之后,在Fragment中就可以通过该实例调用Activity中的方法了。另外,当Fragment需要Context对象时,也可以使用该方法。

掌握了Fragment与Activity间的通信后,Fragment与Fragment间的通信就变得简单了。Fragment之间的通信,首先需要在Fragment中获取Activity的实例对象,然后通过Activity的实例获取另一个Fragment的实例,这样就可以实现Fragment与Fragment的通信了。


12.6 案例-----这设置界面

1. activity_main.xml 布局

在activity_main.xml布局文件中添加两个FrameLayout,这两个FrameLayout将会被相应的Fragment替换掉。

<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="horizontal"
    tools:context=".MainActivity" >

    <FrameLayout
        android:id="@+id/settinglist"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" >
    </FrameLayout>

    <FrameLayout
        android:id="@+id/settingcontent"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" >
    </FrameLayout>

</LinearLayout>

2. 创建两个Fragment布局文件

由于需要实现一个Activity中展现两个Fragment,因此需要创建相应的Fragment的布局。用来展示设置图标的布局fragment_icons.xml如下所示:

<?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="vertical" >

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

</LinearLayout>

接下来创建一个用于展示内容的布局fragment_settinglist.xml,该布局的图形化视图

<?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:background="@drawable/bg_welcome"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="10dp"
        android:background="@android:color/white" >

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:textSize="18sp" />

        <ImageView
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:background="@drawable/arrow_group_right" />
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:background="@android:color/white" >

        <TextView
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:textSize="18sp" />

        <ImageView
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:background="@drawable/arrow_group_right" />
    </RelativeLayout>

</LinearLayout>

3. 创建ListView布局

<?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="wrap_content" >

    <ImageView
        android:id="@+id/settingicon_imgv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_margin="10dp" />

</RelativeLayout>

4. 创建两个Fragment

SettingListFragment的代码如下:

@SuppressLint("NewApi")
public class SettingListFragment extends Fragment
{

    private View view;
    private TextView mTextView1;
    private TextView mTextView2;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        // 将布局文件解析出来
        view = inflater.inflate(R.layout.fragment_settinglist, container, false);
        if (view != null)
        { // 如果view不为空
            initView();
        }
        setText(((MainActivity) getActivity()).getSettingText()[0]);
        return view;
    }

    public void initView()
    {
        mTextView1 = (TextView) view.findViewById(R.id.tv);
        mTextView2 = (TextView) view.findViewById(R.id.tv1);
    }

    public void setText(String[] text)
    {
        mTextView1.setText(text[0]);
        mTextView2.setText(text[1]);
    }
}

还需要定义一个Fragment用来展示设置图标,在这里创建一个SettingiconFragment继承自Fragment,由于需要在该类中实现用ListView展示设置图片,并且点击图标时还需要改变SettingListFragment中的文字,因此需要用到Fragment与Fragment的通信。使用getActivity()获取Activity的实例对象,并通过该实例对象获取到FragmentManager,然后调用findFragmentById()方法获取到相应的Fragment对象,从而实现了Fragment与Fragment间的通信

@SuppressLint("NewApi")
public class SettingiconFragment extends Fragment
{

    private View view;
    private int[] settingicon;
    private String[][] settingText;
    private ListView mListView;

    @SuppressLint("NewApi")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        // 解析布局
        view = inflater.inflate(R.layout.fragment_settingicon, container, false);
        // 获取Acitivty实例对象
        MainActivity activity = (MainActivity) getActivity();
        // 获取Activity中的图标数组
        settingicon = activity.getIcons();
        // 获取Activity中的设置文字数组
        settingText = activity.getSettingText();
        if (view != null)
        { // 如果view不为空
            initView();
        }
        // 为ListView设置条目监听
        mListView.setOnItemClickListener(new OnItemClickListener()
        {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
            {
                // 通过Activity实例获取另一个Fragment实例
                SettingListFragment listFragment = (SettingListFragment) ((MainActivity) getActivity())
                        .getFragmentManager().findFragmentById(R.id.settingcontent);
                // 设置其他Fragment的文字
                listFragment.setText(settingText[position]);

            }
        });
        return view;
    }

    // 初始化控件的方法
    private void initView()
    {
        mListView = (ListView) view.findViewById(R.id.settingicon);
        if (settingicon != null)
        {
            mListView.setAdapter(new MyAdapter());
        }

    }

    // 适配器
    class MyAdapter extends BaseAdapter
    {

        @Override
        public int getCount()
        {
            return settingicon.length;
        }

        @Override
        public Object getItem(int position)
        {
            // TODO Auto-generated method stub
            return settingicon[position];
        }

        @Override
        public long getItemId(int position)
        {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            convertView = View.inflate(getActivity(), R.layout.item_list, null);
            ImageView mNameTV = (ImageView) convertView.findViewById(R.id.settingicon_imgv);
            mNameTV.setBackgroundResource(settingicon[position]);
            return convertView;
        }

    }
}

5. MainActivity

public class MainActivity extends Activity
{

    private FragmentTransaction beginTransaction;
    // 设置文字
    private String[][] settingText = { { "主题", "系统壁纸" }, { "云账户", "百度云账户" }, { "通知", "通知栏推送" }, { "移动数据", "便携式WIFI热点" },
            { "WLAN", "更多" }, { "蓝牙", "可被发现" }, { "天气", "温度" }, { "通话音量", "媒体音量" }, { "密码锁定", "定位服务" },
            { "语言", "输入法设置" }, { "设置快捷手势", "触摸反馈" }, { "设备名称", "存储" } };
    // 设置图标
    private int[] settingicons = { R.drawable.theme, R.drawable.clound, R.drawable.notifycation, R.drawable.internet,
            R.drawable.wifi, R.drawable.bluetooth, R.drawable.wether, R.drawable.volume, R.drawable.gps,
            R.drawable.language, R.drawable.gesture, R.drawable.info };

    // 获取图标数组的方法
    public int[] getIcons()
    {
        return settingicons;
    }

    // 获取设置文字的方法
    public String[][] getSettingText()
    {
        return settingText;
    }

    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建Fragment
        SettingListFragment fragment = new SettingListFragment();
        // 创建Fragment
        SettingiconFragment icFragment = new SettingiconFragment();
        // 获取事务
        beginTransaction = getFragmentManager().beginTransaction();
        // 添加Fragment
        beginTransaction.replace(R.id.settingcontent, fragment);
        beginTransaction.replace(R.id.settinglist, icFragment);
        // 提交事务
        beginTransaction.commit();
    }
}

6. 小技术:如何让选择项变色

android:background="?android:attr/activatedBackgroundIndicator"

//设置ListView为单选模式
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE)

//设置默认选中第一个
getListView().setItemChecked(0, true)
上一篇 下一篇

猜你喜欢

热点阅读