Android学习系列之(七)Fragment深入

来源:互联网 发布:四大台柱 知乎 编辑:程序博客网 时间:2024/04/27 22:10

前言:·前一篇已经讲了Fragment的基本使用,那么现在开始深入一点讨论

一、FragmentManager和FragmentTranscation:

  在上一篇博客,我们在动态使用Fragment时,已经见到了。再次我们重新回顾一下那段代码:

fm = getFragmentManager();ft = fm.beginTransaction();ft.replace(R.id.container, new FragmentLife());ft.commit();

注:变量的声明,private FragmentManager fm; private FragmentTransaction ft;

肯定有不少人对这两个类的情况表示迷茫,现在我们简单介绍一下这两个类的情况。

1. FragmentManager:用于管理activity中的fragments, 可通过getFragmentManager()或getSupportFragmentManager()获得。

   其常用的方法有:
    • findFragmentById()     #根据ID查找Fragment实例,主要用于静态添加fragment的布局中,因为静态添加的fragment才会有ID
    • findFragmentByTag() #根据TAG查找对应的Fragment实例,主要用于在动态添加的fragment中,根据TAG来找到fragment实例
    • getFragments()           #获取所有被ADD进Activity中的Fragment
2. FragmentTransaction:用来对当前的Fragment进行管理,包括add,replace,remove
    其常用的方法有:
    • add(int containerViewId,Fragment fragment,String tag)  # 将一个fragment实例添加到Activity的最上层
    • remove(Fragment fragment)    # 将一个fragment实例从Activity的fragment队列中删除
    • replace(int containerViewId, Fragment fragment)            # 替换containerViewId中的fragment实例

注意:该函数会先把containerViewId中所有fragment删除,然后再add进去当前的fragment
    • hide(Fragment fragment)                                                 # 将指定的fragment隐藏不显示
    • show(Fragment fragment)                                               # 将以前hide()过的fragment显示出来
    • detach(Fragment fragment)      # 将view与fragment分离,将此将view从viewtree中删除,将fragment从Activity的ADD队列中移除,但此fragment实例并不会删除,此fragment的状态依然保持着使用,所以在fragmentManager中仍然可以找到,即通过FragmentManager::findViewByTag()仍然是会有值的
    • attach(Fragment fragment)     # 与detach()所做的工作相反,attach()操作的结果是,最新操作的页面始终显示在最前面

    关于事务回滚使用方法:只需要要使用下面两个代码

# 在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中
transaction.addToBackStack(String tag);

# 在需要回退时,使用popBackStack()将最上层的操作弹出回退栈
manager.popBackStack(); 
注:当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层
void popBackStack(int id, int flags);  
void popBackStack(String name, int flags); 
参数解释:
|--参数int id是当提交变更时transaction.commit()的返回值
|--参数string name是transaction.addToBackStack(String tag)中的tag值
|--参数int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE
|--当取值0时,表示除了参数指定这一层之上的所有层都退出栈,指定的这一层为栈顶层
|--当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈

    关于事务回退的原则:回退是以commit()提交的一次事务为单位的,而不是以其中的add,replace等等操作为单位回退的.举例: 若在一次提交时,一次性添加了fragment2,fragment3,fragment4,则回退时,会依据添加时的顺序,将它们一个个删除,返回到没有添加fragment4,fragment3,fragment2的状态.

    [这样说一些纯文字,可能比较枯燥,但是这些还是必须要掌握的,关于这写函数的例子就不多写了,给推荐一个博客:http://blog.csdn.net/harvic880925/article/details/44927375,这位大牛写的很详细,就不多献丑了]

3. 在实战中的运用方法:
|-- 如果使用replace来切换页面,那么在每次切换的时候,Fragment都会重新实例化,重新加载一边数据,这样非常消耗性能和用户的数据流量。
|--这是因为replace操作每次都会把container中的现有的fragment实例清空,然后再把指定的fragment添加进去,就造成了在切换到以前的fragment时,就会重新实例会fragment。
|-- 正确的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个。这样就能做到多个Fragment切换不重新实例化
|-- 基本算法:

public void switchContent(Fragment from, Fragment to) {      if (!to.isAdded()) {      // 先判断是否被add过      // 隐藏当前的fragment,add下一个到Activity中          transaction.hide(from).add(R.id.content_frame, to).commit();     } else {          transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个      }  } 

|-- add()和replace()千万不要共用

二、Fragment练习:Fragment的切换

看了那么前面一大段的理论性东西,现在我们通过一个小案例来进一步掌握刚刚的知识(FragmentManager和FragmentTranscation的使用)。
  1. 需求:给出一个界面,界面上方是登录、注册横向导航,实现登录、注册的切换
  2. 代码实现:
  • activity_switch_ragment.xml:
<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="vertical"    tools:context="com.johnnie.fragment.MainActivity" >    <LinearLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:orientation="horizontal" >        <TextView            android:id="@+id/tv_login"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:background="#483D8B"            android:gravity="center"            android:text="@string/btn_login"            android:textColor="#FFFFFF"            android:textSize="25sp" />        <TextView            android:id="@+id/tv_regist"            android:layout_width="0dp"            android:layout_height="wrap_content"            android:layout_weight="1"            android:background="#0000FF"            android:gravity="center"            android:text="@string/btn_regist"            android:textColor="#FFFFFF"            android:textSize="25sp" />    </LinearLayout>    <LinearLayout        android:id="@+id/ll_content"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:orientation="vertical" >    </LinearLayout></LinearLayout>
  • fragment_login.xml:
<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="vertical"    tools:context="com.johnnie.login.MainActivity" >    <EditText         android:id="@+id/et_username"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:singleLine="true"        android:hint="@string/et_username"        />        <EditText         android:id="@+id/et_userpass"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:singleLine="true"        android:inputType="textPassword"        android:hint="@string/et_userpass"        />        <Button         android:id="@+id/btn_login"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="@string/btn_login"        /></LinearLayout>
  • fragment_regist.xml:
<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="vertical"    tools:context="com.johnnie.login.MainActivity" >    <EditText         android:id="@+id/et_username"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:singleLine="true"        android:hint="@string/et_username"        />        <EditText         android:id="@+id/et_userpass"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:singleLine="true"        android:inputType="textPassword"        android:hint="@string/et_userpass"        />        <Button         android:id="@+id/btn_regist"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="@string/btn_regist"        /></LinearLayout>
  • LoginFragment:
public class LoginFragment extends Fragment{// 控件的声明private EditText et_username;private EditText et_userpass;private Button btn_login;private View view;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {view = inflater.inflate(R.layout.fragment_login, container, false);return view;}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);et_username = (EditText) view.findViewById(R.id.et_username);et_userpass = (EditText) view.findViewById(R.id.et_userpass);btn_login = (Button) view.findViewById(R.id.btn_login);btn_login.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {login();}});}/** * 用户登录 */private void login() {System.out.println("登录...");// 获取用户输入String username = et_username.getText().toString().trim();String userpass = et_userpass.getText().toString().trim();// 输入校验boolean flag = false;String msg = "登录失败!";if (!TextUtils.isEmpty(userpass) && !TextUtils.isEmpty(username)) {msg = "登录成功!";flag = true;} // 弹出提示Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();if (flag) {// 通过调用 newInstance 函数来获取实例并传递参数WelcomeFragment fragment = WelcomeFragment.newInstance(username);FragmentManager fm = getFragmentManager();FragmentTransaction ft = fm.beginTransaction();ft.replace(R.id.content, fragment);ft.commit();}}}
  • RegistFragment:
public class RegistFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_regist, null);}}
  • SwitchFragmentActivity:
public class SwitchFragmentActivity extends Activity implements OnClickListener{public FragmentManager fm;public FragmentTransaction ft;private LoginFragment loginFragment;private RegistFragment registFragment;private TextView tv_login;private TextView tv_regist;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_switch_ragment);tabWithFragment();}private void tabWithFragment() {tv_login = (TextView) findViewById(R.id.tv_login);tv_regist = (TextView) findViewById(R.id.tv_regist);tv_login.setOnClickListener(this);tv_regist.setOnClickListener(this);fm = getFragmentManager();ft = fm.beginTransaction();loginFragment = new LoginFragment();// 默认是显示 LoginFragmentft.replace(R.id.ll_content, loginFragment); ft.commit();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.tv_login:ft = fm.beginTransaction();loginFragment = new LoginFragment();// 默认是显示 loginFragmentft.replace(R.id.ll_content, loginFragment); break;case R.id.tv_regist:ft = fm.beginTransaction();registFragment = new RegistFragment();// 默认是显示 registFragmentft.replace(R.id.ll_content, registFragment); break;}ft.commit();}}
  • 部署项目,运行如下:

点击注册,变成如下注册界面

三、小结

经过理论和实践,就基本掌握了Fragment的使用。现在总结一下刚才所学。
|-- FragmentManager 和 FragmentTranscation 的基本使用
|-- 如何在同一个Activity中管理Fragment,进行Fragment的切换?步骤如下:
1.获取到FragmentManager: 在V4包中通过getSupportFragmentManager,原生的Fragment是通过getFragmentManager
2.开启一个事务: beginTransaction
3.向容器内加入Fragment: add或者replace,需要传入容器的id和Fragment的实例
4.提交事务: commit
下一篇将讲解Fragment之间的数据传递,以及实现抽屉导航,敬请期待...得意

1 0
原创粉丝点击