第一行代码-Fragment碎片

本文介绍了Android中的Fragment(碎片)概念,包括如何静态和动态地添加碎片,重点讲解了静态添加步骤和动态添加的流程。同时,还探讨了在碎片中模拟返回栈以及碎片的生命周期,对于Android应用开发具有实用价值。

1.碎片

碎片(Fragment)是一种可以嵌入在活动中的UI片段。能让程序更加合理和充分的利用大屏幕的空间。

2.碎片的使用方式。

静态的添加碎片

1.创建一个碎片布局例如left_fragment.xml

<?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/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
            android:text="Button"/>

</LinearLayout>

2.新建一个LeftFragment类,让它继承自Fragment。

使用support-v4库中的Fragment,在build.gradle文件中添加support-v4库的依赖。

通过重写Fragment的onCreateView()方法,通过LayoutInflater的inflate()方法将刚才定义的left_fragment布局动态加载进来。

public class LeftFragment extends android.support.v4.app.Fragment {

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

}

3.接下来修改activity_main.xml中的代码。

使用<fragment>标签在布局中添加碎片,通过android:name属性指定要显式添加的碎片类名。

注意要将类的包名加上。

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


动态的添加碎片

1.新键碎片布局。

<?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:background="#ffff00"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is another right fragmet"/>

</LinearLayout>	

2.新建一个碎片类继承自Fragment。

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

3.在活动的布局文件中添加一个layout并指定id,例如添加一个FrameLayout,并且指定id为fragment_container.因为只有一个碎片,不需要任何定位,适合使用默认所有控件都摆在布局左上角的Framelayout。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="com.example.missj.fragment.MainActivity">


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

</LinearLayout>


4.在代码中向FrameLayou添加内容,从而实现动态添加碎片的功能。


public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
        android.support.v4.app.Fragment  fragment = fragmentManager.findFragmentById(R.id.right_layout);
        if(fragment == null)
        {
            fragment = new AnotherRightFragment();
            android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.add(R.id.right_layout,fragment);
            transaction.addToBackStack(null);
            transaction.commit();
        }


    }


}




动态添加碎片主要分5步
(1)创建待添加的碎片实例。
(2)获取FragmentManager,在活动中可以通过调用getSupportFragmentManager()方法得到。
(3)开启一个事务,通过调用beginTransaction()方法开启。
(4)想容器内添加或替换碎片使用add()或者replace()方法实现,需要传入容器的id和待添加碎片的实例。
(5)提交事务,调用commit()方法完成。

fragment事务被用来添加、移除、分离或者替换fragment队列中的fragment。FragmentManager管理着fragment事务回退栈。
FragmentManager.beginTransaction()方法创建并且返回FragmentTransaction的实例。FragementTransaction类使用了名为fluent interface的接口方法。通过该方法配置
FragmentTransaction返回FragmentTransaction类对象,而不是void,由此可以得到一个FragmentTransaction队列。因此transaction代码可以解读为“创建一个新的fragment事务,加入一个添加操作,然后添加该事务。”
add方法是整个事务的核心,含有两个参数:容器视图资源的id和新创建的AnotherRightFragment。容器视图的资源id可以告诉FragmentManager,fragment视图应该出现在activity_main视图中的位置。用作FragmentManager队列中fragment的唯一标识符。
如需中FragmentManager中获取AnotherRightFragment ,使用容器视图资源id。例如
android.support.v4.app.Fragment  fragment = fragmentManager.findFragmentById(R.id.right_layout);
首先使用R.id.right_layout的容器资源视图id,向FragmentManager请求并且获取fragment。如果获取的fragment已存在于队列中,FragmentManager就直接返回它。
如果指定容器视图资源id的fragment不存在,则fragment变量为空值。这时应该新建一个AnotherRightFragment,并启动一个新的fragment事务。将新建fragment添加到
队列中。


在碎片中模拟返回栈

在活动中动态添加碎片,当一个活动有两个碎片,从一个碎片动态切换到另一个碎片后,按Back键程序就会直接退出。如果想模拟返回栈的效果,按下Back键就可以回到上一个碎片,可以通过FragmentTransaction中提供的addToBackStack()方法,可以将一个事务添加到返回栈中,使用程序中的代码。

transaction.addToBackStack(null);
 这样在事务提交之前调用了FragmentTransaction的addToBackStack()方法,可以接收一个名字用于描述返回栈的状态,一般传入null即可。
碎片和活动之间通信

碎片和活动各自存在一个对立的类当中,活动想要调用碎片的方法,FragmentManager提供了findViewById()的方法,专门用于从布局文件中获取碎片的实例。
LeftFragment leftFragment = (LeftFragment)getFragmentManager().findFragmentById(R.id.left_fragment);
调用FragmentManager的findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就能轻松的调用碎片里的方法。
碎片想要调用活动的方法可以通过调用getActivity()放得到当前碎片相关活动实例。
例如
MainActivity activity = (MainActivity)getActivity();
获取了活动的实例之后就可以调用活动里的方法。当碎片需要Context对象时,也可以使用getActivity()方法,因为获取到的活动本身就是一个Context对象。


碎片的生命周期



碎片的生命周期和活动的生命周期较像。


活动在生命周期中有运行、暂停、停止和销毁状态这四种。
碎片的生命周期也有这四种状态。


1. 运行状态
当一个碎片可见的,并且所关联的活动处于运行状态时,该碎片也处于运行状态。
2.暂停状态
活动进入暂停状态(由于另一个没有占满屏幕的活动被添加到了栈顶),与他相关的可见碎片就会进入暂停状态。
3.停止状态
当活动进入停止状态时,与他相关联的碎片就会进入到停止状态。或者通过FragmentTransaction的remove()、replace()方法将碎片从活动中移除,但如果在事务提交之前调用addToBackStack()方法,这时的碎片也会进入到停止状态。进入停止状态的碎片是不可见的,有可能会被系统回收。
4.销毁状态
碎片依附于活动而存在的,当活动销毁时,与他相关联的碎片也就进入到销毁状态。或者通过调用FragmentTransaction的remove()、replace()方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack()方法。这时的碎片也会进入到销毁状态。

Fragment类提供了一系列回调方法,以覆盖碎片生命周期的每一个环节。活动中的回调方法,碎片都有,碎片还提供了一些附加的回调方法。
×onAttach(),当碎片和活动建立关联的时候调用。
×onCreateView(),为碎片创建视图(加载布局)时调用。
*onActivityCreated()确保与碎片相关联的活动一定已经创建完毕的时候调用。
*onDestroyView(),当与碎片相关联的视图被移除的时候调用。
*onDetach() 碎片和活动解除关联的时候调用。

当活动第一次被加载到屏幕上时会依次执行onAttach()、onCreate()、onCreateView()、onActivityCreated()、onStart()和onResume()方法。当碎片被替换时,碎片进入了停止状态,onPause()、onStop()和onDestroyView()方法会依次得到执行。如果在替换的时候没有调用addToBackStack()方法,此时的碎片就会进入到销毁状态,onDestroy()和onDetach()方法会得到执行。
碎片在替换的时候调用了addToBackStack()方法,按下Back键重重新回到运行状态,onActivityCreated()、onStart()和onResume()方法会得到执行。此时onCreate()和onCreateView()方法并不会执行。因为使用了addToBackStack()方法,使得碎片的视图没有销毁。
按下back键退出程序onPause()、onStop()、onDestroyView()、onDestroy()和onDetach()方法会依次得到执行,最终活动和碎片一起被销毁。

碎片也可以通过onSaveInstanceState()方法来保存数据,因为进入停止状态的碎片可能在系统内存不不足的时候被系统回收。保存下来的数据在onCreate()、onCreateView()和onActivityCreated()方法中可以重新得到。

通用的fragment托管布局

新建activity_fragment.xml文件,并且没有特别指定fragment,因此对于任何activity托管fragment,都可以使用这个布局文件。

通用的创建碎片代码


创建碎片,几乎每个Activity都需要同样的一段代码。为了避免重复,可以将这些代码封装为抽象类。创建一个名为SingleFragmentActivity的抽象类。

public abstract class SingleFragmentActivity extends AppCompatActivity {
    public abstract Fragment createFragment();


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        FragmentManager fm = getSupportFragmentManager();
        Fragment  mFragment =  fm.findFragmentById(R.id.fragment_container);
        if(mFragment == null)
        {
            mFragment =createFragment();
            fm.beginTransaction().add(R.id.fragment_container, mFragment)
                        .commit();


        }
    }
}



 从activity_fragment.xml布局里实例化activity视图。然后在容器中查找FragmentManager里的fragment。如果找不到就新建fragment,并将其添加到容器中。
为了实例化新的 fragment,新增了CreateFragment()的抽象方法。SingleFragmentActivity的子类会实现该方法,来返回Activity托管的fragment实例。
使用抽象类例如如下的代码:

public class CrimeListActivity extends SingleFragmentActivity {
    @Override
    public Fragment createFragment() {
        return new CrimeListFragment();
    }
}


 其中onCreate方法会继承父类的,所以系统调用CrimeListActivity 的onCreate()方法会调用其父类的onCreate()方法。


使用fragment argument

每个fragment可以附带一个Bundle对象,该Bundle对象包含有键值对,可以像附加extra到Activity的intent中那样使用它们。一个键值对即一个argument。
要创建fragment argument,首先需要创建Bundle对象。然后使用Bundle限定类型的“put”方法(类似于Intent的方法),将argument添加到bundle中(如下代码)。

        Bundle args = new Bundle();
        args.putSerializable(EXTRA_MY_OBJECT, myObject);
        args.putInt(EXTRA_MY_INT, myInt);
        args.putCharSequence(EXTRA_MY_STRING, myString);
        args.putString(EXTRA_MY_STRING, myString);

要附加argument给fragment,需要调用Fragment.setArguments(Bundle)方法,而且,必须在fragment创建后,添加给Activity之前完成。

Android开发者采取的做法是:添加名为newInstance()的静态方法给Fragment类。使用该方法完成fragment实例及Bundle对象的创建,然后将
argument放入bundle中,最后再附加给fragment。
托管Activity需要fragment实例时,转而调用newInstance()方法,而非直接调用其构造方法。而且为满足fragment创建argument的要求,activity可传入
任何需要的参数给newInstance()方法。
例如在CrimeFragment类中,编写可以接受UUID参数的newInstance(UUID)方法。

public class CrimeFragment extends Fragment {
  
  public  static final  String ARG_CRIME_ID = "crime_id";
    public   static  CrimeFragment newInstance(UUID crimeId)
    {


        Bundle arg = new Bundle();
        arg.putSerializable(ARG_CRIME_ID, crimeId);
        CrimeFragment crimeFragment = new CrimeFragment();
        crimeFragment.setArguments(arg);
        return  crimeFragment;
    }
....
}



需创建CrimeFragment时,CrimeActivity应调用CrimeFragment.newInstance(UUID)方法,并传入从它的extra中获取的UUID参数值。回到CrimeActivity类中,在CreateFragment()方法里,从CrimeActivity的Intent中获取extra数据,并传入CrimeFragment.newInstance(UUID)方法。例如:
public class CrimeFragment extends SingleFragmentActivity{
    public  static  final  String EXTRA_CRIME_ID = "com.example.missj.CrimelIntent.crime_Id";
    @Override
    public Fragment createFragment() {
        UUID crimeId = (UUID) getIntent().getSerializableExtra(EXTRA_CRIME_ID);
        return  new CrimeFragment().newInstance(crimeId);
      //  return new CrimeFragment();
    }
}



fragment需要获取它的argument时,先调用Fragment类的getArguments()方法,再调用Bundle的限定类型“get”方法。如getSerializable()方法。
现在在CrimeFragment.onCreate()方法中,改为从fragment的argument中获取UUID。

public class CrimeFragment extends Fragment {
  public  static final  String ARG_CRIME_ID = "crime_id";

  public   static  CrimeFragment newInstance(UUID crimeId)
    {


        Bundle arg = new Bundle();
        arg.putSerializable(ARG_CRIME_ID, crimeId);
        CrimeFragment crimeFragment = new CrimeFragment();
        crimeFragment.setArguments(arg);
        return  crimeFragment;
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        UUID crimeId= (UUID)getArguments().getSerializable(ARG_CRIME_ID);
        mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
        returnResult(crimeId);
        setHasOptionsMenu(true);
    }
...
}


从fragment中获取返回结果


如果碎片想要从已经启动的Activity中获取返回结果,不调用Activity的startActivityForResult()方法,转而调用Fragment.startActivityForResult()方法;不覆盖Activity.onActivityResult()方法,转而覆盖Fragment.onActivityResult()方法。

public class CrimeListFragment extends Fragment {


    private static  final  int REQUEST_CRIME =1 ;

 
    public  class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener
    {
        
        @Override
        public void onClick(View view) {
        Intent intent = CrimeActivity.newIntent(getActivity(), mCrime.getId())  ;
           startActivityForResult(newIntent(getActivity(), mCrime.getId()), REQUEST_CRIME);
            
        }


       
    }


    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        //super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == REQUEST_CRIME)
        {
  //Handle result
        }
    }
}



其中CrimeActivity的自定义newIntent()方法如下,这是一种Android的通用写法。
    public  static Intent newIntent(Context packageContext,UUID crimeID)
    {
        Intent intent = new Intent(packageContext, CrimePagerActivity.class);
        intent.putExtra(EXTRA_CRIME_ID, crimeID);
        return intent;
    }


除将返回结果从托管Activity传递给Fragment的额外实现代码外,Fragment.startActivityForResult(Intent, int )方法类似于Activity的同名方法。
从fragment的返回结果与获取处理不同,Fragment能够从Activity中接收返回结果,但自身无法持有返回结果,只有Activity拥有返回结果。因此尽管Fragment有自己
的startActivityForResult(..)和onActivityResult(...)方法,但却没有setResult(..)方法。
所以通过托管Activity返回结果值,在Fragment中添加代码实现如下:

public class CrimeFragment extends Fragment {
 
    public  void returnResult(UUID id)
    {
       // Intent intent = new Intent(getActivity(), CrimeListActivity.class);
        //intent.putExtra(ITEM_POS, 888);
        getActivity().setResult(Activity.RESULT_OK, null);
    }
}



















评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值