Подтвердить что ты не робот

Метод Android onClick не работает на пользовательском представлении

Я начал работать над приложением. Я создаю меню вчера, но метод onClick не работает! Я создал класс, который расширяет представление и вызывает ее MainMenuObject - этот класс предназначен для любого объекта в главном меню (кнопки, логотипы и т.д.). Я создал для них специальный класс, потому что я запускаю анимацию при запуске меню. После того, как я построил класс MainMenuObject, я построил еще один класс (OpeningTimesView), который расширяет представление и будет иметь в нем все кнопки главного меню и будет функционировать в качестве основного вида деятельности.

Все было хорошо, анимация прошла очень хорошо, и я хотел поместить слушателей на свои кнопки, поэтому я добавил реализацию onClickListener в класс OpenTimesView и переопределил метод onClick. Затем я добавил слушателя к кнопкам с setOnClickListener (this) и setClickable (true), но он не работает! Я пробовал все! Пожалуйста, помогите мне выяснить, что я делаю неправильно. Я добавил тост к методу onClick, который не зависит от какого-либо "if", но он не отображает ни того, ни другого.

(Кстати, есть ли способ определить ширину и высоту экрана как переменную, к которой могут обращаться все классы? она не может быть статичной, потому что вы получаете высоту и ширину от экранного объекта, но должен быть другой способ)

это код:

public class OpeningTimesView extends View implements OnClickListener{
    private MainMenuObjectView searchButton;
    private MainMenuObjectView supportButton;
    private MainMenuObjectView aboutButton;
    private int screenWidth;
    private int screenHeight;
    public OpeningTimesView(Context context, Display dis) {
        super(context);

        this.screenWidth = dis.getWidth();
        this.screenHeight = dis.getHeight();

        searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
        supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
        aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);

        searchButton.setClickable(true);
        supportButton.setClickable(true);
        aboutButton.setClickable(true);

        searchButton.setOnClickListener(this);
        supportButton.setOnClickListener(this);
        aboutButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View view){
        Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        if(view == searchButton){
            Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        }
        else if(view == supportButton){
            Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
        }
        else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onDraw(Canvas canvas)
    {
        // Drawing the buttons
        this.searchButton.onDraw(canvas);
        this.aboutButton.onDraw(canvas);
        this.supportButton.onDraw(canvas);
    }

Спасибо, Elad!

4b9b3361

Ответ 1

У меня есть решение! Это не решение для этой конкретной проблемы, а совершенно новый подход. Я отправил эту тему кому-то, кого знаю, и он сказал мне использовать Анимационный SDK, который имеет андроид (например, Wireless Designs), поэтому вместо того, чтобы делать главную страницу меню с 4 классами, я делаю это только с одним классом, который расширяется Активность и класс анимации предлагает множество вариантов анимации. Я хочу поблагодарить вас обоих за то, что помогли мне, вы здоровы. Я добавляю код, если кто-то встретит этот поток с той же проблемой или чем-то:

package elad.openapp;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.Toast;

public class OpeningTimes extends Activity implements OnClickListener{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    // Disabling the title bar..
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);

    // Create the buttons and title objects
    ImageView title = (ImageView)findViewById(R.id.title_main);
    ImageView search = (ImageView)findViewById(R.id.search_button_main);
    ImageView support = (ImageView)findViewById(R.id.support_button_main);
    ImageView about = (ImageView)findViewById(R.id.about_button_main);

    // Setting the onClick listeners
    search.setOnClickListener(this);
    support.setOnClickListener(this);
    about.setOnClickListener(this);

    setButtonsAnimation(title, search, support, about);

}

@Override
public void onClick(View v) {
    if(v.getId()==R.id.search_button_main){
        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        startActivity(new Intent(this,SearchPage.class));
    }
    else if(v.getId()==R.id.support_button_main){
        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
    }
    else if(v.getId()==R.id.about_button_main){

        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        Toast.makeText(this, "Coming soon...", Toast.LENGTH_LONG).show();
    }
}

// Setting the animation on the buttons
public void setButtonsAnimation(ImageView title, ImageView search, ImageView support, ImageView about){
    // Title animation (two animations - scale and translate)
    AnimationSet animSet = new AnimationSet(true);

    Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);
    anim.setDuration(750);
    animSet.addAnimation(anim);

    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);
    animSet.addAnimation(anim);

    title.startAnimation(animSet);

    // Search button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    search.startAnimation(anim);

    // Support button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1.5f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    support.startAnimation(anim);

    // About button animation
    anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 3f, Animation.RELATIVE_TO_SELF, 0.0f);
    anim.setDuration(750);

    about.startAnimation(anim);
}
}

Ответ 2

У меня была одна и та же проблема - я создал пользовательское представление, и когда я зарегистрировал новый прослушиватель для этого в действии, вызвав v.setOnClickListener(new OnClickListener() {...});, слушатель просто не получил вызов.

В моем пользовательском представлении я также перезаписал метод public boolean onTouchEvent(MotionEvent event) {...}. Проблема заключалась в том, что я не вызывал метод класса View - super.onTouchEvent(event). Это решило проблему. Поэтому, если вам интересно, почему ваш слушатель не вызван, вы, вероятно, забыли назвать метод superclass'es onTouchEvent

Ответ 3

Создание пользовательских элементов управления на Android может быть сложным, если вам неудобно работать с UI Framework. Если вы еще этого не сделали, я бы рекомендовал прочитать следующие данные:

http://developer.android.com/guide/topics/ui/declaring-layout.html

http://developer.android.com/guide/topics/ui/custom-components.html

http://developer.android.com/guide/topics/ui/layout-objects.html

Обратите внимание, что когда макеты объявлены в XML, элементы вложены. Это создает иерархию компоновки, которую вы должны создать самостоятельно при настройке компонента с использованием только кода Java.

Скорее всего, вы попадаете в иерархию Android touch. В отличие от некоторых других популярных мобильных платформ, Android предлагает сенсорные события, начиная с вершины иерархии View и прокладывает себе путь вниз. Классы, которые традиционно занимают более высокие уровни иерархии (активность и макеты), имеют в себе логику для перетасовки, которые они сами не потребляют.

Итак, что я бы рекомендовал сделать, это изменить OpeningTimesView, чтобы расширить ViewGroup (суперкласс всех макетов Android) или конкретный макет (LinearLayout, RelativeLayout и т.д.) и добавить ваши кнопки как дети. Прямо сейчас, похоже, не существует определенной иерархии (кнопки на самом деле не "содержатся" в контейнере, они просто члены), что может сбить с толку вопрос о том, где происходят события.

  • Прикосновения должны более естественным образом стекать вниз по кнопкам, позволяя запускать события щелчка
  • Вы можете использовать механизмы компоновки Android, чтобы нарисовать ваш взгляд, а не полагаться на код рисования, чтобы сделать все это.

Выберите класс макета для начала, который поможет вам разместить ваши кнопки в своих FINAL местах. Вы можете использовать рамку анимации в Android или пользовательский код рисования (например, у вас есть), чтобы оживить их в любом случае до этого момента. В случае необходимости расположение кнопки и где эта кнопка в настоящий момент нарисована, может быть очень различной, а также то, как текущая анимационная структура работает в Android (до 3.0)... но это отдельная вопрос. У вас также есть AbsoluteLayout, который позволяет размещать и заменять объекты в любом месте... но будьте осторожны с тем, как ваше приложение выглядит на всех устройствах Android с этим (учитывая разные размеры экрана).

Что касается вашего второго пункта о отображаемой информации. Самый простой способ - это просто использовать Context.getResources().getDisplayMetrics() везде, где вам это нужно. Activity наследует от Context, поэтому они могут напрямую вызвать этот метод. У просмотров всегда есть Context, с которым вы можете получить доступ с помощью getContext(). Любые другие классы, которые вы можете просто передать Context в качестве параметра в конструкции (это общий шаблон в Android, вы увидите, что для многих объектов требуется Context, главным образом для доступа к Resources).

Здесь приведен пример скелета, чтобы начать игру. Это просто выравнивает три горизонтально один раз в качестве конечного местоположения:

Public class OpeningTimesView extends LinearLayout implements OnClickListener {
    private MainMenuObjectView searchButton;
    private MainMenuObjectView supportButton;
    private MainMenuObjectView aboutButton;
    private int screenWidth;
    private int screenHeight;

    public OpeningTimesView(Context context) {
        this(context, null);
    }

    //Thus constructor gets used if you ever instantiate your component from XML
    public OpeningTimesView(Context context, AttributeSet attrs) {
        super(context, attrs);

        /* This is a better way to obtain your screen info
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        screenWidth = display.widthPixels;
        screenHeight = display.heightPixels;
        */
        //This way works also, without needing to customize the constructor
        WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Display dis = wm.getDefaultDisplay();
        screenWidth = dis.getWidth();
        screenHeight = dis.getHeight();

        searchButton = new MainMenuObjectView(context, 200, MovingMode.RIGHT, R.drawable.search, dis);
        supportButton = new MainMenuObjectView(context, 400, MovingMode.LEFT, R.drawable.support, dis);
        aboutButton = new MainMenuObjectView(context, 600, MovingMode.RIGHT, R.drawable.about, dis);

        //Even if they don't extend button, if MainMenuObjectView is always clickable
        // this should probably be brought into that class constructor
        searchButton.setClickable(true);
        supportButton.setClickable(true);
        aboutButton.setClickable(true);

        searchButton.setOnClickListener(this);
        supportButton.setOnClickListener(this);
        aboutButton.setOnClickListener(this);

        //Add the buttons to the layout (the buttons are now children of the container)
        setOrientation(LinearLayout.HORIZONTAL);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        addView(searchButton, params);
        addView(supportButton, params);
        addView(aboutButton, params);       
    }

    @Override
    public void onClick(View view){
        Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        if(view == searchButton){
            Toast.makeText(getContext(), "Search button pressed", Toast.LENGTH_SHORT).show();
        }
        else if(view == supportButton){
            Toast.makeText(getContext(), "Support button pressed", Toast.LENGTH_SHORT).show();
        }
        else Toast.makeText(getContext(), "About button pressed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDraw(Canvas canvas)
    {
        //Drawing the buttons
        // This may only be necessary until they are in place, then just call super.onDraw(canvas)
        this.searchButton.onDraw(canvas);
        this.aboutButton.onDraw(canvas);
        this.supportButton.onDraw(canvas);
    }    
}

Вы можете настроить это оттуда. Возможно, начиная с кнопок с видимостью, установленной в View.INVISIBLE, пока вы не активируете их с помощью кода рисования или пользовательского объекта Animation, а затем сделайте их видимыми в конечном месте отдыха.

Ключ здесь, однако, заключается в том, что макет достаточно умен, чтобы знать, что когда он получает событие касания, он должен перенаправить его соответствующему ребенку. Вы можете создать пользовательский вид без этого, но вам придется перехватывать все касания в контейнере и выполнять математику, чтобы определить, к какому подпункту нужно вручную переслать событие. Если вы действительно не можете заставить менеджера компоновки работать, это ваш регресс.

Надеюсь, что это поможет!

Ответ 4

Вы можете просто вызвать функцию performClick() в onTouchEvent вашего пользовательского представления.

Используйте это в своем пользовательском представлении:

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP){
            return performClick();
        }
        return true;
    }

Ответ 5

Я делаю это так:

public class YourView extends LinearLayout implements OnClickListener {
    OnClickListener listener;

    //... constructors

    public void setOnClickListener(OnClickListener listener) {
        this.listener = listener;
    }

    @Override
    public void onClick(View v) {
        if (listener != null)
             listener.onClick(v);
    }
}

Ответ 6

Вы должны вызвать setOnClickListener(this) в contructor (s) и реализовать View.OnClickListener на себе.

Таким образом:

public class MyView extends View implements View.OnClickListener {

    public MyView(Context context) {
        super(context);
        setOnClickListener(this);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Toast.makeText(getContext(), "On click.", Toast.LENGTH_SHORT).show();
    }
}

Ответ 7

Внедрите onClickListener в класс MainMenuObjectView, так как это объекты, которые будут реагировать на клики.
Другой альтернативой было бы расширить Button вместо View, потому что вы используете только кнопки там


Обновление: полный пример


Это идея реализовать его непосредственно в интерактивных представлениях. Существует класс TestView, который расширяет View и переопределяет onDraw, как вам нужно, а также отвечает на клики. Я оставил любую анимационную реализацию, поскольку у вас есть эта часть, и это не относится к обсуждению ClickListener.
Я тестировал его в эмуляторе Eclair, и он работает как ожидается (сообщение Toast после щелчка).

файл: Test.java

package com.aleadam.test;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;

public class Test extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);
        TextView label = new TextView(this);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
             LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        label.setText("Click the circle!");
        TestView testView = new TestView(this);
        ll.addView(label, layoutParams);
        ll.addView(testView, layoutParams);
        setContentView(ll);
    }
}

файл: TestView.java

package com.aleadam.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class TestView extends View implements OnClickListener {
    Context context;

    public TestView(Context context) {
        super(context);
        this.context = context;
        setOnClickListener(this);
    }

    public void onClick(View arg0) {
        Toast.makeText(context, "View clicked.", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDraw (Canvas canvas) {
        super.onDraw(canvas);
        this.setBackgroundColor(Color.LTGRAY);
        Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
        canvas.drawCircle(20, 20, 20, paint);
    }
}

Если вам нужно несколько кликов, а некоторые из них не доступны, вы можете добавить конструктор с boolean аргумент, чтобы определить, прикреплен ли ClickListener или нет в представлении:

public TestView(Context context, boolean clickable) {
    super(context);
    this.context = context;
    if (clickable)
        setOnClickListener(this);
}

Ответ 8

У меня такая же проблема. В моем случае у меня был LinearLayout в качестве корневого элемента моего настраиваемого представления, для которого clickable и focusable установлено значение true, а сам тег настраиваемого представления (используемый в макете фрагмента) также был настроен для clickable и focusable. Оказывается, единственное, что мне нужно было сделать, чтобы это работало, - это удалить все clickable и focusable атрибуты изнутри XML :). Интуитивно, но это сработало.

Ответ 9

просто добавьте callOnClick() в ваш onTouchEvent()

public boolean onTouchEvent(MotionEvent event) {
        .....YOURCODE.....
        callOnClick();
        return boolean;
    }

Ответ 10

В моем случае у меня был RelativeLayout в качестве родителя в моем настраиваемом представлении, и единственный способ заставить его работать, состоял в том, чтобы установить для focusable и clickable значение true в RelativeLayout и в конструкторе настраиваемого представления, после надувания макета добавьте следующее:

View view = inflate(getContext(), R.layout.layout_my_custom_view, this);

view.findViewById(R.id.theparent).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            performClick();
        }
    });