接口回调-监听器
设置监听,点击之后做响应,监听发生在事件之前
# 1. 大白话理解
我(AClass)给你打电话问某个难题,你(BClass)电话里想不出来,我又不能拿着电话在那里傻等(可能我AClass还在处理别的事)。于是我们约定:等你(BClass)想到了再回我电话(回调)。我接着做其他事。
于是接口回调机制比喻模型如下:
AClass这个类实现了一个接口里的方法doSomething
,然后注册到BClass中,然后AClass就去做别的事情去了,BClass在某个触发的时机回头来调用AClass类中的doSomething的方法。
上述的过程我们可以抽象出三个角色:
- Interface: 定义接口及接口中的方法,打电话问问题这种行为
- AClass: 实现接口中定义的方法,向B提问,等待B回复结果
- **BClass:**定义接口,提供注册接口的方法 和 接口变量调用被实现的接口的方法;成功解决,告诉A结果;无法解决,告知原因
# 2. 进一步抽象理解
我们将上面的图再抽象出来
基于以上打电话的例子,我们可以抽象出监听的处理模型
在事件监听处理的模型中,主要涉及三个类:
- **事件源-Event Source:**事件发生的地方,通常是各个组件上,如Button、进度条等
- **事件-Event:**事件封装了组件上发生的特定事件(如点击事件)
- **事件监听器-Event Listener:**负责监听事件源发生的事件,并对各种事件做出相应的响应
当用户按下一个按钮或者单击某个菜单项时,这些动作就会激发一个相应的事件,该事件就会触发事件源上注册的事件监听器(特殊的Java对象),事件监听器调用对应的事件处理器(事件监听器里的实例方法)来做出相应的响应。
Android的事件处理机制是一种委派式(Delegation)事件处理方式:普通组件(事件源)将整个事件处理委托给特定的对象(事件监听器);当该事件源发生指定的事件时,就通知所委托的事件监听器,由事件监听器来处理这个事件。
每个组件均可以针对特定的事件指定一个事件监听器,每个事件监听器也可监听一个或多个事件源。因为同一个事件源上可能发生多种事件,委派式事件处理方式可以把事件源上所有可能发生的事件分别授权给不同的事件监听器来处理;同时也可以让一类事件都使用同一个事件监听器来处理。
- 事件源:问问题
- 事件:打电话问问题
- 事件监听器:A等B恢复,B能否解除问题都会给A打电话
# 3.Demo案例
# 案例一:网络请求数据
接口
public interface OnDataListener {
// 回调成功
void onDataSuccess(String data);
// 回调失败
void onDataFailed(String error);
}
2
3
4
5
6
B Class负责请求数据,请求成功或失败都会给A反馈
public class MyAsyncTask extends AsyncTask<Void, Void, String> {
// 声明接口,为的是调用接口中的方法
private OnDataListener listener;
int responseCode = 0;
// 接口Set方法,接口声明是私有的,提供给外部调用
public void setOnDataListener(OnDataListener asyncResponse) {
this.listener = asyncResponse;
}
@Override
protected String doInBackground(Void... voids) {
// 请求网络数据
return responseData;
}
@Override
protected void onPostExecute(String msg) {
super.onPostExecute(msg);
if (responseCode == 200) {
// 任务成功完成时执行此方法
listener.onDataSuccess(msg);
} else {
// 任务执行失败执行
listener.onDataFailed("网络无法请求" + responseCode);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
AClass 获取网络数据
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onClick(View v) {
// 提交异步任务
MyAsyncTask asyncTask = new MyAsyncTask(sb.toString());
asyncTask.execute();
// 回调接口
asyncTask.setOnDataListener(new OnDataListener() {
// 执行成功回调
@Override
public void onDataSuccess(String data) {
// TODO
}
// 执行失败回调
@Override
public void onDataFailed(String error) {
// TODO
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 小结
编写回调的步骤:
- 创建这个接口
- 定义接口内部的方法
- 提供设置接口的方法(其实是外部实现)
- 接口方法的调用
在MyAsyncTask中声明接口,提供Setter()
方法供Activity或者Fragment来调用
当MyAsyncTask中的数据请求成功后,Activity或者Fragment通过匿名方法来回调接口,获取MyAsyncTask的返回值
myAsyncTask.setOnDataListener(new OnDataListener() {}
# 4. 接口回调
# 接口回调是什么
接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。
在Android开发中我们很多地方都用到了方法的回调,回调就是把方法的定义和功能导入实现分开的一种机制。目的是为了解耦他的本质是基于观察者设计模式,即观察者设计模式的的简化版
例如:在下载时候的进度回调,在Adapter与Activity之间的回调,在JavaBean和Fragment以及Activity之间的回调等等
回调的目的主要有两个:
- 是传递数据
- 保持数据的同步更新。
常用的有两种形式:
- 使用内部类的形式,得到接口的子类对象
- 直接实现定义的接口。
# 接口回调的分类
1. Android组件封装的回调使用
在回调接口中,得到外部类的当前对象MainActivity.this;
代码给按钮加了一个事件监听器,这其实就是“回调”最常见的应用场景之一。我们自己不会显式地去调用onClick
方法。用户触发了该按钮的点击事件后,它会由Android系统来自动调用。
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.download_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 此处传递的this为监听器接口OnClickListener的当前对象
// 要得到外部类的对象,则为MainActivity.this
Toast.makeText(MainActivity.this,"Toast",Toast.LENGTH_SHORT);
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2. 自定义接口的回调,见第三章两个Demo
回调过程如下:
# 5. 接口回调的典型应用
RecyclerView item点击事件
RecyclerView是将数据以列表的形式给出的,对于列表的每个Item我们可能需要通过点击跳转到另外的Activity查看该Item的详细信息,这个时候就需要对RecyclerView设置点击事件。
对RecyclerView设置点击事件的方式有两种,一种是直接在onBindViewHolder函数中,对Item绑定点击事件,但是这种方式增加了程序的耦合性,我们希望用户能够自己实现点击事件的处理函数。因此我们通过在适配器中设置一个包含onItemClick函数的接口,然后添加setOnItemClickListener方法用于接收用户创建的该接口的实现类对象,最后在onBindViewHolder函数中调用onItemClick方法执行用于传入的对象的onItemClick方法。
第一步:创建OnItemClickListener接口
public interface OnItemClickListener {
void onItemClick(int position);
}
2
3
第二步:添加setOnItemClickListener方法
public void setOnItemClickListener(OnItemClickListener listenser) {
this.listener = listenser;
}
2
3
第三步:调用setOnItemClickListener方法传入OnItemClickListener的实现类对象。
adapter.setOnItemClickListener(new PersonAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Log.e("Main", "" + position + "被点击了!!");
}
});
2
3
4
5
6
第四步:在onBindViewHolder中调用实现类对象的onItemClick函数。
@Override
public void onBindViewHolder(@NonNull PersonViewHolder holder, final int position) {
holder.bindData(items.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
Log.e("click", "!");
listener.onItemClick(position);
}
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
Adapter内声明接口完整代码
RecyclerBaseAdapter
public abstract class RecyclerBaseAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
* 【编写回调的步骤】
* 1. 创建这个接口
* 2. 定义接口内部的方法
* 3. 提供设置接口的方法(其实是外部实现)
* 4. 接口方法的调用
*/
// 1.创建接口
public interface OnItemClickListener {
void onItemClick(int position);
}
// 2.设置item的监听事件(定义接口内部的方法)
public void setOnItemClickListener(OnItemClickListener listener) {
// 设置监听,即设置回调接口
this.mOnItemClickListener = listener;
}
// 3. 提供设置接口的方法(其实是外部实现)
private OnItemClickListener mOnItemClickListener;
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(position);
Log.e("TAG", "BaseAdapter click");
}
}
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Avtivity内
// ListView、GridView、StaggerView 的item 点击事件
adapter.setOnItemClickListener(new RecyclerBaseAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Log.e("TAG", "MainActivity click");
Toast.makeText(MainActivity.this, "点击了第" + position + "个条目", Toast.LENGTH_SHORT).show();
}
});
2
3
4
5
6
7
8