如何通过编程触发Onclick事件?

27

我有一个自定义视图,其中包含两个线性布局:第一个是视图的标题,第二个是详细视图。

在自定义视图中,标题线性布局的OnClickListener已经被定义:当它触发时,它会折叠/展开第二个线性布局。

我想要做的是在标题的OnClickListener事件中添加更多功能(例如:折叠/展开第二个布局并显示Toast)。

我无法修改自定义视图的源代码。我尝试设置新的OnClickListener,但它隐藏了初始事件(折叠/展开)。

我应该如何实现这个问题?

My Custom View的源代码:

public class ExpandoLayout extends ViewGroup
{
    /* some declarations */
    private Linearlayout header;
    private linearlayout footer;

    /* some code */
    @override
    protected void onFinishInflate() {
    header= new LinearLayout(context);
    header.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                toggleExpand();
            }
        });
    }
}

我想做的是在我的活动中为已定义的OnClickListener事件添加一些代码。 就像这样:

public class myActivity extends Activity {
private Linearlayout myCustomView;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.rsdetail);
    myCustomView= (MyCustomView) findViewById(R.id.expanded);

    myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            if(v instanceof LinearLayout)
            {
                v.performClick();

                Toast.makeText(getActivity(), "ExpandoOnClickListener", 2000).show();
            }
        }
    });
}

1
请提供代码...... - Dheeresh Singh
请参考@waqaslam的答案。 - Halil
4个回答

88

您可以使用编程方式触发视图上的点击事件,以调用其OnClickListener,如下所示:

view.performClick();

如果你在第一个布局的OnClickListener下调用这个方法,并将其应用于第二个布局,那么它应该会起作用。


1
当我这样做时,它会给我一个06-05 10:29:59.470: E/AndroidRuntime(7518): java.lang.StackOverflowError。 - Zakaria
你能否发布完整的Logcat异常日志吗? - waqaslam
你是否在调用 myCustomView.performClick()?如果是,那么你需要改为调用 myCustomView.getChildAt(0).performClick() - waqaslam
06-05 10:58:10.440: E/AndroidRuntime(8044): 致命异常:主要 06-05 10:58:10.440: E/AndroidRuntime(8044): java.lang.StackOverflowError 06-05 10:58:10.440: E/AndroidRuntime(8044): 在java.util.HashMap.containsKey(HashMap.java:330) 06-05 10:58:10.440: E/AndroidRuntime(8044): 中断了 06-05 10:58:10.440: E/AndroidRuntime(8044): android.provider.Settings$NameValueCache.getString(Settings.java:599) 06-05 10:58:10.440: E/AndroidRuntime(8044): android.provider.Settings$System.getString(Settings.java:731) 06-05 10:58:10.440: E/AndroidRuntime(8044): android.provider.Settings$System.getInt(Settings.java:780) - Zakaria
expandedCustomer = (ExpandoLayout) viewr.findViewById(R.id.expandedClient); expandedCustomer.getChildAt(0).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(v instanceof LinearLayout) { expandedCustomer.getChildAt(0).performClick(); Toast.makeText(getActivity(), "ExpandoOnClickListener", 2000).show(); } } }); - Zakaria
你正在调用的方式一定会崩溃,因为它在一个连续的循环中调用自身 - 就像黑洞一样 **:)**。不要在 OnCLickListener 中使用 getChildAt(0) 上的 performCLick,而应该是第二个布局或 getChildAt(1)(如果有的话)。 - waqaslam

10

简单的解决方案是获取原始OnClickListener,然后在新的OnClickListener中触发它:

final OnClickListener preDefinedListener = myCustomView.getChildAt(0).getOnClickListner();

myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        if(v instanceof LinearLayout)
        {
            preDefinedListener.onClick(v); // calls default (defined by MyCustomView)

            Toast.makeText(getActivity(), "ExpandoOnClickListener", 2000).show();
        }
    }
});
遗憾的是,View没有getOnClickListner()方法,但我猜你可以使用反射来获取它。它存储在mOnClickListener 字段中(源代码)。
以下是如何获取布局定义的OnClickListener的方法:
OnClickListener tmpOnClickListener = null;
try {
    Class<View> cls = (Class<View>) Class.forName("android.view.View");
    Field fld = cls.getDeclaredField("mOnClickListener");
    fld.setAccessible(true); // because it is protected

    tmpOnClickListener = (OnClickListener) fld.get(myCustomView.getChildAt(0));

    fld.setAccessible(false); // restore it's original property
} catch (SecurityException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (IllegalArgumentException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} 

final OnClickListener preDefinedListener = tmpOnClickListener;

if (preDefinedListener != null) {
    myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View paramView) {
            preDefinedListener.onClick(paramView);

            Toast.makeText(getActivity(), "ExpandoOnClickListener", Toast.LENGTH_LONG).show();
        }
});

我没有真正地去处理所有的异常,但是这已经足够让你理解了。它看起来可能有点杂乱,但实际上只需要新增5行代码就能解决你的问题。


非常感谢 Vladimir :) 它对我很有效。 我只是想问一个问题:反射在内存和/或处理器方面是否具有扩展性? - Zakaria
2
没问题 =) 通过反射获取字段并调用方法比“正常”方式更昂贵,但在这种情况下,差异是如此微小,不会影响任何事情。然而,有一个警告 - 由于该字段是通过名称访问的,如果其名称更改(这可能不会发生),则此代码将停止工作 - 它不会崩溃,但会抛出NoSuchFieldException,并且preDefinedListener最终将为null,这将使布局保持监听器设置不变。 - Vladimir

9
自SDK 15以来,您可以直接调用以下方法:
view.callOnClick()

0

如果您无法修改CustomView的代码,则有以下选项。

  • 扩展CustomView。
  • 覆盖onClick()方法并将其绑定为第一个布局的监听器。
  • 在onClick()中,首先调用super.onClick(),然后在下面添加您的附加功能。

这样,您就可以保留现有的代码并添加额外的功能。


1
OnClickListener事件被定义为匿名监听器。我该如何覆盖它? - Zakaria

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接