【问题标题】:How to get listener of a view如何获取视图的侦听器
【发布时间】:2013-01-14 03:13:46
【问题描述】:

我编写与其他应用程序交互的服务。它在已经有监听器的视图(按钮、文本视图、...)上注册监听器。我需要用我自己的听众(作品)替换它们,做一些事情,然后注销我的听众并恢复旧的。

  1. 一个带有 onClickListener 按钮的应用正在运行
  2. 我的服务在 UI-Thread 中注册了一个 onClickListener + 做一些事情
  3. 我的服务恢复了旧的监听器

如果有 view.getOnClickListener 方法,那就很容易了。然后我可以保存旧的并在完成后替换新的监听器。

有没有办法从一个视图中获取侦听器,或者将多个相同类型的侦听器绑定到一个视图?

Button btn = (Button) findViewById(R.id.btn1);
btn.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {
            //do something
        }
    });
// I need to do, but found no solution for that.
View.OnClickListener oldListener = btn.getOnClickListener();

如果我将新侦听器注册到视图,旧的侦听器会被覆盖,对吗?如果两个侦听器(“新”和“旧”)同时存在也可以。只有旧的不能消失。

编辑:不幸的是,我无法在分配时保存侦听器。我需要从视图组件中获取它。

谢谢

【问题讨论】:

  • 你不能通过 setOnClickListener 方法设置你的旧监听器吗? setOnClickListener(old_listener);
  • 不,因为除了服务“控制/挂钩(通过检测)”的应用程序小部件之外,我什么都不知道。所以我无法在分配旧监听器时保存它,因为我没有应用程序的 src 代码。使用仪器,我可以获得所有小部件、按钮等,但我还需要当前活动的侦听器。希望这能让问题更清楚。
  • 嗨朋友,你做到了吗?我想做同样的事情@dr.AtZe

标签: android listener


【解决方案1】:

感谢 mihail 的提示(感谢 :)))使用 hidden API,我找到了在分配后让听众回来的解决方案:

android.view.View 类有一个嵌套类static class ListenerInfo,它将所有侦听器存储在一个视图上(API 14+)。在旧版本中,侦听器是 android.view.View 中的私有字段。

可以通过反射访问该字段。就我而言(API 14+),

// get the nested class `android.view.View$ListenerInfo`
Field listenerInfoField = null;
listenerInfoField = Class.forName("android.view.View").getDeclaredField("mListenerInfo");
if (listenerInfoField != null) {
    listenerInfoField.setAccessible(true);
}
Object myLiObject = null;
myLiObject = listenerInfoField.get(myViewObj);

// get the field mOnClickListener, that holds the listener and cast it to a listener
Field listenerField = null;
listenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener")
if (listenerField != null && myLiObject != null) {
    View.OnClickListener myListener = (View.OnClickListener) listenerField.get(myLiObject);
}

在那段代码之后(我错过了很多try-catch-blocks),myListener 对象保存了onClickListener 的实例,该实例之前已匿名声明给视图。它也适用于任何其他侦听器,只需将“mOnClickListener 参数”替换为您在反射中需要的参数并正确投射即可。

请注意,即将发布的版本中的代码更改可能会使其不再起作用。

在这里找到最终教程:http://andwise.net/?p=161

【讨论】:

  • 确保在分配 listenerField 后调用 listenerField.setAccessible(true) 否则它将不起作用!
【解决方案2】:

创建实现OnClickListener的类

public static class MyClickListener1 implements OnClickListener{
    Activity mActivity;
    MyClickListener1(Acivity activity){
      mActivity=activity;
    }
    @Override
    public void onClick(View v) {
            //do something
    }
}

public static class MyClickListener2 implements OnClickListener{
    @Override
    public void onClick(View v) {
            //do something
    }
}

您可以在您的代码中轻松使用它们:

btn.setOnClickListener(new MyClickListener1(this));
btn.setOnClickListener(new MyClickListener2());

或者您可以创建实例并重用它们:

OnClickListener listener1 = new MyClickListener1(this);
OnClickListener listener2 = new MyClickListener2();
btn.setOnClickListener(listener1);
btn.setOnClickListener(listener2);

您还可以定义一个构造函数来传递这些类中您需要的任何内容。我通常会通过MyClickListener1中的活动

编辑:如果你想在按钮中有像对象一样的监听器,你可以使用标签。

btn.setTag(listener1);
btn.setOnClickListener(listener1);

然后去使用它

OnClickListener old_listener = (OnClickListenr)btn.getTag();

【讨论】:

  • 我错过了自定义侦听器仍然用它替换旧侦听器。所以在你的例子中listener2 总是替换listener1。如果旧的仍然存在,那就太好了,因为我对分配第一个侦听器的源代码没有影响。我只能通过仪器获得视图。因此,当我没有在创建时保存它时,不存在找回听众的“魔法”?
  • 这很酷,不知道 :) 但不幸的是,它在我的情况下不起作用,我无法在分配时保存侦听器。我需要从视图组件中向后获取它。我可以看到 mListenerInfo 属性设置为调试模式,但无权访问:(当将侦听器分配给按钮时(我无法保存),我需要在分配后取回侦听器。这就是棘手的一点。我的服务必须在不同的应用程序上工作(无需修改代码),这就是为什么我不能保存分配的监听器或将其存储在标签中。
  • 我发现了关于访问隐藏 API 的有趣文章:devmaze.wordpress.com/2011/01/18/…。希望这可以帮助,我现在没有时间,但它看起来真的很有趣。
  • 隐藏的 API 是一个很好的提示,找到了解决方案。我会发布它并将其标记为答案。非常感谢,你帮了我很多,mihail
【解决方案3】:

创建两个 OnCLickListener 实例并将第一个或第二个分配给按钮:

    Button b = (Button) findViewById(R.id.Button1);

    OnClickListener listener_new =  new OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("APP", "NEW CLICK LISTENER ACTIVE");
        }
    };

    OnClickListener listener_old =  new OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("APP", "OLD CLICK LISTENER ACTIVE");
        }
    };

    //setting listener
    b.setOnClickListener(listener_old);
    b.callOnClick();

            //changing listener
    b.setOnClickListener(listener_new);
    b.callOnClick();

            //return your old listener!
        b.setOnClickListener(listener_old);
        b.callOnClick();

添加:

OnClickListener 是 Button 类的受保护字段,继承自 View 类。字段名称“mOnClickListener”。即使通过反射我也无法得到它。

void getListener(Button b) {
java.lang.reflect.Field field =  getClass().getSuperclass().getSuperclass().getDeclaredField("mOnClickListener");
}

因此,如果您无权访问创建按钮的代码,则无法获取按钮的现有侦听器。

但是,如果您可以访问 Activity 的对象(我们知道您可以访问,因为为按钮设置了新的侦听器),您可以在该 Activity 上添加您的按钮和您的侦听器。使现有按钮不可见。而不是在必要时回滚。

【讨论】:

  • 谢谢,但这对我不起作用,因为我无法访问分配了侦听器的应用程序。我需要来自后面的听众和仪器。有了它,我可以获得按钮、视图等,但我还需要他们的听众在检测后恢复它们。否则,当我的检测完成后,应用程序将不再可控。
【解决方案4】:
public abstract class ReflectionUtils {

    private static final String listenerInfoFieldName = "mListenerInfo";
    private static final String onCLickListenerFieldName = "mOnClickListener";

    public static OnClickListener getOnClickListener(View view){
        Object listenerInfo = ReflectionUtils.getValueFromObject(view, listenerInfoFieldName, Object.class);
        return ReflectionUtils.getValueFromObject(listenerInfo, onCLickListenerFieldName, View.OnClickListener.class);
    }

    public static <T> T getValueFromObject(Object object, String fieldName, Class<T> returnClazz){
        return getValueFromObject(object, object.getClass(), fieldName, returnClazz);
    }

    private static <T> T getValueFromObject(Object object, Class<?> declaredFieldClass, String fieldName, Class<T> returnClazz){
        try {
            Field field = declaredFieldClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            Object value = field.get(object);
            return returnClazz.cast(value);
        } catch (NoSuchFieldException e) {
            Class<?> superClass = declaredFieldClass.getSuperclass();
            if(superClass != null){
                return getValueFromObject(object, superClass, fieldName, returnClazz);
            }
        } catch (IllegalAccessException e) {
        }
        return null;
    }

}

myView 上调用OnClickListener onClickListener = ReflectionUtils.getOnClickListener(myView); 将为您提供您正在寻找的myView 的听众。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 2011-06-01
    相关资源
    最近更新 更多