http://www.ituring.com.cn/article/131447
回调函数
把回调和API(Application Programming Interface,应用程序编程接口)放在一起理解 enter image description here
API:低层将函数封装起来供给高层来调用。一般来说这些函数对高层来说都是已知的。
回调:回调正好相反,它是高层提供给低层的调用,对于低层来说高层的函数是未知的。
其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层的那个函数。
例如:模块A有一个函数foo,它向模块B传递foo的地址,然后B里面发生某种事件时,通过A里面传递过来的foo的地址来调用foo函数,通知A发生了什么事情,让A做出反应。这样我们就称foo为回调函数。
为了和最后一张图保持一致,故意倒过来了! ^^
理解CriminalIntent中关于回调的例子 (P324)
要想实现的目的是:向crime的明细fragment中添加CrimeFragment。让CrimeListActivity可以展示一个完整的双版面的用户界面。
enter image description here[+]查看原图
我们可能首先想到这么做:为平板设备实现一个CrimeListFragment。onListItemClick(…)监听器方法。这样onListItemClick(…)方法会获取CrimeListActivity中的FragmentManager,然后提交一个fragment事务,将CrimeFragment添加到明细fragment容器中。
public void onListItemClick(ListView l, View v, int position, long id){
Crime crime = ((CrimeAdapter)getListAdapter()).getItem(position);
Fragment fragment = CrimeFragment.newInstance(crime.getId());
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.beginTransaction().add(R.id.detailFragmentContainer, fragment).commit();
}
如果要开发一个fragment来添加其他fragment到activity的FragmentManager,那么这个fragment就必须知道托管的activity是如何工作的。这样就破坏了fragment的独立性。 下面采用另外一种做法:
添加回调接口(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
private ArrayList mCrimes;
private boolean mSubtitleVisible;
private Callbacks mCallbacks;
public interface Callbacks {
void onCrimeSelected(Crime crime);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks)activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
...
}
这样,CrimeListFragment有了调用托管activity的方法。另外,他不关心托管activity是谁。只要托管activity实现了CrimeListFragment.Callbacks接口,而CrimeListFragment中的代码不需要改变。
CrimeListFragment将托管的activity强转为CrimeListFragment.Callbacks对象。则意味着,托管activity必须实现CrimeListFragment.Callbacks接口。
实现回调接口(CrimeListActivity.java)
public class CrimeListActivity extends SingleFragmentActivity
implements CrimeListFragment.Callbacks {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
@Override
protected int getLayoutResId() {
return R.layout.activity_masterdetail;
}
public void onCrimeSelected(Crime crime) {
}
...
}
最终,在onListItemClick(…)方法里以及在用户创建新crime时,CrimeListFragment将调用onCrimeSelected(Crime)方法。 当onCrimeSelected(Crime)方法被调用时,CrimeListActivity需要完成以下二选一的任务:
设备是手机:启动新的CrimePagerActivity。
设备是平板:将CrimeFragment放入detailFragmentContainer中。
根据条件启动CrimeFragment(CrimeListActivity.java)
public void onCrimeSelected(Crime crime) {
if (findViewById(R.id.detailFragmentContainer) == null) {
// 如果布局中没有包含detailFragmentContainer,
// 说明设备是手机。于是应该启动新的CrimePagerActivity
Intent i = new Intent(this, CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivityForResult(i, 0);
} else {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment oldDetail = fm.findFragmentById(R.id.detailFragmentContainer);
Fragment newDetail = CrimeFragment.newInstance(crime.getId());
// 创建一个fragment事务,将我们需要的CrimeFragment
// 添加到detailFragmentContainer中。如果之前就有CrimeFragment存在,
// 首先应从detailFragmentContainer中移除它
if (oldDetail != null) {
ft.remove(oldDetail);
}
ft.add(R.id.detailFragmentContainer, newDetail);
ft.commit();
}
}
最后应该在CrimeListFragment类中,启动新的CrimePagerActivity的地方,调用onCrimeSelected(Crime)方法。 在CrimeListFragment.java中修改onListItemClick(…)和onOptionsItemSelected(MenuItem)方法实现对Callbacks.onCrimeSelected(Crime)方法的调用。
调用全部回调方法(CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
// get the Crime from the adapter
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
mCallbacks.onCrimeSelected(c);
}
…
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
mCallbacks.onCrimeSelected(crime);
return true;
…
}
}
总结
上面CriminalIntent的例子和第二张图片类比:
enter image description here
enter image description here