Android:如何处理按钮点击


95

我在非Java和非Android领域拥有扎实的经验,正在学习Android。

我对不同领域有很多困惑,其中之一就是如何处理按钮单击。至少有4种方式(!!!),此处简要列出

为了保持一致性,我将列出它们:

  1. View.OnClickListener在活动中具有该类的成员,并将其分配给将处理活动方法中的onClick逻辑的实例onCreate

  2. 在“ onCreate”活动方法中创建“ onClickListener”,然后使用setOnClickListener将其分配给按钮

  3. 在活动本身中实现“ onClickListener”,并将“ this”分配为按钮的侦听器。对于活动很少的按钮的情况,应分析按钮ID以对适当的按钮执行“ onClick”处理程序

  4. 在实现“ onClick”逻辑的活动上具有公共方法,并将其分配给活动xml声明中的按钮

问题1:

这些都是方法吗,还有其他选择吗?(我不需要其他,只是好奇)

对我来说,最直观的方法就是最新的方法:它要求键入的代码量最少,并且可读性最高(至少对我而言)。

不过,我看不到这种方法被广泛使用。使用它有什么缺点?

问题2:

这些方法的优点/缺点是什么?请分享您的经验或一个好的链接。

欢迎任何反馈!

PS我已经尝试过Google并为此主题找到一些东西,但是我发现的唯一内容就是描述“如何”做到这一点,而不是为什么它是好是坏。

Answers:


147

问题1:不幸的是,您所说的最直观的是Android中使用最少的一个。据我了解,您应该将UI(XML)和计算功能(Java类文件)分开。它还使调试更加容易。通过这种方式阅读并思考Android imo实际上要容易得多。

问题2:我相信主要使用的两个是#2和#3。我将以Button clickButton为例。

2

是匿名类的形式。

Button clickButton = (Button) findViewById(R.id.clickButton);
clickButton.setOnClickListener( new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                ***Do what you want with the click here***
            }
        });

这是我的最爱,因为它在使用findViewById设置按钮变量的位置旁边具有onClick方法。处理此clickButton按钮视图的所有内容都位于此处,看起来非常整洁。

我的同事评论的一个缺点是,假设您有许多需要onclick侦听器的视图。您会看到onCreate的长度将非常长。因此,他为什么喜欢使用:

3

假设您有5个clickButton:

确保您的活动/片段实现了OnClickListener

// in OnCreate

Button mClickButton1 = (Button)findViewById(R.id.clickButton1);
mClickButton1.setOnClickListener(this);
Button mClickButton2 = (Button)findViewById(R.id.clickButton2);
mClickButton2.setOnClickListener(this);
Button mClickButton3 = (Button)findViewById(R.id.clickButton3);
mClickButton3.setOnClickListener(this);
Button mClickButton4 = (Button)findViewById(R.id.clickButton4);
mClickButton4.setOnClickListener(this);
Button mClickButton5 = (Button)findViewById(R.id.clickButton5);
mClickButton5.setOnClickListener(this);


// somewhere else in your code

public void onClick(View v) {
    switch (v.getId()) {
        case  R.id.clickButton1: {
            // do something for button 1 click
            break;
        }

        case R.id.clickButton2: {
            // do something for button 2 click
            break;
        }

        //.... etc
    }
}

正如我的同事所解释的那样,这种方式在他眼中更加整洁,因为所有onClick计算都在一个地方进行处理,而不会占用onCreate方法。但是我看到的缺点是:

  1. 看自己
  2. 并且onClick方法使用的onCreate中可能位于的其他任何对象都必须放入字段中。

让我知道您是否需要更多信息。我没有完全回答您的问题,因为这是一个很长的问题。如果我找到一些站点,我将扩大答案,现在,我只是在提供一些经验。


1
对于选项2,您需要使用它:clickButton.setOnClickListener(new View.OnClickListener(){@Override public void onClick(View v){//要做的事情}}); 帮助其解决OnClickListener
ColossalChris

选项3可能是最干净,最容易扩展的MVP模式。
Raffaeu

选项2仍然可以产生onCreate()不太长的时间。点击侦听器分配和匿名类可以分解为单独的帮助器方法,该方法从中调用onCreate()
尼克·阿列克谢夫

@巨无霸:您不必这样做。向Activity类添加扩展,例如“ Implements View.OnClickListener”。
TomeeNS

10

#1当布局上的按钮没有生成(但显然是静态的)时,我经常使用最后一个。

如果您在实践中和业务应用程序中使用它,请在此处格外注意,因为当您使用像ProGuard这样的源混淆器时,您需要在活动中将这些方法标记为不会混淆。

要使用这种方法归档某种编译时安全性,请查看Android Lint示例)。


#2 所有方法的利弊几乎相同,因此,课程应为:

使用最合适或最直观的方法。

如果必须将其分配OnClickListener给多个按钮实例,请将其保存在类范围(#1)中。如果您需要一个简单的Button侦听器,请进行匿名实现:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // Take action.
    }
});

我倾向于OnClickListener在活动中不实施,这有时会引起一些混乱(尤其是当您实施多个其他事件处理程序并且没人知道this正在做什么时)。


我遵循相同的方法,但仍未获得函数的输出,我的代码和查询在这里:stackoverflow.com/questions/25107427/…–
Rocket

8

我更喜欢选项4,但是对我来说这很直观,因为我在Grails,Groovy和JavaFX中做了大量的工作。视图和控制器之间的“魔术”连接是共有的。正确命名方法很重要:

在视图中,将onClick方法添加到按钮或其他小部件:

    android:clickable="true"
    android:onClick="onButtonClickCancel"

然后在类中,处理方法:

public void onButtonClickCancel(View view) {
    Toast.makeText(this, "Cancel pressed", Toast.LENGTH_LONG).show();
}

再次,清楚地命名该方法,无论如何您都应该做,维护就变成了第二自然。

一个很大的优点是您现在可以为该方法编写单元测试。选项1可以做到这一点,但是2和3则更困难。


1
我将稍作讨论,并建议第五个选项(不,不是布鲁斯·威利斯(Bruce Willis)主演)。它使自动测试变得更加容易。看看这个链接,更好的信息:codelabs.developers.google.com/codelabs/android-testing/...
史蒂夫格尔曼

4

最常用的方法是匿名声明

    Button send = (Button) findViewById(R.id.buttonSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // handle click
        }
    });

另外,您可以创建View.OnClickListener对象,然后将其设置为button,但是例如,您仍然需要覆盖onClick方法。

View.OnClickListener listener = new View.OnClickListener(){
     @Override
        public void onClick(View v) {
            // handle click
        }
}   
Button send = (Button) findViewById(R.id.buttonSend);
send.setOnClickListener(listener);

当您的活动实现OnClickListener接口时,您必须在活动级别覆盖onClick(View v)方法。然后,您可以将此活动作为按钮的侦听器,因为它已经实现了接口并覆盖了onClick()方法。

public class MyActivity extends Activity implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // handle click
    }


    @Override
    public void onCreate(Bundle b) {
        Button send = (Button) findViewById(R.id.buttonSend);
        send.setOnClickListener(this);
    }

}

(imho)第四种方法,当多个按钮具有相同的处理程序时,可以在活动类中声明一个方法,并将此方法分配给xml布局中的多个按钮,也可以为一个按钮创建一个方法,但是在这种情况下,宁愿在活动类中声明处理程序。


1

选项1和2涉及使用内部类,这将使代码变得混乱。选项2有点混乱,因为每个按钮都有一个侦听器。如果您的按钮数量很少,可以。对于选项4,我认为这将较难调试,因为您将不得不返回第四和xml和Java代码。当我不得不处理多个按钮单击时,我个人使用选项3。


1

我的示例,在Android Studio 2.1中进行了测试

在xml布局中定义按钮

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Java脉动检测

Button clickButton = (Button) findViewById(R.id.btn1);
if (clickButton != null) {
    clickButton.setOnClickListener( new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            /***Do what you want with the click here***/
        }
    });
}

1

为了使问题2陈述得更容易,您可以使用像这样的lambda方法来节省变量内存并避免在视图类中上下移动

//method 1
findViewById(R.id.buttonSend).setOnClickListener(v -> {
          // handle click
});

但是如果您希望通过一种方法将click事件一次应用于按钮。

您可以使用@D的问题3。回答。但不要忘记使用来实现您的视图类View.OnClickListener

在其他方面正确使用问题3


1
这应该被认为是与方法参考IMO相结合的现代答案。其他大多数答案并没有说明它们是Android上Java8之前的旧代码。
Ryan The Leach

0

问题1-这些是处理视图点击的唯一方法。

问题2-
选项1 /选项4-选项1和选项4之间没有太大区别。我看到的唯一区别是,在一种情况下,活动是实现OnClickListener,而在另一种情况下,则存在匿名实现。

选项2-在此方法中,将生成一个匿名类。该方法有点麻烦,因为如果您有多个按钮,则需要多次执行。对于匿名类,您必须小心处理内存泄漏。

选项#3-不过,这是一种简单的方法。通常,程序员在编写代码之前尽量不要使用任何方法,因此该方法并未得到广泛使用。您会看到大多数人使用Option#4。因为它在代码方面更干净。


嗨,高拉夫,谢谢你的回答。但是,您能否在此阐明您的意思:对于匿名类,您必须小心处理内存泄漏。内存泄漏如何来到这里?
布达

您只需要注意:如果在方法的内部创建一个匿名类,该方法在应用程序的生命周期内可能多次被调用,则不会创建一个类的多个实例,而是会创建多个类,包括它们的实例。您可以通过使用常规内部类并将侦听器实例化为实例字段来避免这种情况。尝试通过构造函数参数使侦听器状态感知来减少不同的侦听器类。常规内部类使您受益于自定义构造函数和其他方法。
里沙迪尼亚

0

还有各种库形式的可用选项,这些选项可使使用其他MVVM框架的人们非常熟悉此过程。

https://developer.android.com/topic/libraries/data-binding/

显示一个官方库的示例,该库允许您绑定如下按钮:

<Button
    android:text="Start second activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{() -> presenter.showList()}"
/>

0

步骤1:建立XML档案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnClickEvent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
</LinearLayout>

步骤2:建立MainActivity:

package com.scancode.acutesoft.telephonymanagerapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity implements View.OnClickListener {

    Button btnClickEvent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnClickEvent = (Button) findViewById(R.id.btnClickEvent);
        btnClickEvent.setOnClickListener(MainActivity.this);

    }

    @Override
    public void onClick(View v) {
        //Your Logic
    }
}

快乐编码!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.