您的位置:首页 > 移动开发 > Android开发

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

2015-09-05 16:15 751 查看
前言:·前一篇已经讲了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的使用)。

需求:给出一个界面,界面上方是登录、注册横向导航,实现登录、注册的切换
代码实现:

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;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

view = inflater.inflate(R.layout.fragment_login, container, false);
return view;
}

@Override
public 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() {

@Override
public 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 {

@Override
public 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;

@Override
protected 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();

// 默认是显示 LoginFragment
ft.replace(R.id.ll_content, loginFragment);
ft.commit();

}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_login:
ft = fm.beginTransaction();

loginFragment = new LoginFragment();
// 默认是显示 loginFragment
ft.replace(R.id.ll_content, loginFragment);
break;

case R.id.tv_regist:
ft = fm.beginTransaction();

registFragment = new RegistFragment();
// 默认是显示 registFragment
ft.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之间的数据传递,以及实现抽屉导航,敬请期待...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android Fragment