本篇文章接上一篇Android Fragment的前世今生(一),不同的是本文将深入的分析Fragment的一些使用技巧和方法
Fragment :主要用于定义Fragment
FragmentManager: 主要用于在Activity中操作Fragment
FragmentTransaction: 保证Fragment操作的原子性
FragmentTransaction :通过benginTransatcion()获取进而开启一个事务
transaction.add() :往Activity中添加一个Fragment
transaction.remove() :从Activity中移除一个Fragment
transaction.replace():替换Fragment,等价于remove+add
transaction.hide():隐藏当前的Fragment,
transaction.show():显示之前隐藏的Fragment
transaction.detach():解除视图状态与fragment实例的关联
transaction.attach():重建view视图,附加到UI上并显示。
transatcion.commit():提交一个事务
remove和detach的区别
在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。
在上一篇文章中,我主要使用android.app.Fragment
包中的Fragment和FragmentManger等,当使用android.support.v4.app.Fragment包时,必须通过
getSupportFragmentManager()`来获取FragmentManger,此外v4包中提供了FragmentPagerAdapter用于支持和ViewPager的配合使用,但前者并没有提供该适配器。
关于这个回退栈的问题我真是够了,studio的模拟器无限卡死,主要是因为硬盘的原因,来回测试了数十次,通过back键无法实现页面回退的效果,此外,还意外发现了两个Crash,这里记录一下。
思路上很简单,我让FragmentFirst和FragmentSecond之间进行来回调用,有两种模式,一种是隐藏前一个,添加下一个(当然你可以show,为了模拟o(∩_∩)o 哈哈),第二种是直接销毁第一个并添加第二个,同时为了展示效果,我改变imageView的图片。。可惜,测试失败,于是去测试了鸿洋和郭神的Demo,依旧失败(back键直接返回home主页,我觉得应该是直接销毁了Activity),那好么我用Log好吧。请了解问题所在的朋友评论下指出问题,不胜感激,
代码:
FirstFragment
public class FirstFragment extends Fragment implements View.OnClickListener {
ImageView imageView;
private Button change;
private Button hide;
private Button destroy;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.first_layout, container, false);
//将控件绑定到View上
imageView = (ImageView) view.findViewById(R.id.imageFirst);
change = (Button) view.findViewById(R.id.imageChange);
hide = (Button) view.findViewById(R.id.hide);
destroy = (Button) view.findViewById(R.id.destroy);
change.setOnClickListener(this);
destroy.setOnClickListener(this);
hide.setOnClickListener(this);
//Log下显示BackStack元素个数
int TAG = getFragmentManager().getBackStackEntryCount();
Log.d("TAG1",String.valueOf(TAG));
if(TAG>0)
Log.d("TAG1",getFragmentManager().getBackStackEntryAt(TAG-1).toString());
return view;
}
@Override
public void onClick(View v) {
SecondFragment secondFragment = new SecondFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
switch (v.getId()) {
case R.id.destroy:
//保存fragment实例,移除视图与fragment实例的关联
transaction.addToBackStack(null);
transaction.replace(R.id.mainLayout, secondFragment);
break;
case R.id.imageChange:
imageView.setImageResource(R.drawable.dog);
break;
case R.id.hide:
transaction.hide(this);
transaction.addToBackStack(null);
transaction.add(R.id.mainLayout, secondFragment);
break;
}
transaction.commit();
}
}
SecondFragment
public class SecondFragment extends Fragment {
FragmentTransaction transaction;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.second_layout, container, false);
}
//在onCreateView之后执行
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FirstFragment firstFragment = new FirstFragment();
transaction = getFragmentManager().beginTransaction();
transaction.hide(this);
transaction.add(R.id.mainLayout,firstFragment);
transaction.addToBackStack(null);
transaction.commit();
int TAG = getFragmentManager().getBackStackEntryCount();
Log.d("TAG2",String.valueOf(TAG));
if(TAG>0)
Log.d("TAG2",getFragmentManager().getBackStackEntryAt(TAG-1).toString());
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
Fragment fragmentFirst;
FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentFirst = new FirstFragment();
transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.mainLayout,fragmentFirst);
transaction.commit();
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
}
Log里倒是没什么问题,前两次并没有创建fragment实例,故为0,后面每次都会创建fragment实例,则依次+1.
如果在Activity中包含了对应的Fragment实例,那么直接访问Fragment的属性和方法即可。
如果没有上述实例,可以通过getFragmentManager.findFragmentById()获取Fragment实例。
Fragment fragment = getFragmentManager().findFragmentById();
在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。
测试Demo:
主要更改Frgment的代码即可,原理上就是获取FirstFragment的imageFirst引用的资源并显示到SecondFragment的imageSeocond视图上。
public class SecondFragment extends Fragment {
FragmentTransaction transaction;
private ImageView imageViewSecond;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//绑定imageSecond到view
View view = inflater.inflate(R.layout.second_layout,container,false);
imageViewSecond = (ImageView)view.findViewById(R.id.imageSecond);
return view;
}
//在onCreateView之后执行
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//获取ImageFirst的图片资源
ImageView img = (ImageView) getActivity().findViewById(R.id.imageFirst);
imageViewSecond.setImageDrawable(img.getDrawable());
int TAG = getFragmentManager().getBackStackEntryCount();
Log.d("TAG2",String.valueOf(TAG));
if(TAG>0)
Log.d("TAG2",getFragmentManager().getBackStackEntryAt(TAG-1).toString());
//延迟新建FirstFragment
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
FirstFragment firstFragment = new FirstFragment();
transaction = getFragmentManager().beginTransaction();
transaction.hide(SecondFragment.this);
transaction.add(R.id.mainLayout,firstFragment);
transaction.addToBackStack(null);
transaction.commit();
}
},5000);
}
}
特别注意:imageSecond所显示的图片取决于回退栈栈底的那个Fragment视图的引用。所以在执行一次后不会再改变。
对与Fragment的通信,通过接口回调进行封装可以使Activity和Fragment的耦合完全分离,提高开发的效率,但相对来说代码上会复杂一些,现在有更好的解决办法,那就是EventBus。
关于利用EventBus进行通信我准备通过第三篇进行介绍,欢迎关注。
Tab实战Demo效果图:
特点:代码耦合度高
特点:代码简介,每个fragment单独管理一个页面,但不支持滑动
全面解耦并支持滑动和点击事件
其实还可以进行很多的优化使用,后续会继续总结出来,实践代码上主要参考了郭霖和鸿洋的博客文章,有什么疑问欢迎指出。
本文源码(包括TabDemo):源码地址