一、前期基础知识储备
(1)面向接口编程
面向接口编程是面向对象编程中的一个很重要的概念,即“接口变量存放实现该接口的类的对象的引用,从而接口变量就可以回调类中实现的接口方法”。
(2)方法的调用和方法的实现
Java中所有的方法会出现在两个地方——方法的调用和方法的实现,而且在Java中两者是分开的,即方法的实现写在一个地方,方法的调用写在另一个地方。比如:
A类中创建set()方法和具体实现,public void set(String s){//方法的具体实现};
B类中传入参数,调用set()方法,a.set(“Android Cool”);
接口回调中,接口方法的实现和接口方法的调用就是分开的。
(3)接口回调/回调函数
回调函数 不是 由该函数的实现方直接调用,而是在特定的事件或条件发生时由另一方调用,用于对该事件或条件进行响应。即接口方法的调用和接口方法的实现在不同的地方。(定义接口的地方往往和接口方法调用的地方在一块)。
(4)接口回调的结果
接口回调的结果:接口变量调用被实现的接口的方法。
二、上代码,具体实现
接口回调的实例中通常会有:
两个类:一个类实现接口和实现该接口的方法,即完成接口方法的定义;另一个类中定义接口和接口中的方法,并且提供注册接口的方法和接口变量调用被实现的接口的方法;
两个类+一个接口:具体步骤和上面一样,只不过接口的定义不在放在类中,而是单独抽离出来。
(1)《Android群英传》自定义View,为自定义View设立点击事件
自定义View类中:
①定义点击接口和接口中的方法;
public interface OnTopBarClickListener {
// 左按钮点击事件
void leftClick();
// 右按钮点击事件
void rightClick();
}
②创建注册接口的方法,暴露接口给调用者;
private OnTopBarClickListener mListener;
public void setOnTopBarClickListener(OnTopBarClickListener listener) {
mListener = listener;
}
③接口变量调用被实现的接口方法;
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.leftClick();
}
}
});
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.rightClick();
}
}
});
主Activity类中:
以匿名类的形式注册接口,实现接口中的方法
mTopBar.setOnTopBarClickListener(new TopBar.OnTopBarClickListener() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this, "点击了 返回", Toast.LENGTH_SHORT).show();
}
@Override
public void rightClick() {
Toast.makeText(MainActivity.this, "点击了 更多", Toast.LENGTH_SHORT).show();
}
});
点击左右按钮,可以看见弹出对应的toast。
分析:注册接口的方法至关重要,它关联起调用接口方法的类(自定义View类)和实现接口方法的类(Activity类),注意看以下注册接口的代码:
public void setOnTopBarClickListener(OnTopBarClickListener listener),里面传入的形参是接口变量,但是Activity类中注册接口时是以匿名内部类的完成的,即
mTopBar.setOnTopBarClickListener(new TopBar.OnTopBarClickListener() {}):
注册接口方法里面里面传入的实参是接口的实例对象,从这里就体现出面向接口编程的中要义——接口变量存放实现该接口的类的对象的引用,从而接口变量就可以回调类中实现的接口方法。即,
public voidsetOnTopBarClickListener(OnTopBarClickListener listener) {
mListener = listener;
}
这里的接口变量mListener存储了实现接口的类的对象的引用,即mListener持有实现了接口的匿名内部类的引用,所以mListener就可以直接调用该匿名内部类中实现的接口方法,即是
mListener.leftClick();
mListener.rightClick();
最后看一下按钮点击事件发生时,代码的执行顺序:
1)首先是两个按钮的点击事件的onClick()方法;
2)在onClick()方法内部调用mListener.leftClick()和mListener.rightClick();
3)最后是Activity中的leftClick()和rightClick()方法,结果为弹出不同的弹窗。
(2)RecyclerView中的适配器Adapter相关的点击事件
①创建独立接口;
public interface IPhoneEvent {
void setPhoneBitmap(int position, Bitmap bitmap);
}
②主Activity类中实现接口和实现接口中的方法;
public class PhoneActivity extends BaseEditFragment implements IPhoneEvent{
...
@Override
public void setPhoneBitmap(int position, Bitmap bitmap) {
if (position == 0) {
if (isAdded()) {
Log.d(TAG, "setPhoneBitmap: ---");
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.phone2);
mFreePuzzleView.setPhoneBitmap(position, mBitmap);
showBtnCommit();
}
} else {
mFreePuzzleView.setPhoneBitmap(position, bitmap);
showBtnCommit();
}
}}
③适配器Adapter类中创建接口变量和调用被实现的接口的方法;
imageHodler.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bmp = BitmapFactory.decodeResource(v.getResources(), mPhoneList.get(position).getImageId());
mIPhoneEvent.setPhoneBitmap(position, bmp);
Log.d(TAG, "onBindViewHolder onClick: ");
}
});
分析:这里的接口方法需要传入参数position,获取RecyclerView子Item的位置数据,所以接口方法的调用是写在了onBindViewHolder()中。注意,在这里接口的实现是直接在外部Activity进行实现的,而不是以匿名内部类的形式实现,所以这里没有出现注册接口的方法,那么是如何初始化接口变量呢?
通过适配器类的构造方法,即
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
imageHodler.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bmp = BitmapFactory.decodeResource(v.getResources(), mPhoneList.get(position).getImageId());
mIPhoneEvent.setPhoneBitmap(position, bmp);
Log.d(TAG, "onBindViewHolder onClick: ");
}
});
}
分析:结合以上两个例子,我们可以看出整个接口回调代码的起点就是在——接口变量调用被实现的接口方法,该方法的调用在第一个自定义View的例子中是由按钮的点击事件发起的;在第二个例子中是由RecyclerView的子ItemView被点击时发起的。
接口回调是面向接口编程的一个很重要的运用,往往平时中很多地方都会用到,但不能说出所以然,所以通过本篇文章的分析,希望可以抛砖引玉。
最后在延伸一点知识:接口变量的实例化。
我们都知道,接口变量直接调用接口方法,然后实现接口和实现接口变量的地方写出方法的具体实现,这样两段代码分工明确,这也是面向接口编程的核心思想,如上面代码中适配器中定义接口、接口方法、接口变量,并且使用接口变量直接调用接口方法,而适配器接口方法的实现放在了Activity中。
这里面还有一点关键的知识点,就是接口变量的初始化:“接口的引用指向他的实例化对象”,即可通过实例化实现接口的类的方式实例化一个接口如:MyListener mListener = new XXXX(); XXXX即为实现MyListener接口的类,这里就是通过实例化实现接口的类的方式实现一个接口变量的初始化。