Android: как обрабатывать нажатие кнопки

97

Имея солидный опыт работы в области, отличной от Java и Android, я изучаю Android.

У меня много путаницы с разными областями, одна из них - как обрабатывать нажатия кнопок. Есть как минимум 4 способа сделать это (!!!), они кратко перечислены здесь

для единообразия перечислю их:

  1. Имейте член View.OnClickListenerкласса в действии и назначьте его экземпляру, который будет обрабатывать onClickлогику в onCreateметоде действия.

  2. Создайте onClickListener в методе действия onCreate и назначьте его кнопке с помощью setOnClickListener

  3. Реализуйте onClickListener в самом действии и назначьте this в качестве слушателя для кнопки. В случае, если активность имеет несколько кнопок, необходимо проанализировать идентификатор кнопки, чтобы выполнить обработчик onClick для соответствующей кнопки.

  4. Имейте общедоступный метод для действия, реализующего логику onClick, и назначьте его кнопке в объявлении xml действия.

Вопрос 1:

Это все методы, есть ли другой вариант? (Мне не нужны другие, просто любопытно)

Для меня наиболее интуитивно понятным способом был бы последний: он требует наименьшего количества кода для ввода и является наиболее читаемым (по крайней мере, для меня).

Хотя я не вижу широкого применения этого подхода. Какие минусы его использования?

Вопрос 2:

Каковы плюсы и минусы каждого из этих методов? Поделитесь, пожалуйста, своим опытом или хорошей ссылкой.

Любые отзывы приветствуются!

PS Я попытался найти в Google что-то по этой теме, но нашел только описание «как» это сделать, а не почему это хорошо или плохо.

Будда
источник

Ответы:

149

Вопрос 1: К сожалению, тот, который, по вашему мнению, наиболее интуитивно понятен, меньше всего используется в Android. Насколько я понимаю, вы должны разделить свой пользовательский интерфейс (XML) и вычислительную функциональность (файлы классов Java). Это также упрощает отладку. На самом деле намного проще читать таким образом и думать об Android imo.

Вопрос 2: Я считаю, что в основном используются два номера - №2 и №3. В качестве примера я буду использовать кнопку 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***
            }
        });

Это мой любимый вариант, так как он имеет метод onClick рядом с тем местом, где переменная кнопки была установлена ​​с помощью findViewById. Кажется очень аккуратным и аккуратным, что все, что связано с этим представлением кнопки clickButton, расположено здесь.

Минус, который комментирует мой коллега, заключается в том, что представьте, что у вас есть много просмотров, которым нужен onclick listener. Вы можете видеть, что ваш onCreate станет очень длинным. Вот почему он любит использовать:

3

Допустим, у вас есть 5 кнопок ClickButtons:

Убедитесь, что ваша активность / фрагмент реализует 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, должен быть преобразован в поле.

Дайте мне знать, если вам нужна дополнительная информация. Я не ответил на ваш вопрос полностью, потому что это довольно длинный вопрос. И если я найду какие-то сайты, я расширю свой ответ, сейчас я просто даю некоторый опыт.

думамилк
источник
1
Для варианта 2 вам нужно сделать это: clickButton.setOnClickListener (new View.OnClickListener () {@Override public void onClick (View v) {// TODO what you want to do}}); чтобы помочь разрешить OnClickListener
ColossalChris
Вариант 3, вероятно, самый чистый и простой для расширения с помощью шаблона MVP.
Raffaeu
Вариант 2 все еще может производить onCreate(), это не очень долго. Назначения прослушивателя кликов и анонимные классы могут быть выделены в отдельный вспомогательный метод, вызываемый из onCreate().
Ник Алексеев
@Colossal: Вам не нужно этого делать. Добавьте расширение к классу деятельности, например «реализует View.OnClickListener».
TomeeNS
11

# 1 Я часто использую последний, когда на макете есть кнопки, которые не генерируются (но, очевидно, статичны).

Если вы используете его на практике и в бизнес-приложении, обратите на это особое внимание, потому что, когда вы используете обфускатор исходного кода, такой как ProGuard, вам нужно пометить эти методы в своей деятельности, чтобы они не запутывались.

Для архивации какой-либо защиты во время компиляции с помощью этого подхода взгляните на Android Lint ( пример ).


# 2 Плюсы и минусы всех методов почти одинаковы, и урок должен быть таким:

Используйте то, что вам больше всего подходит или кажется вам наиболее интуитивно понятным.

Если вам нужно назначить одно и то же OnClickListenerнескольким экземплярам кнопки, сохраните его в области действия класса (# 1). Если вам нужен простой слушатель для кнопки, сделайте анонимную реализацию:

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
Я собираюсь немного подумать и предложить пятый вариант (нет, без Брюса Уиллиса в главной роли :)), вариант варианта 2: использовать класс Presenter в структуре Model-View-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);
    }

}

(imho) 4-й подход, используемый, когда несколько кнопок имеют один и тот же обработчик, и вы можете объявить один метод в классе активности и назначить этот метод нескольким кнопкам в макете xml, также вы можете создать один метод для одной кнопки, но в этом случае я предпочитают объявлять обработчики внутри класса активности.

Георгий Гобозов
источник
1

Варианты 1 и 2 включают использование внутреннего класса, который сделает код беспорядочным. Вариант 2 немного запутан, потому что на каждую кнопку будет по одному слушателю. Если у вас небольшое количество кнопок, это нормально. Для варианта 4, я думаю, это будет сложнее отладить, так как вам придется вернуться и четверть XML и Java-код. Я лично использую вариант 3, когда мне приходится обрабатывать несколько нажатий кнопок.

CChi
источник
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***/
        }
    });
}
Webserveis
источник
1

Чтобы упростить задачу asp Вопрос 2, вы можете использовать лямбда-метод, подобный этому, чтобы сохранить память переменных и избежать навигации вверх и вниз в вашем классе представления.

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

но если вы хотите применить событие щелчка к вашей кнопке сразу в методе.

вы можете использовать Вопрос 3 от @D. Тран отвечает. Но не забудьте реализовать свой класс представления с помощью View.OnClickListener.

В другом случае, чтобы правильно использовать вопрос №3

Майкл
источник
1
Это следует рассматривать как современный ответ в сочетании со ссылками на методы IMO. В большинстве других ответов не упоминается тот факт, что они являются старым кодом до Java8 на Android.
Ryan The Leach
0

Вопрос №1. Это единственный способ обрабатывать клики по просмотрам.

Вопрос №2 -
Вариант №1 / Вариант №4 - Нет большой разницы между вариантом №1 и вариантом №4. Единственное различие, которое я вижу, заключается в том, что в одном случае действие реализует OnClickListener, тогда как в другом случае будет анонимная реализация.

Вариант №2 - в этом методе будет создан анонимный класс. Этот метод немного громоздок, так как вам придется делать это несколько раз, если у вас несколько кнопок. Для анонимных классов нужно быть осторожным при обработке утечек памяти.

Вариант №3 - Впрочем, это простой способ. Обычно программисты стараются не использовать какой-либо метод, пока он не написан, и поэтому этот метод не получил широкого распространения. Вы увидите, что в основном люди используют вариант №4. Потому что он чище с точки зрения кода.

Гаурав Арора
источник
Привет, Гаурав, спасибо за ответ. Но не могли бы вы прояснить, что вы имеете в виду здесь: для анонимных классов вы должны быть осторожны при обработке утечек памяти. Как здесь возникают утечки памяти?
Budda
Вам просто нужно знать, что: если вы создаете анонимный класс внутри метода, который может вызываться несколько раз в течение времени существования вашего приложения, будут созданы не несколько экземпляров одного класса, а несколько классов, включая их экземпляры. Вы можете избежать этого, используя обычные внутренние классы и создавая экземпляры слушателей как поля экземпляра. Попробуйте уменьшить количество различных классов слушателей, сделав состояние слушателя осведомленным с помощью аргументов конструктора. Обычный внутренний класс дает вам преимущество настраиваемых конструкторов и других методов.
Risadinha
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
    }
}

HappyCoding!

Маниканта Редди
источник