Android视角,精妙绝伦的回调方法
众所周知,在android里面充斥着众多的监听器,如一个按钮具有的OnClickListener,能对按钮的点击事件进行监听,这些监听器通常是一个接口,我们可以通过实现接口里的回调方法,执行事件处理。而AsyncTask也能通过其回调方法在恰当的时间执行异步任务(doInBackground()中),并且在执行完毕后回调到onPostExecute(),我们可以在onPostExecute下面获得异步任务的结果,并使结果安全地在UI线程中显示。AsyncTask是个抽象类,这些回调方法可能是抽象方法,也可能是普通的方法,像doInBackground是抽象方法,强制使用AsyncTask的人去实现,而onPostExecute等回调方法则不是抽象的,使用者可选地对其进行重写。
回调方法这种设计在我看来是如此的精妙绝伦,它总能在恰到好处的时间返回和执行正确的事。上面提到了两种实现回调方法的方式:1.是抽象的抽象,面向接口,所有监听器中的方法都必须实现。2.是单纯的抽象,能够可选地进行回调需要的方法。下面我就自己写了两种方式的回调,及说明使用场景。
方式一:使用接口(使用场景:自定义View时作为事件监听器)
/** * 自定义列表头视图 * * @author Change * */ public class ListHeadView extends LinearLayout implements OnClickListener { private OnHeadControlListener colLis; private CheckBox c_all; private TextView field1, field2, field3, field4; private Button controlBtn; private final int count = 4; public ListHeadView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); inflateView(context); } public ListHeadView(Context context, AttributeSet attrs) { super(context, attrs); inflateView(context); } public ListHeadView(Context context) { super(context); inflateView(context); } private void initView() { c_all = (CheckBox) this.findViewById(R.id.c_all); field1 = (TextView) findViewById(R.id.field1); field2 = (TextView) findViewById(R.id.field2); field3 = (TextView) findViewById(R.id.field3); field4 = (TextView) findViewById(R.id.field4); controlBtn = (Button) findViewById(R.id.controlBtn); controlBtn.setOnClickListener(this); c_all.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { colLis.onCheckAllChange(isChecked);// 调用1 } }); } private void inflateView(Context context) { View.inflate(context, R.layout.list_head, this); initView(); } public void setTexts(String[] texts) { if(texts.length>=count){ field1.setText(texts[0]); field2.setText(texts[1]); field3.setText(texts[2]); field4.setText(texts[3]); }else{ throw new ArrayIndexOutOfBoundsException("you don't have tomany fields!"); } } public void setButtonResource(int resid){ controlBtn.setBackgroundResource(resid); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.controlBtn: colLis.onClickRightBtn();// 调用2 break; default: break; } } /** * 回调接口,提供按钮和复选框的监听 * * @author Change * */ public interface OnHeadControlListener { /** * 点击右边按钮后触发 */ public void onClickRightBtn(); /** * 复选框状态改变后触发 * * @param isChecked */ public void onCheckAllChange(boolean isChecked); } /** * 设置监听器 * * @param colLis */ public void setOnHeadControlListener(OnHeadControlListener colLis) { this.colLis = colLis; } }
那么,我们在使用的时候就可以通过
setOnHeadControlListener方法去设置监听器了。
ListHeadView headView = new ListHeadView(context); headView.setOnHeadControlListener(new OnHeadControlListener() { @Override public void onClickRightBtn() { Toast.makeText(context, "点击按钮", Toast.LENGTH_SHORT).show(); } @Override public void onCheckAllChange(boolean isChecked) { Toast.makeText(context, "选择变化为"+isChecked, Toast.LENGTH_SHORT).show(); } });
方式二:使用抽象类(异步任务结果返回)
/** * apk搜索器 * * @author Change * */ public abstract class ApkFileSearcher { private String keyword;// 搜索关键字 private static final String TAG = ApkFileSearcher.class.getSimpleName(); public ApkFileSearcher(String keyword) { this.keyword = keyword; } /** * 从列表中获得关键字匹配的新列表 * * @param allApps * @return */ public List<AppsBean> onSearchIn(List<AppsBean> allApps) { List<AppsBean> results = new ArrayList<AppsBean>(); for (AppsBean app : allApps) { if (app.appName.contains(keyword) || app.pkgName.contains(keyword)) { results.add(app); } } allApps.clear(); allApps.addAll(results); return allApps; } /** * 抽象方法:回调搜索结果 * * @param result */ public abstract void onSearchFinished(List<AppsBean> result); /** * 本地异步搜索 * * @param allApps */ public void startAsyncLocalSearch(final List<AppsBean> allApps) { new AsyncTask<Void, Void, List<AppsBean>>() { @Override protected List<AppsBean> doInBackground(Void... params) { return onSearchIn(allApps); } @Override protected void onPostExecute(List<AppsBean> result) { onSearchFinished(result);// 调用,传入结果 }; }.execute(); }
使用的时候便要实现其中的抽象方法。
final ApkFileSearcher searcher = new ApkFileSearcher(keyword) { @Override public void onSearchFinished(List<AppsBean> result) { //在界面对结果进行处理。如传入并构建列表适配器 } };
再次感慨这前人伟大的发明,无敌的回调设计,当然,使用回调方法也有弊端,个人认为大量使用匿名内部类将降低代码的可读性,用的时候需要慎重,不过,贵在用之方便,依旧强调它的特点--恰到好处。