【问题标题】:Android: how to handle button clickAndroid:如何处理按钮点击
【发布时间】:2013-01-24 19:53:23
【问题描述】:

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

我对不同的领域有很多困惑,其中之一是如何处理按钮点击。至少有 4 种方法(!!!),它们被简要列出 here

出于一致性目的,我将列出它们:

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

  2. 在 'onCreate' 活动方法中创建 'onClickListener' 并使用 setOnClickListener 将其分配给按钮

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

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

问题 #1:

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

对我来说,最直观的方法是最新的方法:它需要输入的代码最少并且可读性最强(至少对我来说)。

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

问题 #2:

每种方法的优缺点是什么?请分享您的经验或良好的链接。

欢迎任何反馈!

附:我尝试在 Google 上找到有关此主题的内容,但我发现的唯一内容是描述“如何”做到这一点,而不是为什么它好或坏。

【问题讨论】:

    标签: java android onclicklistener android-button


    【解决方案1】:

    问题 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 Button View 的所有东西都位于这里,看起来非常整洁。

    我的同事遇到的一个问题是,假设您有许多需要 onclick 监听器的视图。你可以看到你的 onCreate 会变得很长。所以他喜欢用:

    3

    假设你有 5 个点击按钮:

    确保您的 Activity/Fragment 实现 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. 以及任何其他可能位于 onCreate 中被 onClick 方法使用的对象都必须被制成一个字段。

    如果您想了解更多信息,请告诉我。我没有完全回答你的问题,因为这是一个很长的问题。如果我找到一些网站,我会扩展我的答案,现在我只是提供一些经验。

    【讨论】:

    • 对于选项 2,您需要: clickButton.setOnClickListener( new View.OnClickListener() {@Override public void onClick(View v) { //TODO what you want to do } } );帮助它解决 OnClickListener
    • 选项 3 可能是使用 MVP 模式扩展的最简洁和最容易的方法。
    • 选项 2 仍然可以产生 onCreate() 这不是很长。点击监听器分配和匿名类可以分解成一个单独的辅助方法,从onCreate()调用。
    • @Colossal:你不必这样做。向 Activity 类添加扩展,例如“implements View.OnClickListener”。
    【解决方案2】:

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

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

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


    #2所有方法的优缺点几乎相同,教训应该是:

    使用最合适或对您来说最直观的东西。

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

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

    我倾向于不在活动中实现OnClickListener,这有时会让人有点困惑(尤其是当您实现多个其他事件处理程序并且没有人知道this 都在做什么时)。

    【讨论】:

    【解决方案3】:

    我更喜欢选项 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 更难。

    【讨论】:

    • 我会有点胡扯,并建议第五个选项(不,不是由布鲁斯威利斯主演:)),选项 2 的变体:在 Model-View-Presenter 框架中使用 Presenter 类处理点击。它使自动化测试变得更加容易。查看此链接以获取更多信息: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 对象并稍后将其设置为按钮,但您仍然需要覆盖 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);
        }
    
    }
    

    (恕我直言)当多个按钮具有相同的处理程序时使用的第 4 种方法,您可以在活动类中声明一个方法并将此方法分配给 xml 布局中的多个按钮,您也可以为一个按钮创建一个方法,但是在这种情况我更喜欢 在活动类中声明处理程序。

    【讨论】:

      【解决方案5】:

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

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

      但如果您希望在方法中一次将点击事件应用于您的按钮。

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

      在其他正确使用问题#3

      【讨论】:

      • 这应该被认为是结合 IMO 方法参考的现代答案。大多数其他答案都没有说明它们是 Android 上 Java8 之前的旧代码。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        我的示例,在 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***/
                }
            });
        }
        

        【讨论】:

          【解决方案8】:

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

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

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

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

          【讨论】:

          • 您好 Gaurav,谢谢您的回答。但是您能否澄清一下您的意思:对于匿名类,您必须小心处理内存泄漏。内存泄漏是怎么来的?
          • 您只需要注意:如果您在一个可能在应用程序生命周期内被多次调用的方法内创建一个匿名类,则不会创建一个类的多个实例,而是创建几个类,包括他们的实例。您可以通过使用常规内部类并将侦听器实例化为实例字段来避免这种情况。尝试通过构造函数参数使侦听器状态感知来减少不同的侦听器类。常规内部类为您提供自定义构造函数和其他方法的好处。
          【解决方案9】:

          还有以各种库的形式提供的选项,可以使使用过其他 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()}"
          />
          

          【讨论】:

            【解决方案10】:

            第 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
                }
            }
            

            快乐编码!

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-01
              相关资源
              最近更新 更多