Fragment产生的原因是为了使一个App同时适应手机和平板的屏幕。
Fragment的生命周期:
因为Fragment不能脱离Activity而工作,所以它知识比Activity多了一些生命周期方法,多的方法介绍:
onAttach():Fragment和Activity关联时调用
onCreateView():创建Fragment视图
onActivityCreated():Activity创建完成时调用
onDestroyView():与onCreateView对应,销毁Fragment视图
onDetach():Fragment和Activity解除关联时调用
其他的方法介绍参考Activity的生命周期。
静态使用Fragment:其实就是把Fragment当成普通的空间,在xml文件中直接使用。
由于静态使用方式很简单,所以直接给出一个例子:
fragment_title.xml:
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="60dp"
android:background="?attr/colorPrimary" >
<ImageButton
android:id="@+id/id_title_left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="3dp"
android:background="@android:drawable/ic_btn_speak_now" />
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="TitleFragment"
android:textColor="#fff"
android:textSize="20sp"
android:textStyle="bold" />
RelativeLayout>
TitleFragment.java:
package com.egotrip.wcc.fragment;
/**
* Created by wcc on 2015/12/3.
*/
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import com.egotrip.wcc.R;
import com.egotrip.wcc.utils.ToastUtils;
public class TitleFragment extends Fragment {
private ImageButton menu;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_title, container, false);
return
view
;
}
}
fragment_content.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" >
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="ContentFragment"
android:textSize="20sp"
android:textStyle="bold" />
LinearLayout>
ContentFragment2.java:
package com.egotrip.wcc.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.egotrip.wcc.R;
/**
* Created by wcc on 2015/12/3.
*/
public class ContentFragment2 extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_content, container, false);
}
}
Activity布局文件activity_fragment_demo:
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">
<fragment
android:id="@+id/title"
android:name="com.egotrip.wcc.fragment.TitleFragment"
android:layout_width="match_parent"
android:layout_height="60dp" />
<fragment
android:id="@+id/content"
android:name="com.egotrip.wcc.fragment.ContentFragment2"
android:layout_width="match_parent"
android:layout_height="match_parent" />
LinearLayout>
注意:fragment标签必须添加id属性,否则会报错。
FragmentDemoActivity:
package com.egotrip.wcc.ui;
import android.app.Activity;
import android.os.Bundle;
import com.egotrip.wcc.R;
/**
* Created by wcc on 2015/12/3.
*/
public class FragmentDemoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_demo);
}
}
效果图:
动态使用Fragment:在代码里动态添加Fragment。
为了演示动态使用Fragment的效果,先修改一下Activity的布局:
<RelativeLayout 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" >
<fragment
android:id="@+id/id_fragment_title"
android:name="com.egotrip.wcc.fragment.TitleFragment"
android:layout_width="match_parent"
android:layout_height="45dp" />
<include
android:id="@+id/id_ly_bottombar"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
layout="@layout/content_bottom_bar" />
<FrameLayout
android:id="@+id/id_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/id_ly_bottombar"
android:layout_below="@id/id_fragment_title" />
RelativeLayout>
TitleFragment还是使用静态使用方式,但新增了一个FrameLayout替代了原来的ContentFragment,为了实现Fragment的动态的添加,替换等操作,同时在页面底部添加了一个bottombar,它包含四个ImageButton,通过点击不同的ImageButton实现不同Fragment的转换。具体的bottombar的布局:
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="60dp"
android:background="#43000000"
android:orientation="horizontal">
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="save"
android:src="@drawable/save" />
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="back"
android:src="@drawable/back" />
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="pause"
android:src="@drawable/pause" />
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="forward"
android:src="@drawable/forward" />
LinearLayout>
下面是Activity的代码:
package com.egotrip.wcc.ui;
/**
* Created by wcc on 2015/12/3.
*/
import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import com.egotrip.wcc.R;
import com.egotrip.wcc.fragment.BackFragment;
import com.egotrip.wcc.fragment.SaveFragment;
public class FragmentActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_fragment);
setDefaultFragment();
}
private void setDefaultFragment() {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.id_content,new SaveFragment()).commit();
}
public void save(View v)
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.id_content,new SaveFragment()).commit();
}
public void back(View v)
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.id_content,new BackFragment()).commit();
}
}
上面的代码中有一个不好的地方,就是实现点击事件的方式导致重复多次开启事务,通过实现OnClickListener接口的方式可能更好。
由于Fragment是Android3.0版本新添加的,所以3.0以下版本要使用它,必须引入android.support.v4包,同时Activity要继承FragmentActivity,通过getSupportFragmentManager()方法获取FragmentManager。
同时,上面代码中的SaveFragment和BackFragment的实现方式和ContentFragment2的实现方式类似,这里就不贴代码了。
效果图:
下面主要介绍一下Fragment常用的三个类:
Fragment:用于创建Fragment实例
FragmentManager:用于管理Fragment
FragmentTransaction:保证一系列Fragment操作的原子性
获取FragmentManager的方式:getFragmentManager()(3.0以下用v4包中的getSupportFragmentManager())
FragmentManager下的方法:findFragmentById()和findFragmentByTag()
FragmentTransaction下的方法介绍:
add():给Activity添加一个Fragment
remove():从Activity中移除一个Fragment,如果没有添加到BackStack中,会被直接销毁
replace():用新的Fragment替代旧的,相当于remove()+add()
hide():隐藏当前的Fragment,但并不销毁
show():显示之前隐藏的Fragment
detach():解除Fragment和Activity的关联,但Fragment依然由FragmentManager维护
attach():重新建立Fragment和Activity的关联
下面说一下这些方法的用法、回退栈的概念。下面展示一个例子,里面用到了上面的一些方法,具体等贴完代码再做解释,同时例子中也引入了回退栈。
fragment_one.xml:
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">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:text="fragment one" />
<Button
android:id="@+id/btn_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="fragment one" />
RelativeLayout>
OneFragment.java:
package com.egotrip.wcc.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.egotrip.wcc.R;
/**
* Created by wcc on 2015/12/3.
*/
public class OneFragment extends Fragment implements View.OnClickListener {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_one, container, false);
((Button) v.findViewById(R.id.btn_one)).setOnClickListener(this);
return v;
}
@Override
public void onClick(View v) {
if(getActivity() instanceof OnFOneClickListener)
((OnFOneClickListener) getActivity()).OnFOneClick();
}
public interface OnFOneClickListener{
public void OnFOneClick();
}
}
fragment_two.xml:
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">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:text="fragment two" />
<Button
android:id="@+id/btn_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="fragment two" />
RelativeLayout>
TwoFragment.java:
package com.egotrip.wcc.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.egotrip.wcc.R;
/**
* Created by wcc on 2015/12/3.
*/
public class TwoFragment extends Fragment implements View.OnClickListener {
private OnFTwoClickListener listener;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_two, container, false);
((Button) v.findViewById(R.id.btn_two)).setOnClickListener(this);
return v;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
public interface OnFTwoClickListener{
public void OnFTwoClick();
}
public void setOnFTwoClickListener(OnFTwoClickListener listener)
{
this.listener = listener;
}
@Override
public void onClick(View v) {
if(null != listener)
listener.OnFTwoClick();
}
}
fragment_three.xml:
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">
<Button
android:id="@+id/btn_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="fragmentthree" />
RelativeLayout>
ThreeFragment.java:
package com.egotrip.wcc.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.egotrip.wcc.R;
/**
* Created by wcc on 2015/12/3.
*/
public class ThreeFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_three, container, false);
}
}
下面是Activity的代码,FragmentStackActivity.java:
package com.egotrip.wcc.ui;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;
import com.egotrip.wcc.R;
import com.egotrip.wcc.fragment.OneFragment;
import com.egotrip.wcc.fragment.ThreeFragment;
import com.egotrip.wcc.fragment.TwoFragment;
public class FragmentStackActivity extends Activity implements OneFragment.OnFOneClickListener,TwoFragment.OnFTwoClickListener{
private OneFragment oneFragment;
private TwoFragment twoFragment;
private ThreeFragment threeFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_stack);
// if(savedInstanceState == null)
// {
oneFragment = new OneFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.content, oneFragment, "ONE").commit();
// }
}
@Override
public void OnFOneClick() {
if(twoFragment == null)
{
twoFragment = new TwoFragment();
twoFragment.setOnFTwoClickListener(this);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.content, twoFragment, "TWO");
transaction.addToBackStack(null);
transaction.commit();
}
@Override
public void OnFTwoClick() {
if(threeFragment == null)
{
threeFragment = new ThreeFragment();
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.hide(twoFragment);
transaction.add(R.id.content, threeFragment, "THREE");
transaction.addToBackStack(null);
transaction.commit();
}
}
效果图:
因为是在手机上截的图,所以可能有点看不明白。这里解释一下,启动程序,载入OneFragment,在EditText中输入aaa,点击重甲的fragment one按钮,替换为TwoFragment,重复再OneFragment中的操作,先hide TwoFragment,再add ThreeFragment;接下来连续按三次返回按钮(其实这里是4次,因为退出输入法弹出窗也要1次),可以看到TwoFragment中的后输入的aaa被保留并复显了,而OneFragment中的就没有保留,这是因为在用TwoFragment替换OneFragment时,用的是replace()方法,而用ThreeFragment替换TwoFragment时却是先用hide()隐藏掉TwoFragment,然后再用add()方法添加ThreeFragment。具体方法的含义上面都解释过,这里就不再多做解释了。
上面的例子中还有类似Activity的TaskStack效果的实现,是因为用到了FragmentTransaction.addToBackStack(String)方法,这个方法将当前的Fragment添加到回退栈中,通过返回键实现Fragment的退栈操作。
还有就是在OneFragment和TwoFragment中用两种方式添加了监听回调,这么做的目的是为了把它们中的按钮点击事件交给Activtiy来处理。这就解除了它们和Activity之间的耦合。
有一个需要
注意的地方是:
Activity文件一定要继承Activity类,这是我亲自实践得出来的结论。之前我用FragmentStackActivity继承AppCompatActivity,当然,也不是我有意这么做,是Android Studio自动完成的,结果一按返回键,应用直接退出回到桌面,根本不出现回退栈的效果。
继承FragmentActivity也没效果,亲试。
下面来介绍以下Fragment与Activity之间以及Fragment之间的通信:
Fragment与Activity之间的通信比较简单,想要在Fragment中获取Activity的数据,只要通过getActivity()方法获取到Activity的引用,然后就可以通过此引用访问Activity中公共的属性和方法了;而想要在Activity中获取Fragment中的数据,有两种方式,一种方式是Activity直接持有目标Fragment的引用,通过该引用访问目标Fragment的数据即可,另一种方式是通过FragmentManager.findFragmentById()或FragmentManager.findFragmentByTag()方法获取目标Fragment的引用,然后通过该引用访问目标Fragment的数据。
Fragment之间的通信,说白了就是两个Fragment通过各自的Activity进行通信,也就是说Fragment之间不能直接通信,必须通过Activity才能完成通信。
一般情况下,某个Fragment要接收从另一个Activity通过Intent传递过来的数据,只要用下面的这种方式就能完成:
getActivity().getIntent().getStringExtra(String);
不过这样有一个问题,就是这个Fragment和它的Activity绑定了,不能达到解耦的目的。所以推荐使用Arguments来传递参数。下面展示一个例子:
这个例子主要的效果是:在一个ListFragment中有一个字符串列表,点击具体条目跳到内容Fragment,其中显示每个条目的内容(将选定条目的内容传递过来),并在此基础上添加一些新内容(将原有的内容和新增内容拼接起来传递回去),当返回ListFragment时,该条目显示拼接后的内容。
ListTitleFragment.java:
package com.egotrip.wcc.fragment;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.egotrip.wcc.ui.ContentActivity;
import java.util.Arrays;
import java.util.List;
/**
* Created by wcc on 2015/12/3.
*/
public class ListTitleFragment extends ListFragment {
public static final int REQUEST_DETAIL = 1;
private List<String> mTitles = Arrays.asList("Hello", "Android", "World");
private int mCurrPos;
private ArrayAdapter<String> adapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCurrPos = position;
Intent intent = new Intent(getActivity(),ContentActivity.class);
intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
startActivityForResult(intent, REQUEST_DETAIL);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_DETAIL)
{
mTitles.set(mCurrPos, mTitles.get(mCurrPos)+"--"+data.getStringExtra(ContentFragment.RESPONSE));
adapter.notifyDataSetChanged();
}
}
}
ContentFragment.java:
package com.egotrip.wcc.fragment;
/**
* Created by wcc on 2015/12/3.
*/
import android.app.Fragment;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Random;
public class ContentFragment extends Fragment {
private String mArgument;
public static final String ARGUMENT = "argument";
public static final String RESPONSE = "response";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if(bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
Intent intent = new Intent();
intent.putExtra(RESPONSE, "good");
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
}
}
public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment fragment = new ContentFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Random r = new Random();
TextView tv = new TextView(getActivity());
tv.setText(mArgument);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundColor(Color.argb(r.nextInt(100), r.nextInt(255), r.nextInt(255), r.nextInt(255)));
return tv;
}
}
ContentActivity.java:
package com.egotrip.wcc.ui;
import android.app.Fragment;
import com.egotrip.wcc.fragment.ContentFragment;
/**
* Created by wcc on 2015/12/3.
*/
public class ContentActivity extends FragmentSingleActivity {
private ContentFragment fragment;
@Override
public Fragment createFragment() {
String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);
fragment = ContentFragment.newInstance(title);
return fragment;
}
}
ListTitleActivity.java:
package com.egotrip.wcc.ui;
import android.app.Fragment;
import com.egotrip.wcc.fragment.ListTitleFragment;
/**
* Created by wcc on 2015/12/3.
*/
public class ListTitleActivity extends FragmentSingleActivity {
private ListTitleFragment fragment;
@Override
public Fragment createFragment() {
fragment = new ListTitleFragment();
return fragment;
}
}
因为上面两个Activity的布局一样,所以单独抽象出来一个类,让它们继承它:
package com.egotrip.wcc.ui;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import com.egotrip.wcc.R;
public abstract class FragmentSingleActivity extends Activity {
public abstract Fragment createFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_fragment);
FragmentManager manager = getFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.fragment_container);
if(fragment == null)
{
fragment = createFragment();
manager.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
}
}
activity_single_fragment.xml:
<RelativeLayout 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:id="@+id/fragment_container"
/>
效果图:
上面的ListTitleFragment中,直接startActivityForResult()方法和Intent将数据传递到ContentActivity中,接收数据用的方式是复写onActivityResult()方法,通过判断请求码和参数data的getStringExtra(String)方法完成数据的接收,这都是在Activity中常见的操作,都没什么好说的。要说的是ContentActivity中的ContentFragment获取数据的方式。
ContentFragment提供了一个newInstance(String)方法,通过此方法创建一个ContentFragment实例,更重要的是此方法里有一句setArguments的代码。ContentActivity中,先用
getIntent().getStringExtra(ContentFragment.ARGUMENT);
方式获取了ListTitleFragment传递过来的数据,在用newInstance(String)方法创建ContentFragment实例时,将获得的数据作为参数传递给了newInstance(String)方法,在方法内部,通过Bundle绑定,再通过setArguments(Bundle)方法将数据传递到ContentFragment内部。在ContentFragment的onCreate()方法中,通过getArguments()方法获取Bundle对象,通过Bundle.getString(String)方法获取数据。这就是整个接收数据的过程。这样就实现了解耦的目的。
至于ContentFragment设置Result的方式,这就简单了,直接通过
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
即可。和Activity一样。
以上就是这两天所学,在此做个记录。
参考博客:
http://blog.csdn.net/lmj623565791/article/details/37970961
http://blog.csdn.net/lmj623565791/article/details/37992017
http://blog.csdn.net/lmj623565791/article/details/42628537