fragment最佳实践
前言
fragment在项目中广泛使用,但是怎么使用才能发挥它的最大价值呢,我认为有个标准就是:你的fragment是否能复用,随便扔到哪个Acvtivity都玩得转。从实际开发中我总结了以下几点,旨在更规范地使用fragment(当然不是所有fragment都必须来复用,也可以专门处理某些业务,那就没必要刻意追求fragment的规范了,随意用就好。。)
Fragment or Activity?
我的看法是不要去比较两者的性能,没有意义,google让他们都存在肯定都是有价值的。它们的应用场景不同。Activity更倾向于一个整体模块容器,而Fragment是其中的子模块。可以理解成一个工厂(App)有N个生产不同产品的产房(Activity),每个厂房(Activity)里面有生产N类子产品的机器(Fragment)。所以,Activity的存在可以对应用更好的结构化和模块化的划分,让应用有更健壮和清晰的层次,而Fragment可以让将应用的功能细化和具象化。两者没有好坏之分,根据功能划分粒度来选取合适的载体才是正确的架构方式。
基本使用
FragmentManager fm = getSupportFragmentManager();
mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container);
if(mContentFragment == null )
{
mContentFragment = new ContentFragment();
fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit();
}
1、为什么需要判null呢?
主要是因为,当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。
2、add(R.id.id_fragment_container,mContentFragment)中的布局的id有何作用?
一方面呢,是告知FragmentManager,此fragment的位置;另一方面是此fragment的唯一标识;就像我们上面通过fm.findFragmentById(R.id.id_fragment_container)查找
封装BaseFragment基类
public abstract class BaseFragment extends Fragment {
protected View mRootView;@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(null == mRootView){
mRootView = inflater.inflate(getLayoutId(), container, false);
}
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
afterCreate(savedInstanceState);
}
//实现fragment的布局
protected abstract int getLayoutId();
protected abstract void afterCreate(Bundle savedInstanceState);
}
使用静态工厂方法newInstance(...)来获取Fragment实例
为啥要这么弄?fragment应用到不同的Activity,我们可以重载newInstance方法,根据需求new不同的fragment实例
public class WeatherFragment extends Fragment{
private static final String ARG1 = "arg1";
public static WeatherFragment newInstance(String cityName) {
Bundle args = new Bundle();
args.putString(cityName,ARG1);
WeatherFragment fragment = new WeatherFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
String city = bundle.getString(ARG1);
}
}
}
fragment监听虚拟按键和back按键
//我见过很多方法,这个方法是最好的,给rootView设置一个OnKeyListener来监听key事件
mRootView.setFocusable(true);
mRootView.setFocusableInTouchMode(true);
mRootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//不一定是要触发返回栈,可以做一些其他的事情,我只是举个栗子。
getActivity().onBackPressed();
return true;
}
return false;
}
});
回退栈
如果一个Activity有FragmentOne打开了FragmentTwo,想要back回退,可以使用回退栈
http://img.blog.csdn.net/20140720144355734
FragmentThree fThree = new FragmentThree();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
tx.add(R.id.id_content , fThree, "THREE"); //如果是hide,add那么fragment的视图不会重绘,比如edittext输入文字还会保存
// tx.replace(R.id.id_content, fThree, "THREE"); //如果是replace 那么视图重绘
tx.addToBackStack(null);
tx.commit();
Activity接收fragment数据
这个是fragment与Activity解耦的关键,如果你觉得这个fragment可以被复用,那么在fragment不要处理任何事件,全部以接口形式抛到Activity,在需要回调的activity实现这个接口,这么一来Fragment就如同一个空壳子,真正的逻辑都让调用者Activity去做:
public class FragmentOne extends Fragment implements OnClickListener
{
private Button mBtn;
/**
* 设置按钮点击的回调
* @author zhy
*
*/
public interface FOneBtnClickListener
{
void onFOneBtnClick();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_one, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
mBtn.setOnClickListener(this);
return view;
}
/**
* 交给宿主Activity处理,如果它希望处理
*/
@Override
public void onClick(View v)
{
if (getActivity() instanceof FOneBtnClickListener)
{
((FOneBtnClickListener) getActivity()).onFOneBtnClick();
}
}
}
不同Activity的fragment间传递数据
依旧是一个简单的场景:两个Fragment,一个展示文章列表的Fragment(叫做ListTitleFragment),一个显示详细信息的Fragment(叫做:ContentFragment),当然了,这两个Fragment都有其宿主Activity。
现在,我们点击列表Fragment中的列表项,传入相应的参数,去详细信息的Fragment展示详细的信息,在详细信息页面,用户可以进行点评,当用户点击back以后,我们以往点评结果显示在列表的Fragment对于的列表项中;
也就是说,我们点击跳转到对应Activity的Fragment中,并且希望它能够返回参数,那么我们肯定是使用Fragment.startActivityForResult ;
在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,没有setResult()方法,用于设置返回的intent,这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
单个Fragment的Activity封装
为何会有单个fragment的Activity,直接用Activity不行吗?这是为以后的扩展,比如需求变了这个Activity界面需要再放两个页签,像之前只有一个Activity就麻烦了,现在直接加fragment就完事!
package com.example.demo_zhy_23_fragments;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
public abstract class SingleFragmentActivity extends FragmentActivity
{
protected abstract Fragment createFragment();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment =fm.findFragmentById(R.id.id_fragment_container);
if(fragment == null )
{
fragment = createFragment() ;
fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit();
}
}
}