看了hyman老师的“明日之星-RecyclerView”后决心弄清楚Java回调机制到底是怎么执行的。
在学习的过程中hyman老师利用到了回调机制对RecyclerView来进行onClick()与onlongClick()功能的补充,详细的来说是利用View组件的onClick()加上自己的回调机制来实现了RecyclerView组件的点击与长点击功能的实现。
下面我讲贴出三段代码来对java回调机制来进行详细的说明。
首先,我觉得很有必要回忆一下接口的有关方面的知识。
- 当接口被某个类继承时,这个类必须实现这个接口里面的所有的方法。
- 接口里面的方法都隐式的设为public abstract,而且接口里面的变量也都隐式的设置为常量也就是final static
- 接口可以被作为参数在类与方法之间传递。
- (按我自己的理解)其实接口是一种更加纯粹的抽象类。
第一段代码简单的利用java接口来讲解最基本的回调:
- 创建接口,接口名字:interface ShowPrice
- 创建类来实现接口ShowPrice
- 用接口变量调用自己的方法将会调用实现接口的类的实现接口的方法;
public interface Showprice{
//当接口被实现时,这个方法必须被实现
//这个i作为商品的数量
void showprice(int i);
}
public Class Goods implements ShowPrice{
public int price;
public Goods(int price){
this.price=price;
}
public void showprice(int i){
System.out.println("所买商品价格一共是:"+price*i);
}
}
public Class Test{
public static void main(String[] args){
//一共买5件商品,每件商品的价格是10元
ShowPrice s=new Goods(10);
s.showprice(5);
//执行结果输错是“所买商品价格一共是:50”
}
}
在这里我们可以看到,我们在main方法里面将new Good(10);新建的引用赋值给了接口变量
ShowPrice s;
然后我们用接口变量s调用自己的showprice()方法,但是在这个时候是直接输出了值,也就是接口变量s直接调用了实现了这个接口的类的里面的实现接口的方法showprice();
上述代码是一个简单的接口回调的例子,接下来让我们来仔细看一看Android里面的一个重要组件View给自己设置监听器,onClick()方法,并利用Java回调机制来实现的例子;
————you call me ,i will call back。
这是我引用网上的一句话,其他的概念我也不再赘述,有兴趣的可以去看看夏安明老师的CSDN关于Java回调机制打概念的详细介绍,但是在这里我必须引用到一个网上概念步骤来帮助大家更加容易的理解View的onClick()事件的回调机制;
- Class A实现接口CallBack callback。-背景1
- Class A中包含一个Class B的引用b。-背景2
- Class B有一个参数为callback的方法f(CallBack callBack)。-A类调用B类的某个方法C
然后b就可以在f(CallBack callback)方法中调用A的方法。-B类调用A类某个方法D
这个概念步骤对Java回调机制的理解尤为重要,每当利用Java回调或者分析代码的回调机制的时候,这个概念步骤就要出现在眼前。
引用一下夏安明老师的代码。同时附上夏安明老师关于Java回调CSDN博客地址
http://blog.csdn.net/xiaanming/article/details/8703708
//这个是View的一个回调接口
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
package com.example.demoactivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/**
* 这个就相当于Class A
* 实现了 OnClickListener接口---->背景一
*/
public class MainActivity extends Activity implements OnClickListener{
/**
* Class A 包含Class B的引用----->背景二
*/
private Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button1);
//class B的引用b
/**
* Class A 调用View的方法,而Button extends View----->A类调用B类的某个方法 C
*/
button.setOnClickListener(this);
}
/**
* 用户点击Button时调用的回调函数,你可以做你要做的事
* 这里我做的是用Toast提示OnClick
*/
@Override
public void onClick(View v) {
Toast.makeText(getApplication(), "OnClick", Toast.LENGTH_LONG).show();
}
}
/**
* 这个View就相当于B类
* @author xiaanming
*
*/
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected OnClickListener mOnClickListener;
/**
* setOnClickListener()的参数是OnClickListener接口------>背景三
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
/**
* Call this view's OnClickListener, if it is defined.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//这个不就是相当于B类调用A类的某个方法D,这个D就是所谓的回调方法
mOnClickListener.onClick(this);
return true;
}
return false;
}
}
在这里我们来对上述的概念步骤对View的监听回调机制来进行对比
- 第一步:MainActivity相当于Class A实现了接口OnClickListener
- 第二步:MainActicity中包含View(Button)相当于Class B的一个引用就是button
- 第三步:View(Button)中有一个参数类型为OnClickListener的方法setOnClickListener(),在这个方法中当在MainActivity类中给button注册监听器也就是button调用setOnClickListener()方法时,将接口变量作为参数传递过去,并且在方法中将传过去的参数赋给View本身申明的全局接口变量mOnClickListener;
- 第四步:当用户点击按钮时,在View类的performClick()方法中就会触发mOnClickListener.onClick();这个地方也就是接口回调,回调了MainActivity中的onClick()方法,Toast开始显示。
接下来我们来看看hyman老师利用回调机制的例子:
package com.example.administrator.myandroid_recyclerview;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
*这里MainActivity相当于Class A
/
public class MainActivity extends ActionBarActivity {
private RecyclerView mRecyclerView;
private List<String> mDatas;
private SimpleAdapter mAdapter;//mAdapter相当于Class B的引用b
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatas();
initViews();
mAdapter = new SimpleAdapter(this, mDatas);
mRecyclerView.setAdapter(mAdapter);
//设置RecyclerView的布局管理
LinearLayoutManager linearLayoutManager =
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(linearLayoutManager);
// 设置RecyclerView的Item分隔线
//mRecyclerView.addItemDecoration(
// new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter.setOnItemClickListener(new SimpleAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "click" + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(MainActivity.this, "Longclick" + position, Toast.LENGTH_SHORT).show();
}
});
}
private void initViews() {
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerView);
}
private void initDatas() {
mDatas = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++) {
mDatas.add((char) i + "");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case R.id.action_list_view:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
break;
case R.id.action_grid_view:
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
break;
case R.id.action_staggered_grid_view:
mRecyclerView.setLayoutManager(new
StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));
break;
case R.id.action_staggered_view:
Intent intent = new Intent(MainActivity.this, StaggeredGridLayoutActivity.class);
startActivity(intent);
break;
//addDatas()方法写在SimpleAdapter类中体现的是用适配器修改数据的思想
case R.id.action_menu_add:
mAdapter.addDatas(1);
break;
case R.id.action_menu_delete:
mAdapter.deleteDatas(1);
break;
}
return super.onOptionsItemSelected(item);
}
}
package com.example.administrator.myandroid_recyclerview;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
/**
* Created by Administrator on 2015/7/3.
*
*/
public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.MyViewHolder> {
private LayoutInflater mInflater;
private Context context;
protected List<String> mDatas;
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public SimpleAdapter(Context context, List<String> mDatas) {
this.context = context;
this.mDatas = mDatas;
mInflater = LayoutInflater.from(context);
}
//把接口变量OnItemClickListener作为参数的ClassB中的方法
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
@Override
public int getItemCount() {
return mDatas.size();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = mInflater.inflate(R.layout.list_item, viewGroup, false);
MyViewHolder holder = new MyViewHolder(itemView);
return holder;
}
@Override
//在这个方法中对Class A中的方法进行回调,这里需要注意的是这里将回调系列方法块写在了一个setUpItemEvent()方法中
public void onBindViewHolder(final MyViewHolder myViewHolder, final int i) {
myViewHolder.tv.setText(mDatas.get(i));
//这个地方设置!=null是为了防止MainActivity并没有给mAdapter设置监听器
setUpItemEvent(myViewHolder);
}
protected void setUpItemEvent(final MyViewHolder myViewHolder) {
if (onItemClickListener != null) {
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = myViewHolder.getLayoutPosition();
onItemClickListener.onItemClick(myViewHolder.tv, pos);
}
});
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = myViewHolder.getLayoutPosition();
onItemClickListener.onItemLongClick(myViewHolder.tv, pos);
return false;
}
});
}
}
//这里是在添加完数据后调用的是notifyItemInsert(int position);
//notifyItemInsert不会去刷新所有的View不会去重置position
public void addDatas(int position) {
mDatas.add(position, "Insert One");
notifyItemInserted(position);
}
//这里是在删除完数据后调用的的是notifyItemRemoved(int position)
public void deleteDatas(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.id_listitem);
}
}
}
不用说,这里Class A是MainActivity ,Class B是simpleAdapter
我们依然用上述的概念步骤对代码进行分析:
- 第一步:MainActivity实现了写在SimpleAdapter内部的接口OnItemClickListener;
- 第二步:MainActivity中包含了SimplerAdapter的引用mAdapter;
- 第三步:当MainActivity中的mAdapter调用其类中的setOnItemClickListener()方法时,将接口变量OnClckListener作为参数传递过去,并且将值赋给了SimpleAdapter中的全局接口变量onItemClickListener;
- 第四步:当每个itemView回调onClick()的时候(就是用户点击item是),这个时候onClick()中的代码得到执行,也回调了onItemClick()方法,以及onItemLongClick()方法;
在这里我不得不说hyman老师的代码的确写的很赞!!
在这里关于代码的几个比较特殊的地方我还是想指出来给大家分享一下,如果有错误,请大家留言指出:
首选是hyman老师没有用MainActivity直接去implements OnItemClickListener接口,而是直接MainAcitivity中的setOnItemClick()方法的括号中new OnItemClickListener(),转而将引用直接作为参数传递,我想这里的new 和implements后的this有同样的功效吧。‘
还有一点就是hyman老师的接口回调代码执行时写在重载的onBindViewHolder()里面的,在onBindViewHolder()方法里面给每一个item注册点击监听,我想的确在RecyclerView强制使用ViewHolder后,数据更新都是在onBindViewHolder()里面执行的。
在View类里面和hyman老师代码里面都有在回调之前加一个判断条件if(OnClickListener!=null)的条件判断,我觉得这个地方是为了防止用户并未给组件注册监听来采取的方法。