点击事件未触发 | Android

59

我用一个活动和一个布局创建了一个非常简单的测试应用程序。第一次按下 onClick 时,它不会触发,而它应该。

这是活动:

package com.example.mytest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText ed1 = (EditText) findViewById(R.id.editText1);

        ed1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                Toast.makeText(getApplicationContext(), "1", Toast.LENGTH_LONG)
                        .show();

            }

        });
    }

}
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:ems="10" >

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/editText2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/editText1"
        android:ems="10" />

</RelativeLayout>

如果你运行这个应用程序,并点击第二个editText,然后再回到第一个editText,它将不会触发onClick。你可以一直来回选择,但根本不会触发onClick。我需要这个基本功能,但一直没有想出让它工作的方法。有什么想法吗?

注意

我已经在我的API级别16的物理设备和API级别8的模拟器上尝试了所有推荐的方法,但是我遇到了相同的问题。

澄清

当焦点在editText1上并被点击时,onClick方法会触发。如果editText2受到关注,然后点击editText1,它不会触发。非常棘手的问题。


这是什么:xmlns:tools="http://schemas.android.com/tools" - mainu
你只是显示toast吗?尝试sysout..有时候toast不会显示,可能是由于键盘输入的影响。 - Youddh
我尝试了 system.out,但它的效果与 toast 完全相同。 - EGHDK
1
tools 命名空间用于新的 (r20) 开发者工具链。它是应用程序项目模板的一部分。 - Sparky
9个回答

218
概述:当用户与任何UI组件交互时,各种监听器按自上而下的顺序调用。 如果较高优先级的监听器“消耗了事件”,那么较低的监听器将不会被调用

在你的情况下,这三个监听器按顺序被调用:

  1. OnTouchListener
  2. OnFocusChangeListener
  3. OnClickListener

第一次用户触摸EditText时,它接收焦点以便用户可以输入。 此处消耗了动作。 因此,较低优先级的OnClickListener不会被调用。每次连续的触摸不会改变焦点,因此这些事件会传递到OnClickListener。

按钮(和其他此类组件)不会从触摸事件中接收焦点,这就是为什么每次都会调用OnClickListener。

基本上,你有三个选择:

  1. 只实现一个OnTouchListener:

     ed1.setOnTouchListener(new OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {
             if(MotionEvent.ACTION_UP == event.getAction())
                 editTextClicked(); // Instead of your Toast
             return false;
         }
     });
    

    每次触摸EditText时,此代码将被执行。请注意,监听器返回false,这允许事件向下传递到内置的OnFocusChangeListener,它将更改焦点以便用户可以在EditText中输入。

    同时实现一个OnFocusChangeListener和OnClickListener:

  2.  ed1.setOnFocusChangeListener(new OnFocusChangeListener() {
         @Override
         public void onFocusChange(View v, boolean hasFocus) {
             if(hasFocus)
                 editTextClicked(); // Instead of your Toast
         }
     });
    

    当焦点改变时,此侦听器会捕获第一个触摸事件,而您的OnClickListener会捕获所有其他事件。

    (这不是一个有效的答案,但这是一个很好的技巧要知道。)在XML中将focusable属性设置为false

     android:focusable="false"
    

    现在每次点击时,OnClickListener都会触发。但这使得EditText变得无用,因为用户无法再输入任何文本...

注意:

getApplicationContext()可能导致内存泄漏。一个好的习惯是避免使用它,除非绝对必要。你可以安全地使用v.getContext()代替。


#2对我的使用情况很有帮助 - 我需要我的自定义相对布局按钮同时处理onClick和_onBlur_。请注意,如果有人有这种使用情况,则您的onClick不需要执行任何操作,因为onFocusChange会处理both the onClick (when hasFocus = true) and onBlur (when hasFocus = false)。如果您正在使用相同类型的视图,请确保将 android:focusable =“true”android:focusableInTouchMode =“true”设置为真。谢谢,Sam。 - h-bomb
3
我有一个带有android:textIsSelectable="true"属性的TextView。如果我需要监听OnClick()事件,同时用户也可以选择TextView中的文本,该怎么办呢? - Shadab Ansari
顺便问一下,@Sam,你记得在文档中看到这个内容吗?还是从经验和调试中得出的结论? - stan0
1
我通过反复试验、调试才艰难地学会了这个。 - Sam
你能解释一下getApplicationContext()如何创建内存泄漏吗?getApplicationContext()不是返回一个具有应用程序范围的Context单例实例吗?虽然有点离题,但任何澄清都将不胜感激!谢谢! - ONE
1
第三种解决方案对我很有用,因为我想让用户选择一个日期(并在之后查看它),但只能通过单击EditText弹出的DatePickerDialog来实现。这使用户可以看到该字段是可编辑的,但只允许他们像预期的那样通过日期选择器更改它。非常感谢! - Adrian Wiik

7

我可能来迟了,但是这里有一个代码片段可以解决onClick()无法被调用的问题:

ed1.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP && !v.hasFocus()) {
            // onClick() is not called when the EditText doesn't have focus,
            // onFocusChange() is called instead, which might have a different 
            // meaning. This condition calls onClick() when click was performed
            // but wasn't reported. Condition can be extended for v.isClickable()
            // or v.isEnabled() if needed. Returning false means that everything
            // else behaves as before.
            v.performClick();
        }
        return false;
    }
});

5

使编辑文本可点击... 在XML中使用android:clickable="true", 或在代码中实现。

ed1.setClickable(true);

然后执行。
ed1.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
      Toast.makeText(getBaseContext(), "1",Toast.LENGTH_SHORT).show();
    }
  });

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="fill_parent"     android:layout_height="fill_parent"     android:orientation="vertical">    <TextView         android:layout_width="fill_parent"         android:layout_height="wrap_content"         android:text="@string/hello" /> </LinearLayout> 这是默认的内容。 - mainu
setClickable(true) 是我的问题。并不是所有的视图默认都设置为 true。我没有使用按钮,而是使用了一个通常不可点击的视图。 - Mike Miller
@EGHDK @mainu xmlns:tools 不会影响部署后的行为,请参考 http://tools.android.com/tech-docs/tools-attributes 和 http://tools.android.com/tips/layout-designtime-attributes。 - TWiStErRob
setOnClickListener 使任何视图可点击,参见代码。@MikeMiller - TWiStErRob
显示剩余3条评论

5
这是因为第一个点击会将焦点放在视图上。接下来的点击会触发单击事件。
如果您正在动态地填充视图,请按照以下步骤进行:
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false"

如果这不起作用,尝试在父视图上应用它。


1
这是与日期选择器一起工作的最简单方法。
private DatePickerDialog datePickerDialog;
EditText etJoiningDate;
etJoiningDate=(EditText)findViewById(R.id.etJoiningDate);

etJoiningDate.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:

                final Calendar cldr = Calendar.getInstance();
                int day = cldr.get(Calendar.DAY_OF_MONTH);
                int month = cldr.get(Calendar.MONTH);
                int year = cldr.get(Calendar.YEAR);
                // date picker dialog
                datePickerDialog = new DatePickerDialog(TestActivity.this,
                        new DatePickerDialog.OnDateSetListener() {
                            @Override
                            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                                etJoiningDate.setText(dayOfMonth + "/" + (monthOfYear + 1) + "/" + year);
                            }
                        }, year, month, day);
                datePickerDialog.show();

                break;
        }


        return false;
    }


});

0
public class TestProject extends Activity implements OnClickListener {
TextView txtmsg;
EditText ed1, ed2;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    txtmsg = (TextView) findViewById(R.id.txtmsg);
    ed1 = (EditText) findViewById(R.id.edt1);
    ed2 = (EditText) findViewById(R.id.edt2);

    ed1.setOnClickListener(this);
    ed2.setOnClickListener(this);
}

@Override
public void onClick(View v) {


    if(v==ed1){
        txtmsg.setText("1");
        Toast.makeText(this, "first",Toast.LENGTH_SHORT).show();
    }
    if(v==ed2){
        txtmsg.setText("2");
        Toast.makeText(this, "second",Toast.LENGTH_SHORT).show();
    }

}

}

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <EditText
        android:id="@+id/edt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:ems="10" />

    <EditText
        android:id="@+id/edt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/edt1"
        android:layout_marginTop="14dp"
        android:ems="10" />

    <TextView
        android:id="@+id/txtmsg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/edt2"
        android:layout_below="@+id/edt2"
        android:layout_marginRight="22dp"
        android:layout_marginTop="160dp"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

0
有一次我遇到了这个问题,花了我一分钟才弄明白。我的ImageButton设置了setOnClickListeneronClick,但似乎没有反应,后来我发现它实际上被覆盖在xml布局的另一个元素下面了,所以我把它改成了:
 <RelativeLayout>
     <ImageButton>
     <LinearLayout></LinearLayout>
 </RelativeLayout>

转换成这个:

 <RelativeLayout>
     <LinearLayout></LinearLayout>
     <ImageButton>
 </RelativeLayout>

突然之间,ImageButton 不再被其他布局覆盖,因为它现在是后添加到父布局中的,并且现在处于顶部并且每次都有效。祝好运,当基本的东西突然停止工作时总是很有趣。


0
避免使用FocusChangeListener,因为当你不需要它时(例如进入一个activity时),它会表现得不稳定。只需设置一个OnTouchListener以及OnClickListener,像这样:
@Override
public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            view.requestFocus();
            break;
    }
    return false;
}

这将使您的EditText首先获得焦点,并且您的onClick在第一次正常工作。


0

简单、可重用的 Kotlin 解决方案

我首先使用了两个自定义扩展函数:

val MotionEvent.up get() = action == MotionEvent.ACTION_UP

fun MotionEvent.isIn(view: View): Boolean {
    val rect = Rect(view.left, view.top, view.right, view.bottom)
    return rect.contains((view.left + x).toInt(), (view.top + y).toInt())
}

然后监听Edittext上的触摸事件。只有在初始的ACTION_DOWN事件最初在Edittext上并且仍然在Edittext上时,才会触发此事件。

myEdittext.setOnTouchListener { view, motionEvent ->
    if (motionEvent.up && motionEvent.isIn(view)) {
        // Talk your action here
    }
    false
}

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