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) {
                    //在界面对结果进行处理。如传入并构建列表适配器
                }
            };

再次感慨这前人伟大的发明,无敌的回调设计,当然,使用回调方法也有弊端,个人认为大量使用匿名内部类将降低代码的可读性,用的时候需要慎重,不过,贵在用之方便,依旧强调它的特点--恰到好处

原文地址:https://www.cnblogs.com/changewu/p/3229089.html