(转)从零开始的回调
来源:互联网 发布:软件职业技能培训学校 编辑:程序博客网 时间:2024/04/29 11:11
原地址:https://innofang.github.io/2017/03/08/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84%E5%9B%9E%E8%B0%83/
关于回调,先讲个例子
假如现在你有一家鞋店,生意十分红火,每天来买鞋的客人络绎不绝。为了照顾生意,你招聘了一名店员——小王来帮忙。那么现在,有一名客人挑中了一双鞋,但是这时货架上符合客人的鞋码没有了,所以你打算叫小王去库房去符合客人鞋码的鞋子过来,然后你继续去招呼其他客人。
好,例子讲完了。下面分析一下这个例子,我们有几个主体:
- 主体一:老板( 你 )
- 主体二:店员(小王)
接下来分析一下这个例子的主要事件,
- 老板叫店员去做事
这个做事可以是去库房取鞋,也可以是打杂等工作,也就是说,这个老板叫店员去做的事是完全可以抽象出来
那么老板,店员,叫店员做事之间就大致可以用下面的关系描述
上面这个例子就是一个生活中十分常见的回调过程:
老板
调用小王
的工作
方法,小王
再反过来调用老板
让他具体做的事
,其中老板叫店员具体做的事就是一个回调方法
其实这个过程更通俗的讲,就是老板叫了小王一声,小王跑过来看看老板有什么吩咐,然后根据吩咐去做事
那么经过这个简单但是常见的例子过后,就可以用代码具体实现一下这个过程了
回调的简单实现
根据上面对例子的分析,首先需要将这个 叫店员去做的事 抽象出来作为一个接口,这里就直接将接口命名为CallBack
1234
// 这是一个叫店员做事的接口public interface CallBack {void call();}
然后创建Boss
类,并实现该接口
12345678910111213141516171819202122
public class Boss implements CallBack {private Wang wang;public Boss(Wang wang){this.wang = wang;}public void doThings(){for (int i = 0; i < 10; i++) {System.out.println("招呼客人");if (i == 5) { // 没鞋了,叫小王去拿鞋wang.doWork(this);}}}public void call() {System.out.println("小王去库房拿鞋");}}
那么对于店员 小王
类的实现就更简单了,只需要照着老板说的做就好了
123456
public class Wang {public void doWork(CallBack callBack) {// 回调老板让做的事情callBack.call();}}
测试的话,只需要分别new出各自的对象,并调用老板那个招呼客人的方法就可以了
1234567
public class Test {public static void main(String[] args) {Wang wang = new Wang();Boss boss = new Boss(wang);boss.doThings();}}
为什么需要回调?
上面讲了什么是回调以及回调的简单实现,但是如果你是第一次接触这个,可能就会问为什么需要回调?不要可以吗?
对于这个问题,还是拿上面鞋店的例子来解释好了,如果你不采用回调就有可能面临这样的窘境:
现在来了一个客人,他挑中了一双鞋,但是现在这个鞋码的鞋在鞋架上已经没有了,这时你叫小王到库房去找这类鞋码的鞋,但是你不知道他拿到了鞋子没有,这时,你就要主动去库房问小王,你找到鞋了吗?小王回复说,还在找。然后你回去招呼其他客人,但是过了一会,你又去库房问小王有没有找到鞋,小王回复说,快了。又过了一会儿,你又去问,小王又回复:马上就找到了… …,经过几次询问小王终于找到了鞋,然后你把鞋拿来再去给客人穿。
你会发现这中处理事情的过程其实是不妥的,或许当你拿到鞋的那一刻,客人都已经走了。那么回调就是用来解决这个问题的
利用回调,老板只需要叫小王自己去找即可,自己继续去招呼其他客人,当小王找到鞋过后,他就会自己把鞋拿过来,完全不需要你自己去询问然后自己把鞋子拿来,你可以继续去执行你自己的逻辑,两边都不会耽误
回调的应用
上面讲了很多,下面就来看看在实际开发当中如何使用回调
案例一:给RecyclerView的item添加点击事件
因为本文主要是讲解如何使用回调,所以这里就不解释如何使用RecyclerView了
在平常使用RecyclerView的时候,我们发现其实RecyclerView是没有给item设置点击事件的,但是我们肯定是需要这个功能的,那么怎么办呢?其实这个点击事件是可以利用回调实现的
下面直接上代码
123456789101112131415161718192021222324252627282930313233343536373839404142
public class ClickAdapter extends RecyclerView.Adapter<ClickAdapter.ViewHolder>{// . . .private OnClickItemListener mOnClickItemListener;/*设置接口实例*/public void setOnClickItemListener(OnClickItemListener onClickItemListener) {mOnClickItemListener = onClickItemListener;}/*加载RecyclrView的item视图*/public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { . . . }/*绑定视图*/public void onBindViewHolder(ViewHolder holder, int position) { . . . }/*获得item个数*/public int getItemCount() { . . . }/*ViewHolder逻辑*/public class ViewHolder extends RecyclerView.ViewHolder{// ...// 视图控件初始化public ViewHolder(View itemView) { . . . }/*绑定视图方法,在Adapter的onBindViewHolder中调用*/public void bindHolder(final String text) {mTextView.setText(text);if (null != mOnClickItemListener) {itemView.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {/*回调方法*/mOnClickItemListener.onClick(text);}});}}}/*回调接口*/interface OnClickItemListener{void onClick(String text);}}
代码很长,但是大部分都是关于RecyclerView的Adapter和ViewHolder的编写,符合点击事件回调的逻辑其实并不长,所以,在上面代码中,只需要关注下面几点就可以了
private OnClickItemListener mOnClickItemListener;
接口实例setOnClickItemListener(OnClickItemListener onClickItemListener)
设置接口实例- ViewHolder 中的 bindHolder (在这里接收一个从Adapter中onBindViewHolder中传来的String参数,用于设置视图内容,这一点也可以不看)
OnClickItemListener
回调接口
可以看到,这里将接口写在Adapter的内部,因为大部分情况,接口点击需要参数都不一样,所以一般都会写在Adapter的内部
所以如果除去关于RecyclerView的Adapter和ViewHolder的编写,那么剩下的逻辑无非就是:
- 创建内部接口
- 定义接口实例
- 设置接口实例的setter方法
- 在点击事件中进行回调
如果你在疑惑为什么要在itemView的点击事件中进行回调,那么可以这么想,itemView其实是一个子item布局加载后产生的view,view都是有默认点击事件的,但是这个点击事件无法传递我们想要的参数,所以我们就可以借助这个itemView的点击事件,在该点击事件中进行回调
如果想要使用这个回调,那么就可以想之前给Button设置点击事件那样使用这个回调
123456789101112131415161718
List<String> strings = new ArrayList<>();/*实例化Adapter*/ClickAdapter adapter = new ClickAdapter(this, strings);/*数据初始化*/for (int i = 1; i <= 100; i++) {strings.add("Hello, I am " + i);}/*初始化RecyclerView*/RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.setAdapter(adapter);/*为RecyclerView的item设置点击事件*/adapter.setOnClickItemListener(new ClickAdapter.OnClickItemListener() {public void onClick(String text) {Toast.makeText(ClickRecyclerViewActivity.this, text, Toast.LENGTH_SHORT).show();}});
测试运行,点击第二个item,效果如下:
案例二:在子线程中加载网络图片并显示在IamgeView中
在平常的开发中,加载网络图片要在子线程中操作是常识,但是当加载完图片资源过后,返回的bitmap如何传递给主线程中的ImageView就成了一个问题,关于这个问题,这里提出两个解决办法:
- 方法一:使用
runOnUiThread()
,这个方法在Activity中,所以如果是在工具类中使用的话,就需要传递一个Activity实例 - 方法二:使用回调
这里还是只讲如何使用回调,同样的,先上代码:
1234567891011121314151617181920212223242526272829
public class ImageUtil {public static void loadeIamge(final String url, final LoadeImageListener listener) {new Thread(new Runnable() {public void run() {InputStream is = null;BufferedInputStream bis = null;try {HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();connection.setConnectTimeout(5000);connection.connect();is = connection.getInputStream();bis = new BufferedInputStream(is);listener.onLoadeImageListener(BitmapFactory.decodeStream(bis));} catch (Exception e) {e.printStackTrace();}finally {CloseUtil.closeQuietly(is);CloseUtil.closeQuietly(bis);}}}).start();}public interface LoadeImageListener {void onLoadeImageListener(Bitmap bitmap);}}
跟案例一进行对比,可以发现这个案例的逻辑和代码相较案例一更简洁
上面的代码就是一个加载网络图片的工具类,可以发现在这里,没有定义接口实例,也没有定义接口实例的setter方法,而是直接用调用者那里传来的接口实例进行操作
通过Android自带的HttpURLConnection获取url的内容,然后转化为bitmap,当这一过程完成过后,就可以将biatmap回调给调用者了
下面来看看调用者的实现:
12345678
final ImageView imageView = (ImageView) findViewById(R.id.image_view);ImageUtil.loadeIamge(IMG_URL, new ImageUtil.LoadeImageListener() {public void onLoadeImageListener(Bitmap bitmap) {imageView.setImageBitmap(bitmap);}});
IMG_URL 是一个静态字符串常量,这里直接调用了ImageUtil的loadeImage方法,采用了匿名内部类的方式将接口的具体是实现传递给了loadeImage,并且利用回调过来的bitmap直接设置给imageView,整个代码看起来更加的简洁。
效果图就不上啦~~
需要源代码请看这里 –> CallBackDemo
END.
- (转)从零开始的回调
- 【转】从零开始讲述编译的基本过程
- 从零开始搭建安卓EasyAr环境(三) 识别成功后的回调
- 从零开始的学习
- 从零开始的高龄程序员
- martyr的从零开始。
- 从零开始的blog
- 从零开始的JAVA学习
- 从零开始的JAVA【一】
- 从零开始的c++
- 从零开始的web前端
- 从零开始的深度学习
- 从零开始的IT生活
- Android的从零开始
- (转)基于IAR的STM32工程从零开始建立
- 虚函数:从零开始(转)
- 软件测试从零开始 [转]
- 转:软件测试 从零开始
- 数据传递在UI中的作用
- Linux程序编译+GDB调试
- $.ajax 提交表单
- ORMLite使用中出现的问题及解决办法
- C++ this指针解析
- (转)从零开始的回调
- vmware ubuntu上网
- linux一些常用iptables防火墙规则整理收集
- 知道后/中序遍历,求前
- ubuntu自动启动ssh server
- HDU
- PCA学习总结
- js中日期格式正则和两日期相差时间计算
- 关于原创文章的发布技巧以及平台