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) {
//在界面对结果进行处理。如传入并构建列表适配器
}
};
再次感慨这前人伟大的发明,无敌的回调设计,当然,使用回调方法也有弊端,个人认为大量使用匿名内部类将降低代码的可读性,用的时候需要慎重,不过,贵在用之方便,依旧强调它的特点--恰到好处。


浙公网安备 33010602011771号