Android onClick 方法不适用于自定义视图
我已经开始开发一个应用程序。我昨天构建了菜单,但 onClick 方法不起作用! 我创建了一个扩展 View 的类,并将其称为 MainMenuObject - 该类适用于主菜单中的任何对象(按钮、徽标等)。我为他们创建了一个特殊的类,因为我在菜单启动时制作动画。在构建了 MainMenuObject 类之后,我构建了另一个类 (OpeningTimesView),该类扩展了 View,并且其中包含主菜单的所有按钮,并将用作主活动的布局。
一切都很好,动画进行得非常顺利,我想在按钮上放置侦听器,因此我向 OpeningTimesView 类添加了 onClickListener 的实现,并重写了 onClick 方法。然后我使用 setOnClickListener(this) 和 setClickable(true) 将侦听器添加到按钮,但它不起作用!我已经尝试了一切!请帮我弄清楚我做错了什么。我已经向 onClick 方法添加了一个 toast,该方法不依赖于任何“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);
}
预先感谢,埃拉德!
I've started working on an app. I build the menu yesterday but the onClick method doesn't work!
I created a class that extends View and called her MainMenuObject - that class is for any object in the main menu (buttons, logos etc). I've created a special class for them because I'm doing an animation when the menu starts. After I've built the MainMenuObject class I've built another class (OpeningTimesView) that extends View and will have all the buttons of the main menu in it, and will function as the main activity's layout.
Everything was good, the animation went very well and I wanted to put listeners on my buttons, so I've added an implemention of onClickListener to the OpeningTimesView class, and overrided the onClick method. Then I've added the listener to the buttons with setOnClickListener(this) and setClickable(true), but it doesn't work! I've tried everything! Please help me figure out what I'm doing wrong. I've added a toast to the onClick method that doesn't depend on any "if" but it's won't show neither.
(BTW is there any way to define the screen width and height as variable that all classes can access? it can't be static because you get the height and width from a display object but there must be another way)
this is the code:
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);
}
Thanks in advance, Elad!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
我刚刚遇到了同样的问题 - 我创建了一个自定义视图,当我通过调用
v.setOnClickListener(new OnClickListener() {...});
监听器在活动中为其注册一个新的监听器时只是没有接到电话。在我的自定义视图中,我还覆盖了
public boolean onTouchEvent(MotionEvent event) {...}
方法。问题是我没有调用View类的方法 -super.onTouchEvent(event)
。这解决了问题。因此,如果您想知道为什么您的侦听器没有被调用,您可能忘记调用超类的onTouchEvent
方法,这是一个简单的示例:
I just had the same Problem - I created a custom view and when I registered a new Listener for it in the activity by calling
v.setOnClickListener(new OnClickListener() {...});
the listener just did not get called.In my custom view I also overwrote the
public boolean onTouchEvent(MotionEvent event) {...}
method. The problem was that I did not call the method of the View class -super.onTouchEvent(event)
. That solved the problem. So if you are wondering why your listener does not get called you have probably forgotten to call the superclass'esonTouchEvent
methodHere is a simple example:
如果您不熟悉 UI 框架的操作方式,那么在 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 从视图层次结构的顶部开始传递触摸事件,然后向下传递。传统上占据层次结构较高级别的类(活动和布局)具有转发它们本身不消耗的触摸的逻辑。
因此,我建议您更改
OpeningTimesView
以扩展ViewGroup
(所有 Android 布局的超类)或特定布局(LinearLayout
、RelativeLayout
等)并将按钮添加为子按钮。目前,似乎没有定义的层次结构(按钮并未真正“包含”在容器中,它们只是成员),这可能会混淆事件真正发生的位置的问题。首先选择一个布局类,它将帮助您将按钮放置在其最终位置。您可以使用 Android 中的动画框架或自定义绘图代码(就像您现在拥有的那样)来按照您喜欢的方式为它们设置动画。如有必要,按钮的位置和当前绘制该按钮的位置可以有很大不同,这就是当前动画框架在 Android 中的工作方式(3.0 之前)...但这是一个单独的问题。您还可以使用
AbsoluteLayout
,它允许您在任何您喜欢的位置放置和替换对象...但请注意您的应用程序在使用此设备的所有 Android 设备上的外观(考虑到不同的屏幕尺寸)。关于显示信息的第二点。
最简单的方法可能就是在需要的地方使用 Context.getResources().getDisplayMetrics() 。
Activity
继承自Context
,因此可以直接调用此方法。视图始终有一个可以通过getContext()
访问的Context
。任何其他类,您都可以在构造中将Context
作为参数传递(这是 Android 中的常见模式,您会看到许多对象需要Context
,主要是为了访问资源
)。这是一个快速启动的框架示例。这只是将三个水平排列一次作为最终位置:
您可以从那里自定义它。也许启动按钮时将可见性设置为 View.INVISIBLE,直到您使用绘图代码或自定义动画对象对它们进行动画处理,然后使它们在最终静止位置可见。
不过,这里的关键是布局足够智能,知道当它收到触摸事件时,应该将其转发给相应的子级。您可以在没有此操作的情况下创建自定义视图,但您必须拦截容器上的所有触摸并进行数学计算以确定将事件手动转发到哪个子视图。如果您确实无法使任何布局管理器工作,那么这就是您的资源。
希望有帮助!
Creating custom controls in Android can be tricky if you aren't comfortable with how the UI Framework operates. If you haven't already, I would recommend reading these:
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
Notice that when layouts are declared in XML the elements are nested. This creates a layout hierarchy that you must create your self when customizing a component using only Java code.
Most likely you are getting caught up in Android's touch hierarchy. Unlike some other popular mobile platforms, Android delivers touch events starting at the top of the View hierarchy and works its way down. The classes that traditionally occupy the higher levels of the hierarchy (Activity and Layouts) have logic in them to forward touches they don't themselves consume.
So, what I would recommend doing is changing your
OpeningTimesView
to extend aViewGroup
(the superclass of all Android layouts) or a specific layout (LinearLayout
,RelativeLayout
, etc.) and add your buttons as children. Right now, there does not seem to be a defined hierarchy (the buttons aren't really "contained" in the container, they're just members) which may be confusing the issue as to where events are really going.Pick a layout class to start with that will help you place your buttons in their FINAL locations. You can use the animation framework in Android or custom drawing code (like you have now) to animate them anyway you like up to that point. The location of a button and where that button is currently drawn are allowed to be very different if necessary, and that's how the current Animation Framework works in Android (prior to 3.0)...but that's a separate issue. You also have
AbsoluteLayout
, which allows you to place and replace objects anywhere you like...but be careful of how your app looks on all Android devices with this one (given the different screen sizes).As to your second point about display info.
The simplest method is probably just to use
Context.getResources().getDisplayMetrics()
wherever you need it.Activity
inherits fromContext
, so they can call this method directly. Views always have aContext
you can access withgetContext()
. Any other classes you can just pass theContext
as a parameter in construction (this is a common pattern in Android, you'll see many objects require aContext
, mainly to accessResources
).Here's a skeleton example to jump start things. This just lines the three up horizontally once as a final location:
You can customize this from there. Perhaps starting the buttons with visibility set to View.INVISIBLE until you animate them in with your drawing code or a custom Animation object, then making them visibile in their final resting place.
The key here, though, is the the layout is smart enough to know that when it receives a touch event it is supposed to forward it to the corresponding child. You can create a custom view without this, but you will have to intercept all touches on the container and do the math to determine which subview to manually forward the event to. If you truly can't make any layout manager work, this is your recourse.
Hope that Helps!
您只需在自定义视图的onTouchEvent 中调用performClick() 即可。
在您的自定义视图中使用它:
You can just call performClick() in onTouchEvent of your custom view.
Use this in you custom view:
我这样做:
I do this so:
您必须在构造函数中调用
setOnClickListener(this)
并在自身上实现View.OnClickListener
。这样:
You have to call
setOnClickListener(this)
in contructor(s) and implementView.OnClickListener
on self.In this way:
我也有同样的问题。就我而言,我将 LinearLayout 作为自定义视图的根元素,将
clickable
和focusable
设置为 true,并将自定义视图标记本身(在片段的布局中使用) )也被设置为可点击
和可聚焦
。事实证明,要让它工作,我唯一要做的就是从 XML 中删除所有clickable
和focusable
属性:) 违反直觉,但它有效。I had the same problem. In my case I had a LinearLayout as a root element of my custom view, with
clickable
andfocusable
set to true, and the custom view tag itself (used in a fragment's layout) was also set to beclickable
andfocusable
. Turns out that the only thing I had to do to get it working was to remove all theclickable
andfocusable
attributes from within the XML :) Counter-intuitive, but it worked.在
MainMenuObjectView
类中实现onClickListener
,因为这些对象将响应点击。另一种选择是扩展
Button
而不是View
,因为您只使用其中的按钮更新:完整示例
这是将其直接实现到可点击视图中的想法。有一个
TestView
类,它可以根据需要扩展View
并重写onDraw
,并且还可以响应点击。我遗漏了任何动画实现,因为您有该部分,并且它与ClickListener
讨论无关。我在 Eclair 模拟器中对其进行了测试,它按预期工作(单击后出现 Toast 消息)。
文件:Test.java
文件:TestView.java
和一些不可点击的,你可以添加一个带有
布尔参数来确定 ClickListener 是否附加到视图:
Implement the
onClickListener
in theMainMenuObjectView
class, since those are the objects that will respond to clicks.Another alternative would be to extend
Button
instead ofView
, because you are using only buttons in thereUpdate: Full example
This is the idea to implement it directly into the clickable views. There is a
TestView
class that extendsView
and overridesonDraw
, as you need it to, and also responds to clicks. I left out any animation implementation as you have that part and it's not relevant to theClickListener
discussion.I tested it in an Eclair emulator and it works as expected (a Toast message after a click).
file: Test.java
file: TestView.java
If you need some clickable and some not clickable, you can add a constructor with a
boolean argument to determine whether the ClickListener is attached or not to the View:
我有办法解决!
这并不是这个特定问题的真正解决方案,而是一种全新的方法。
我将此线程发送给我认识的人,他告诉我使用 android 具有的动画 SDK(如提到的无线设计),因此我没有使用 4 个类来执行主菜单页面,而是只使用一个扩展类来执行此操作Activity 和 Animation 类提供了许多动画选项。
我要感谢你们对我的帮助,你们很棒。
如果有人遇到此线程有相同的问题或其他问题,我将添加代码:
I've got a solution!
It's not really a solution for this specific issue, but a whole new approach.
I sent this thread to somebody I know and he told me to use the Animation SDK the android has (like Wireless Designs mentioned), so instead of doing the main menu page with 4 classes, I'm doing it only with one class that extends Activity, and the Animation class offers many animation options.
I want to thank you both for helping me, you are great.
I'm adding the code if someone will encounter this thread with the same problem or something:
就我而言,我的自定义视图中有一个RelativeLayout作为父级,使其工作的唯一方法是将
focusable
和clickable
设置为true
在RelativeLayout和自定义视图的构造函数中,膨胀布局后,添加以下内容:In my case I had a RelativeLayout as a parent in my custom view and the only way to make it work was to set
focusable
andclickable
totrue
in the RelativeLayout and in the constructor of the custom view, after inflating the layout, add this:就这么简单:
It is this easy:
只需将
callOnClick()
添加到您的onTouchEvent()
方法中just add
callOnClick()
to youronTouchEvent()
method