浅析Java匿名内部类在Android事件响应中的应用

在阅读本文之前,你首先要对Java匿名内部类和android 事件分发机制有一定的了解。还不了解这些知识的童鞋请先学习这部分知识。
可参考的相关书籍:

  1. Java核心技术 卷I (6.4节内部类)
  2. Android开发艺术探索 (第3、4章 View 事件体系和 view的工作原理)

好了,本文介绍内容包括两方面:对匿名内部类的理解和分析匿名内部类在Android中的使用。
匿名内部类的语法在Java中很是奇特,它将局部内部类的使用深入了一步。思想是:假如只创建这个类的一个对象,就不必命名了。Java核心技术上给出的实例如下:

public void start(int interval, final boolean beep)
{   // 匿名内部类的具体使用
    ActionListener listener = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
            Date now = new Date();
            System.out.println("At the tone, the time is " + now);
            if(beep) Toolkit.getDefaultToolkit().beep();
        }
    };
    Timer t = new Timer(interval, listener);
    t.start();
}

我们不分析这段代码的具体逻辑是什么,这里只关注匿名内部类的使用形式:

ActionListener listener = new ActionListener() {
    public void actionPerformed(ActionEvent event) {
        Date now = new Date();
        System.out.println("At the tone, the time is " + now);
        if(beep) Toolkit.getDefaultToolkit().beep();
    }
};

不要被这段代码的形式吓到了,它其实只是创建了一个对象,并把这个对象赋值给了listener。奇特的地方是这个ActionListener并不是类,而是一个接口,接口里面定义了一个要实现的actionPerformed(ActionEvent event)方法,如下所示:

public interface ActionListener {
    void actionPerformed(ActionEvent event)
}

接着上面说,listener = 后面的代码的含义是:创建一个实现了ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在括号 {} 内。不知道看到这你是不是稍微清楚些了,下面给一个小李子帮助大家理解,小李子给大家演示的是 如何在不创建具体类的条件下调用一个接口中定义的方法
测试环境:AndroidStudio,手机真机调试
首先,创建一个简单的Android工程,名字叫做MyAnonymousInnerClassDemo, 里面很简单只有两个文件MainActivity.java和接口MyCustomInterface.java
MyCustomInterface.java里面很简单定义了一个方法

package shi.whu.edu.cn.myanonymousinnerclassdemo;

/**
 * Created by SHI on 2016/7/24.
 */
public interface MyCustomInterface {
    void run();
}

MainActivity.java 也很简单我们把代码直接写在onCreate方法中,这样app运行起来我们的代码就得到了执行。

package shi.whu.edu.cn.myanonymousinnerclassdemo;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 我现在不想新建类实现MyCustomInterface接口,但是想调用接口定义的run方法
        // 怎么办呢?....思考。。。。可以用匿名内部类
        MyCustomInterface listener = new MyCustomInterface() {
            @Override
            public void run() {
                String string = "我是接口定义函数的具体实现,你可以自己在这里定义想做的事情^_^";
                Toast.makeText(MainActivity.this, string, Toast.LENGTH_SHORT).show();
            }
        };
        // 调用接口定义的方法啦,方法里面代码逻辑是我自己写的,我想干啥就干啥!!!
        listener.run();
    }
}

很简单,运行结果就打印一句话
这里写图片描述

接下来,介绍下Android系统组件的事件监听函数是怎么使用匿名内部类。
上面调用好像不怎么牛,相当于调用了一个方法嘛,是这样的,但是这个方法是实现了MyCustomInterface接口的匿名内部类对象的方法,没有通过new一个具体类就得到了一个对象是不是很奇特,我们还调用了这个对象的方法,打印了一条消息。Android中通常的用法是在控件(如Button)点击等事件的调用上。
我们在上面的代码后面再加一段简单的代码,定义一个button的点击处理事件

findViewById(R.id.my_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.out.println("点击了发送按钮");
            }
        });

在activity_main.xml的布局中添加一个Button,id为my_button。这个很显然用的是上面我们讲到的匿名内部类,运行的效果大家很容易知道,同样打印了一句话“点击了发送按钮”。
这里写图片描述
这是怎么做到的呢?
这里用到了一些Android中View事件传递和分发机制的知识,具体的过程大家可以查阅相关的资料,这里只对匿名内部类中实现的方法是怎么被系统调用的作简单的描述。
结合着源码来看,首先看一下setOnclickListener做了什么

/**
     * 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(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

可以看到,通过匿名内部类创建的对象被赋值给了getListenerInfo().mOnClickListener,getListenerInfo()又是什么呢?接着看

    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

它返回的是ListenerInfo类型的对象mListenerInfo,我们的匿名内部类对象被赋值给了mListenerInfo.mOnClickListener。这里可以猜测一下,系统最后肯定是通过mListenerInfo.mOnClickListener调用了我们实现的onClick(View view)方法。

系统怎么样把事件一层层传递下来,最终被my_button消费掉比较复杂这里不详细分析,我们只关注最终的结果就是调用了view的performClick()方法

/**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            // 注意这里,调用了li.mOnClickListener.onClick(this)方法
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

从上面可以清楚的看到在performClick()中将mListenerInfo 对象赋值给ListenerInfo 的对象li,接着调用了 li.mOnClickListener.onClick(this); 注意 li.mOnClickListener不正是我们通过setOnclickListener方法设置的内部类对象嘛,所以就调用了我们在内部类中实现的onClick方法了。

到这里终于清楚了,为什么我们写的点击事件的处理逻辑能够在适当的时刻被执行了,通过将创建的匿名内部类对象传给系统定义好的变量,系统在恰当的时候调用了我们传过去的匿名内部类对象的方法。

由于技术水平有限,文中难免会出现错误,欢迎大家批评指正。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值