Android onClick方法不适用于自定义视图

问题描述 投票:27回答:10

我已经开始研发应用了。我昨天建立了菜单但是onClick方法不起作用!我创建了一个扩展View并调用她的MainMenuObject的类 - 该类用于主菜单中的任何对象(按钮,徽标等)。我为他们创建了一个特殊的类,因为我在菜单启动时正在做动画。在我构建了MainMenuObject类之后,我构建了另一个扩展View的类(OpeningTimesView),它将包含主菜单的所有按钮,并将作为主要活动的布局。

一切都很好,动画非常好,我想把听众放在我的按钮上,所以我在onClickListener上添加了一个实现onClickListener,并覆盖了onClick方法。然后我用setOnClickListener(this)和setClickable(true)将监听器添加到按钮,但它不起作用!我已经尝试了一切!请帮我弄清楚我做错了什么。我已经为onClick方法添加了一个toast,它不依赖于任何“if”,但它不会显示。

(BTW有没有办法将屏幕宽度和高度定义为所有类都可以访问的变量?它不能是静态的,因为你从显示对象获得高度和宽度但必须有另一种方式)

这是代码:

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!

android android-view android-custom-view
10个回答
21
投票

我只是遇到了同样的问题 - 我创建了一个自定义视图,当我通过调用v.setOnClickListener(new OnClickListener() {...});在活动中为它注册一个新的Listener时,监听器就没有被调用。

在我的自定义视图中,我也覆盖了public boolean onTouchEvent(MotionEvent event) {...}方法。问题是我没有调用View类的方法 - super.onTouchEvent(event)。这解决了这个问题。因此,如果你想知道为什么你的监听器没有被调用你可能忘记调用超类'onTouchEvent方法


0
投票

在我的情况下,我在自定义视图中将RelativeLayout作为父级,并且使其工作的唯一方法是在RelativeLayout和自定义视图的构造函数中将onTouchEvent()public boolean onTouchEvent(MotionEvent event) { .....YOURCODE..... callOnClick(); return boolean; } 设置为focusable,在对布局进行充气后,添加以下内容:

clickable

15
投票

如果您对UI Framework的运行方式不满意,在Android中创建自定义控件可能会非常棘手。如果您还没有,我建议您阅读以下内容:

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的触摸层次结构中。与其他一些流行的移动平台不同,Android从View层次结构的顶部开始提供触摸事件,并逐渐降低。传统上占据层次结构较高级别(活动和布局)的类在其中具有逻辑,以转发它们自己不消耗的触摸。

所以,我建议做的是改变你的OpeningTimesView以扩展ViewGroup(所有Android布局的超类)或特定布局(LinearLayoutRelativeLayout等)并添加你的按钮作为孩子。现在,似乎没有一个定义的层次结构(按钮在容器中并没有真正“包含”,它们只是成员),这可能会使事件真正发生的问题混淆不清。

  1. 触摸应该更自然地向下流到按钮,允许您的点击事件触发
  2. 您可以利用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's 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对象为它们设置动画,然后使它们在最终的休息位置中可见。

但是,这里的关键是布局足够智能,以便知道当它收到触摸事件时,它应该将它转发给相应的孩子。您可以在没有此项的情况下创建自定义视图,但是您必须拦截容器上的所有触摸并进行数学运算以确定将事件手动转发到哪个子视图。如果您真的无法让任何布局管理器工作,那么这就是您的追索权。

希望有帮助!


9
投票

您可以在自定义视图的onTouchEvent中调用performClick()。

在自定义视图中使用此选项:

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

7
投票

我这样做:

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
投票

你必须在构造函数中调用qazxsw poi并在self上实现qazxsw poi。

In this way:

setOnClickListener(this)

3
投票

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(); } } ,因为这些是响应点击的对象。 另一种选择是扩展onClickListener而不是MainMenuObjectView,因为你只使用那里的按钮


更新:完整示例


这是将其直接实现到可点击视图中的想法。有一个Button类扩展View并覆盖TestView,因为你需要它,并且还响应点击。我遗漏了任何动画实现,因为你有这个部分,它与View讨论无关。 我在Eclair模拟器中对它进行了测试,它按预期工作(点击后播放Toast消息)。

file:Test.java

onDraw

file:TestView.java

ClickListener

如果您需要一些可点击的和一些不可点击的,您可以添加一个带有布尔参数的构造函数来确定是否将ClickListener附加到视图:

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);
    }
}

3
投票

我有一个解决方案!它不是这个特定问题的真正解决方案,而是一种全新的方法。我把这个帖子发给了我认识的人,他告诉我要使用android所拥有的动画SDK(比如提到的无线设计),所以我只使用一个扩展的类,而不是使用4个类的主菜单页面。 Activity,Animation类提供了许多动画选项。我要感谢你们两位帮助我,你们很棒。我正在添加代码,如果有人遇到同样的问题或某事的这个线程:

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);
    }
}

0
投票

我有同样的问题。在我的例子中,我有一个LinearLayout作为我的自定义视图的根元素,public TestView(Context context, boolean clickable) { super(context); this.context = context; if (clickable) setOnClickListener(this); } 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); } } 设置为true,自定义视图标记本身(在片段的布局中使用)也设置为clickablefocusable。事实证明,我必须要做的唯一事情是从XML中删除所有clickablefocusable属性:)反直觉,但它的工作原理。


0
投票

只需将clickable添加到您的focusable方法中

callOnClick()
© www.soinside.com 2019 - 2024. All rights reserved.