Android碎片(Fragment)的小秘密

      碎片(Fragment)是一种可以嵌入在活动中的UI片段。Android3.0开始引入的碎片(Fragment),其最主要的目的是为了给大屏幕手持设备提供更加灵活和动态的UI设计,在平板应用方向运用非常广泛。

      碎片(Fragment)和活动(Activity)是有着紧密的联系。虽然各自有自己的布局和生命周期,关键是碎片要嵌入到活动中才能更好的展现自己呀!所以他们还是要发生关系的(偷笑.........)。碎片和活动之间的关系主要发生在生命周期的联系和相互之间的通风报信(安静)。

      说实话,我们和碎片相依为命却互不相识(惊讶)。



      作为帅比的程序员,任之所责正式开始。一阴一阳之谓道也,一静一动之未睡也(呃呃呃....不好意思打错字了,碎也、碎也、碎也)。碎片的静态和动态使用方式。

静态召唤碎片:

思路是,第一步新建两个布局,代码如下:

<?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="#00f">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="静态碎片"/>

</LinearLayout>
<?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="#00fff0">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="静态碎片"
        android:id="@+id/textView" />
</LinearLayout>
第二步新建两个类用于加载布局:建议使用V4的碎片
public class LeftFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
                              ViewGroup container,
                              Bundle savedInstanceState) {
        View view=inflater.inflate(
                R.layout.layout_left_fragment,container,false);
        return view;
    }
}
public class RightFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
                              ViewGroup container,
                              Bundle savedInstanceState) {
        View view=inflater.inflate(
                R.layout.layout_right_fragment,container,false);
        return view;
    }
}
第三步是在主布局中静态使用碎片,要注意布局权重的问题和name记得加上包名。
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="com.example.dell.myfragment.MainActivity">

    <fragment
        android:id="@+id/left_fragment"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.LeftFragment"/>

    <fragment
        android:id="@+id/right_fragment"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.RightFragment"/>
</LinearLayout>
平板效果图:


来看下手机效果图:


动态召唤碎片:静态使用碎片是在主布局中添加的碎片,那么动态使用碎片则是在活动中(java.xml)添加碎片。步骤:

1.创建待添加碎片实例。

2.获取FragmentManager,在活动中可以直接调用getSupportFragmentManager()方法得到。

3.开启一个事务,通过调用beginTransaction方法来开启。

4.向容器内添加或替换碎片,一般使用replace()方法实现,需要传入容器的id和待添加的碎片实例。

5.提交事务,调用commit()方法来完成。

       事务重要的方法:主要的操作都是FragmentTransaction的方法

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

transaction.add() 

往Activity中添加一个Fragment

transaction.remove()

从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。

transaction.replace()

使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

transaction.hide()

隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

transaction.show()

显示之前隐藏的Fragment

detach()

会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach()

重建view视图,附加到UI上并显示。

transatcion.commit()//提交一个事务

代码如下:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="com.example.dell.myfragment.MainActivity">

    <fragment
        android:id="@+id/left_fragment"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.LeftFragment"/>

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

    </FrameLayout>
</LinearLayout>
public class MainActivity extends AppCompatActivity
        implements View.OnClickListener{
    private Button rightBotton;
    private Button topBotton;
    private Button downBotton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initLayout();
    }

    public void initLayout(){
        rightBotton=(Button) findViewById(R.id.button1);
        topBotton=(Button) findViewById(R.id.button2);
        downBotton=(Button) findViewById(R.id.button3);
        rightBotton.setOnClickListener(this);
        topBotton.setOnClickListener(this);
        downBotton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button1:
                FragmentManager fragmentManager=
                        getSupportFragmentManager();
                FragmentTransaction transaction=
                        fragmentManager.beginTransaction();
                RightFragment rightFragment=new RightFragment();
                transaction.add(R.id.move_fragment,rightFragment);
                transaction.commit();
                break;
            case R.id.button2:
                FragmentManager fragmentManager1=
                        getSupportFragmentManager();
                FragmentTransaction transaction1=
                        fragmentManager1.beginTransaction();
                TopFragment topFragment=new TopFragment();
                transaction1.add(R.id.move_fragment,topFragment);
                transaction1.commit();
                break;
            case R.id.button3:
                FragmentManager fragmentManager2=
                        getSupportFragmentManager();
                FragmentTransaction transaction2=
                        fragmentManager2.beginTransaction();
                DownFragment downFragment=new DownFragment();
                transaction2.add(R.id.move_fragment,downFragment);
                transaction2.commit();
                break;
        }
    }
}
    布局的话,这里只贴出了主布局,改变了的主布局,大伙看得出来,博主有新增了两个布局,和静态的写法一样,不啰嗦。值得注意的是,这里我们使用的add()这个方法来动态添加碎片,其实也可以使用replace()方法来实现。咱们需要注意FragmentTransaction中add和replace方法的区别。
  1. add()方法是把Fragment添加到ViewGroup中,通过该方法可以添加多个Fragment。第一个参数是容器ViewGroup,第二个是要添加的fragment。
  2. replace()方法会把ViewGroup中的原有Fragment先remove掉然后再add新Fragment。

使用add方法可以避免再次创建Fragment实例,一般情况是建议使用add方法,并配合FragmentTransaction的show和hide方法来使用。



手机效果图:


大家发现了吧!这平板电脑的效果图倒好看了,手机效果图看着有点别扭呀!咋办呢?只需改下主布局就好了,请看代码:

activity_main.xml
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="com.example.dell.myfragment.MainActivity">

    <FrameLayout
        android:id="@+id/move_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <fragment
        android:id="@+id/left_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.LeftFragment"/>
        
    </FrameLayout>

</LinearLayout>

效果图上:


       怎么样!还行吧!点按钮动态碎片1之后,就跳到了另外一个碎片中去了,是不是有点像活动的页面跳转,别说,还真像。值得称赞的是,在动态碎片1碎片中又可以退回来继续点击动态碎片2、3按钮到各个碎片中去。不瞒大家说,除了改掉上面的主布局外,还稍微改了一些地方,改变的地方一样,所以只贴出一部分,代码如下:

MainActivity.java
case R.id.button1:
                FragmentManager fragmentManager=
                        getSupportFragmentManager();
                FragmentTransaction transaction=
                        fragmentManager.beginTransaction();
                RightFragment rightFragment=new RightFragment();
//                transaction.add(R.id.move_fragment,rightFragment);
                transaction.replace(R.id.move_fragment,rightFragment);
                transaction.addToBackStack(null);
                transaction.commit();
                break;

至于为什么把add()方法换成了replace()方法,他们各有千秋呀,大伙回头上去再看下,上面有详细介绍。细心的朋友回发现,

怎么多出了一行代码transaction.addToBackStack(null);

哈哈哈哈!被发现了,既然这样,只好老实交代咯!在调用commit()之前,可以用addToBackStack()把事务添加到一个后退栈中,这个后退栈属于所在的activity。有了它,就可以在用户按下返回键时,返回到fragment执行事务最初的地方,演示了如何用一个fragment代替另一个fragment,同时在后退栈中保存被代替的fragment的状态。简单的说,这就是在碎片中模拟还回栈。

        不要高兴的太早,问题来了,既然现在可以做到让平板电脑上的碎片美美的展现,也能让手机上的碎片风骚起来,关键是,在平板上,我们写的是一样代码,在手机上运行的碎片又是另一样代码(还需要改布局),其实还有呀,平板电脑和手机的屏幕那是有大小区别呀!平板上双页模式那叫美,手机上单页模式那叫骚。那咱们能不能只写一次代码,既能根据屏幕的大小尺寸来决定到底要双页模式还是要单页模式呢?

        巧了,特么还真有这个办法。使用限定符(Qualifiers)来实现。以动态添加碎片为例,静态的太简单(大家要学会举一反三,已是老生常谈)两个步骤:

1.主布局不变,上面刚刚改过的那个,activity_main.xml。  额,还是贴出来吧!

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="com.example.dell.myfragment.MainActivity">

    <FrameLayout
        android:id="@+id/move_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <fragment
        android:id="@+id/left_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.LeftFragment"/>

    </FrameLayout>

</LinearLayout>

2.第二步(重点),在res目录下新建layout_large文件夹,在这个文件夹中新建一个布局,也叫做 activity_main.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="horizontal">

    <fragment
        android:id="@+id/left_fragment"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:name="com.example.dell.myfragment.LeftFragment"/>

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

    </FrameLayout>

</LinearLayout>

        代码中是不是发现了什么?是不是没看到large在那,large有咋作用呀,等等,小伙子别急,layout/activity_main布局中只包含一个碎片,即单页模式;layout_large/activity_main布局是有权重设置的,就是平分屏幕(屏幕太大),即双页模式。其中large就是一个限定符。那些屏幕被认为是large的设备就会自动加载layout_large文件夹下的布局,而小屏幕的设备则还是会加载layout文件下的布局。还有一种限定符,是小宽度限定符哟!一样的,很简单,在res目录下新建文件夹,然后在这个文件夹下新建一个layout_sw600dp布局。这意味着,当程序运行在屏幕宽度大于600dp的设备上时,会加载layout_sw600dp/activity_main布局,当程序运行在屏幕宽度小于600dp的设备上时,则仍然加载默认的layout/activity_main布局。效果图就不贴出来了,平板的是双页模式,手机是单页模式,上面有图。

/**
 * 是时候说Android碎片的小秘密了(他们是有点关系,只是不是很亲密)
 * Android碎片的小秘密其实是告诉我们,如何在活动中调用碎片的方法,
 * 或者在碎片中调用活动里方法。
 * 为了解决碎片和活动之间的通信,FragmentManager提供了一个类似于
 * findViewById()的方法,专门用于从布局中获取碎片的实例,代码如下:
 */
RightFragment rightFragment=(RightFragment)
        getSupportFragmentManager()
                .findFragmentById(R.id.left_fragment);
//调用FragmentManagerfindFragmentById方法,
// 可以在活动中得到相应碎片的实例,
// 然后就可以轻松的调用碎片里的方法。
/**
 * 碎片中都可以调用getActivity()方法来得到和当前相关联的活动实例。
 */
MainActivity activity=(MainActivity) getActivity();

碎片的生命周期(我的小心脏哟):

      碎片的生命周期和活动的生命周期很像。都有四种状态,运行状态、暂停状体、停止状态、销毁状态。

fragments的大部分状态都和activitie很相似,但fragment有一些新的状态。

  • onAttached() —— 当fragment和activity关联之后,调用这个方法。
  • onCreateView() —— 创建fragment中的视图的时候,调用这个方法。
  • onActivityCreated() —— 当activity的onCreate()方法被返回之后,调用这个方法。
  • onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
  • onDetach() —— 当fragment和activity分离的时候,调用这个方法。
来图片直观一点:

Activity和Fragment生命周期对比图如下:



最后一张王牌:


解析:

1.当一个fragment被创建的时候,它会经历以下状态.。

  • onAttach()
  • onCreate()
  • onCreateView()
  • onActivityCreated()

2.当这个fragment对用户可见的时候,它会经历以下状态。

  • onStart()
  • onResume()

3.当这个fragment进入“后台模式”的时候,它会经历以下状态。

  • onPause()
  • onStop()

4.当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。

  • onPause()
  • onStop()
  • onDestroyView()
  • onDetach()

5.就像activitie一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。

  • onCreate()
  • onCreateView()
  • onActivityCreated()
松口气吧!
感觉还没说完,碎片还生了几个娃娃(派生类),了解下吧!碎片的这些派生类跟其他的控件啊组件啊有一腿,比如ListFragment和ViewPager:
通常我们会继承系统内置的fragment的三个派生类:

DialogFragment

对话框式的Fragments。可以把fragmentdialog并入到activity的返回栈中,使用户能再返回到这个对话框。

ListFragment

显示一个列表控件,就像ListActivity类,它提供了很多管理列表的方法,onListItemClick和setListAdapter等

PreferenceFragment

显示一个Preference对象组成的列表,类似PreferenceActivity,主要用来创建设置界面




猜你喜欢

转载自blog.csdn.net/l_201607/article/details/70570864