Android中的接口回调详解,回调机制:以Activity和Adapter传递数据为例。

首先解决啥是回调:

我觉得这个例子比较好:某天,我打电话向你请教问题,当然是个难题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。


以Activity和Adapter为例:

我们知道,Activity中的ListVIew的为界面层,其数据要通过放到适配器中去,才能展示。Activity的通过各种逻辑(不论是模拟数据,还是网络加载的数据)得到的数据,一般是通过适配器的构造方法来传递的。这样的话,适配器拿到数据就去一一适配展示了。但是,如果activity想要去拿到adapter的数据呢?比如:我想拿到ListView的每条item的数据(或者某个控件或者item的位置id),想想怎么拿呢?------------------这时候就不好拿了是吧?



下面会上一个Demo,Demo中简单的实现我想通过点击每个item的某个控件那个这个item的位置id。


Adapter的代码:

package com.example.adaptercallbackdemo;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;

/**
 * Created by Administrator on 2017/10/19.
 */

public class MyAdapter extends BaseAdapter {
    private Context mContext;
    private List<String> mList;
    private LayoutInflater mLayoutInflater;
    private OnClickMyTextView mOnClickMyTextView;
    //构造函数接收MainActivity传递过来的数据
    public MyAdapter(Context context, List<String> list) {
        mLayoutInflater = LayoutInflater.from(context);
        this.mContext = context;
        this.mList = list;
    }
    @Override
    public int getCount() {
        return mList.size();
    }
    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder mViewHolder = null;
        if (convertView == null) {
            mViewHolder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.list_item, null);
            mViewHolder.mTextView = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(mViewHolder);
        } else {
            mViewHolder = (ViewHolder) convertView.getTag();
        }
        mViewHolder.mTextView.setText(mList.get(position).toString());	
//这里便是知道难题答案的地方,setOnClickListener即点击了item的控件TextView,
点击了就能拿到id撒,但是这个时候我要告诉Activity啊,就有了接口回调。
	if (mOnClickMyTextView != null){
            mViewHolder.mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
  
  
//想到问题的答案的地方。其实这里的mOnClickMyTextView是传来的接口对象,接口就是一种规范,这里的
myTextViewClick
函数
和activity处的
myTextViewClick
都要遵守这个规范(即指向同一个接口对象),才能回调成功。
                    mOnClickMyTextView.myTextViewClick(position);
               }            
	});        
	}        
	return convertView;    
   
public class ViewHolder {
   
   
       	TextView mTextView;   
 }    
public interface OnClickMyTextView {
   
   //创建一个接口类       
 	void myTextViewClick(int id);//创建一个回调函数,实例化接口的时候就要具体化这个回调函数,即要有函数体    
}    
//注册函数    
public void setOnClickMyTextView(OnClickMyTextView onClickMyTextView){   
     this.mOnClickMyTextView = onClickMyTextView;    
}
}
 
 
 
 
Activity的代码:
public class MainActivity extends AppCompatActivity {
    private ListView mListView;//声明ListView控件来展示数据
    private ArrayList<String> mlist;//列表存放数据
    private MyAdapter myAdapter;//自定义的adapter

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        initData();
        myAdapter = new MyAdapter(MainActivity.this, mlist);//将数据传到适配器中去
        mListView.setAdapter(myAdapter);

        //接口的调用,获取传递的id
	//其实想想,下面的代码在OnCreate方法中调用,必定会执行setOnClickMyTextView方法(里面的
myTextViewClick暂时不会执行),(相当于我Activity在问你Adapter,这个第几个位置啊?即这个的id是多少啊?
这时没点击某一个item,我怎么告诉你id啊,就是上面的遇到难题要思考一下),那就先思考着吧)
	//调用了此方法就表示有人联系你了,注册到这来了
        myAdapter.setOnClickMyTextView(new MyAdapter.OnClickMyTextView() {
            @Override
            public void myTextViewClick(int id) {
                String string = mlist.get(id);
                Toast.makeText(MainActivity
                        .this, mlist.get(id), Toast.LENGTH_LONG).show();
            }
        });
    }
    //列表模拟数据
    private void initData() {
        mlist = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            mlist.add("这是第" + i);
        }
    }
}

以上各个关键的地方都有代码注释,结合了文章开头的那个例子给出注释。
那个例子说明了“异步+回调”的编程模式。其中,我问完你问题之后,我们在各干各的事,我不用在那一直等你,可以去干别的事,这边是“异步”的过程。
你后来打手机告诉我结果便是一个“回调”过程;我的手机号码必须在以前告诉你,这便是注册回调函数;我的手机号码应该有效并且手机能够接收到你的呼叫,
这是回调函数必须符合接口规范。


对回调的深入思考:

程序的本质就是代码跳转,不管同步异步反射接口虚函数,本质上都是函数调用。函数我们要调用它,就需要它的指针,不同语言通过不同的方式来得到这个指针。而我们定义的接口其实就是一个函数指针,那么那个注册过程,其实就是相当于对那个函数指针赋值。通过这个函数指针来调用我们定义的自实现函数。


文中例子来自:http://blog.csdn.net/a78270528/article/details/46918601




猜你喜欢

转载自blog.csdn.net/sunbinkang/article/details/78290010