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

被折叠的 条评论
为什么被折叠?



