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

前言:·前一篇已经讲了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;
	
	
	@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();
	}
}
  • 部署项目,运行如下:
Android学习系列之(七)Fragment深入_第1张图片
点击注册,变成如下注册界面

三、小结

经过理论和实践,就基本掌握了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)