Fragment 踩过的那些坑

                   

本文主要是针对fragment的 fragment的重影问题和按返回键无法像activity那样返回上个fragment, 对这两个坑进行阐述


 Fragment 的优点:       

                                     1.它可以数据保存,特别在网页加载并且需要反复使用的页面上。

                                      2.UI组件切换的灵活性及流畅性 ,在配合使用 table时更能体现。

                                      3.可以非常方便与主activity进行数据交换。


Fragment 的缺点

                                       1.各个fragment的管理非自动化,需要人为手动管理添加和移除。

                                       2.fragment的重影问题。

                                        3.按返回键无法像activity那样返回上个fragment。

这两个问题点的解决需要理解fragment的生命周期,如下

如图一,是Fragment的生命周期;图二,是Activity与Fragment生命周期的对比图; 
                                           图一                                                                 图二 

 


一、fragment的重影问题的探究

      fragment的重影问题产生的原因是:你的activity未移除fragment,而你只是销毁你之前添加的layout。

     我们需要明确一点:fragment!=layout。他们两者是独立的,只有在onCreateView中把相应的layout添加到fragment,他们才产生了关联,此时fragment显示出来。在onDestoryView 后他们又是独立的个体,此时在界面上看去你的fragment已经移除了,但是实际上它还是存在的只是不显示画面。fragment是容器,跟Activity一样。它可以添加许多layout。

那问题来了,fragment什么时候才是真正的从activity上移除呢? 

    从fragment的生命周期上看是onDetach,在我们实际的使用中FragmentManager.popBackStack()、FragmentTransaction.remove(Fragment fragment) 、FragmentTransaction.replace()。这三个方法能把fragment从Aciivity上移除。

    在实际开发中避免重影问题的方法有:

 1.在onCreateView中判断view是否已经存在如下

public abstract class BaseFragment extends Fragment {
	public View view;
        @Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		
		 if (view==null) {
		  view=inflater.inflate(R.layout.auto_root, container, false);
		}
		 
		return view;
	}
	 
	


2.AndroidManifest.xml            

添加 <android:configChanges="locale|touchscreen|keyboardHidden|orientation|screenSize|screenLayout|layoutDirection">


3.在主activity oncreate中如下设置

  

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
        mFgCotrol=new FragmentCotrol(this);  //mFgCotrol是一个fragment的管理类。如果saved...存在就把fragment全部移除并且再重新加载一次。这样就能避免重影问题
         if (savedInstanceState!=null) {
        	 mFgCotrol.popBackAllStacks();
		  } 

* @author Bill
 * 2018-3-21
 * @description:
 * fragment 管理类
 */
public class FragmentCotrol {
	private MainActivity mMainActivity;
	private final String TAG="FragmentCotrol";
	private FragmentManager fm;
	private List<ResultData> allFragment=new ArrayList<ResultData>();
	
	
	public FragmentCotrol(MainActivity activity){
		mMainActivity=activity;
		init();	
	
	}
	
	private void init(){
		 fm=mMainActivity.getFragmentManager();
		
	}
	
	
	/**
	 *2018-3-24
	 * @description : 移除所有的fragment
	 */
	public void popBackAllStacks(){
		if (fm.getBackStackEntryCount()>0) {
			for (int i = fm.getBackStackEntryCount(); i >= 1; i--) {
				BackStackEntry backStack= fm.getBackStackEntryAt(i-1);
				String backStackName=backStack.getName();
				removeFragmentFromLis(backStackName);
				fm.popBackStackImmediate();
			}
		}
		
	}
	
	/**
	 *2018-3-24
	 * @description :移除单个fragment
	 */
	public void popBackStacks(){
		int count=fm.getBackStackEntryCount();
		if (count<=0) {
          return ;
		}
		BackStackEntry backStack= fm.getBackStackEntryAt(count-1);
		String backStackName=backStack.getName();
		removeFragmentFromLis(backStackName);
		fm.popBackStackImmediate();
			
	}
	
	private void removeFragmentFromLis(String backStackName){
		int size=allFragment.size();
		if (size<=0) {
			return;
		}
		int j = 0;
		for (int i = 0; i < size; i++) {
			ResultData rd=allFragment.get(i);
			String name=rd.getTag();
			if (name.equals(backStackName)) {
				j=i;
			}
		}
		allFragment.remove(j);
		TLog.i(TAG, "removeFragmentFromLis -- backStackName="+backStackName+"  size="+allFragment.size());
	}
	
   public void replaceFragment(ResultData rd){
		 BaseFragment bf=rd.getFragment();
         String tag=rd.getTag();
         int id=rd.getLayoutId();
         allFragment.add(rd);
         FragmentTransaction ft=fm.beginTransaction();
         ft.replace(id, bf, tag).addToBackStack(tag).commit();
	}
	
   /**
 *2018-3-24
 * @description :增加到fragmentmanager上,并且也增加到allfragment上。
 * 新增的fragment会隐藏已经添加到fragmentManager上的frament。
 */
public void addFragmentToFragmentManager(ResultData rd){
	           
	           BaseFragment bf=rd.getFragment();
	           String tag=rd.getTag();
	           int id=rd.getLayoutId();
	           TLog.i(TAG, "BaseFragmentName:"+bf.getClass().getSimpleName()+"Fragment isadd: "+bf.isAdded()+" isHidden: "+bf.isHidden()+
	        		   " isVisible "+bf.isVisible());
	           if (!bf.isAdded()) {
	        	   allFragment.add(rd);
	        	   addFragmentToFragmentManager(bf, tag, id);
			}
	           
   }
   
	private void addFragmentToFragmentManager(BaseFragment bf,String tag,int id){
		FragmentTransaction ft=fm.beginTransaction();
		if (!bf.isAdded()) {
			ft.add(id, bf, tag).addToBackStack(tag);
			int size=allFragment.size();
			 for (int i = 0; i < size; i++) {
					ResultData rd=allFragment.get(i);
					String tagName=rd.getTag();
					if (tagName.equals(tag)) {
						continue;
					}
					ft.hide(fm.findFragmentByTag(tagName));
				}
				
			 ft.commit();
		}
		
	}
	
	public FragmentManager getFregmentmanager(){
		return fm;
	}

	public void showFragment(String tag) {
		// TODO Auto-generated method stub

		FragmentTransaction ft=fm.beginTransaction();
		int size=allFragment.size();
		if (size<=1) {
			return;
		}
		  for (int i = 0; i < size; i++) {
			ResultData rd=allFragment.get(i);
			String tagName=rd.getTag();
			if (tagName.equals(tag)) {
				continue;
			}
			//TLog.i(TAG, "showFragment--tagName:"+tagName);
			ft.hide(fm.findFragmentByTag(tagName));
		}
		  ft.show(fm.findFragmentByTag(tag));
		  ft.commit();
		
	}
 
	
	public void hideFragment(String tag) {
		// TODO Auto-generated method stub
		FragmentTransaction ft=fm.beginTransaction();	
		ft.hide(fm.findFragmentByTag(tag)).commit();	
	}
	
	
	public void printy(){
		 int size=allFragment.size();
		 for (int i = 0; i < size; i++) {
			 ResultData rd=allFragment.get(i);
			 BaseFragment bf=rd.getFragment();
	        TLog.i(TAG, "printy-BaseFragmentName:"+bf.getClass().getSimpleName()+"Fragment isadd: "+bf.isAdded()+" isHidden: "+bf.isHidden()+
	        		   " isVisible "+bf.isVisible());
		}
		 int count=fm.getBackStackEntryCount();
		 for (int i = count-1; i >=0; i--) {
			 TLog.i(TAG, "printy-BackStackEntryName:"+fm.getBackStackEntryAt(i).getName());
		}
	}
	
	public FragmentManager getFragmentManager(){
		if (fm==null) {
			mMainActivity.getFragmentManager();
		}
		return fm;
	}
    
	
}

以上几步,重影问题基本能解决掉了。



二、按返回键自动销毁fragment,实现跟activity一样的功能

    原理是:按返回键,会调用Activity的onBackPressed() 方法。

         那如何让onBackPressed() 方法与每个fragment联系起来呢?

        答案是:使用接口回调函数。

 实现步骤:

              1.写个接口类  

package com.cn.along.interfaces;

import com.cn.along.fragment.BaseFragment;

public interface HandledFragment {
	void setSelectedFragment(BaseFragment selectedFragment);

}
             

         

             2.Activity实现此接口

public class MainActivity extends Activity implements HandledFragment {
 private final String TAG="MainActivity";
 private FragmentCotrol mFgCotrol;
 private BaseFragment bf;//当前在活动fragment。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
        mFgCotrol=new FragmentCotrol(this);
         if (savedInstanceState!=null) {
        	 mFgCotrol.popBackAllStacks();
		  } 
         init();
    }
 
    
    @Override
    	protected void onDestroy() {
    		// TODO Auto-generated method stub
    		super.onDestroy();	    
    	mFgCotrol.popBackAllStacks();	
    	}
    @Override
    	public void onBackPressed() {
    		// TODO Auto-generated method stub
    	//判断fragment自己是否处理onBackPressed事件
        if (bf!=null&&bf.onBackPressed()){
			return;
		}
    	//如果只剩余一个fragment连续按两下返回键销毁activity,并且返回桌面。
    	if (mFgCotrol.getFragmentManager().getBackStackEntryCount()==1) {
    		oldTime=newTime;
    		long currentTime=System.currentTimeMillis();
    		newTime=currentTime;
    		long interval=newTime-oldTime;
			if (interval<=FINISHTIME) {
				//可以返回桌面。
				super.onBackPressed();
				finish();
			 
			}else{
				String show=getResources().getString(R.string.finish_activity);
				Toast.makeText(this, show, Toast.LENGTH_SHORT).show(); 	
			 }
		}else{	
			mFgCotrol.popBackStacks();
		}
    		
    }
//接口的实现
  @Override
	public void setSelectedFragment(BaseFragment selectedFragment) {
		// TODO Auto-generated method stub
		bf=selectedFragment;
	}

       

         3.fragment的实现

 public abstract class BaseFragment extends Fragment {
	public MainActivity mMainActivity;
	public View view;
	private final String TAG="BaseFragment";
	public HandledFragment mHandledFragment;
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// 获取mHandledFragment实例 
		 mMainActivity=(MainActivity) getActivity();
		 if(!(mMainActivity instanceof HandledFragment)){  
	            throw new ClassCastException("Hosting Activity must implement HandledFragment");  
	        }else{  
	        	mHandledFragment = (HandledFragment)mMainActivity;  
	        }  
		return view;
	}
	public abstract boolean onBackPressed();//子类复写此方法。
	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		mHandledFragment.setSelectedFragment(this);// 获取把此fragment传递到activity 

	}

   经过以上三个步骤就能实现按返回键自动销毁fragment,实现activity一样的功能。

至此重影问题和返回功能的实现已经可以解决了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值