In my Android app, I have a custom View that receives touch events. However, it doesn't react every time I touch it - only sometimes. From what I can tell, if I touch the screen, move my finger, and then let go - even if I move only a little - the event is picked up, but if I tap the screen too quickly for my finger to slide across it, nothing happens. How can I fix this?
Here is the View's code:
public class SpeedShooterGameView extends GameActivity.GameView {
public SpeedShooterGameView(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
#Override
protected GameThread getNewThread(SurfaceHolder holder, Context context) {
return new SpeedShooterGameThread(holder, context);
}
// Program is driven by screen touches
public boolean onTouchEvent(MotionEvent event) {
SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread();
if (thread.isRunning()) {
return thread.recieveTouch(event);
} else {
return false;
}
}
}
I am pretty confident that the object returned in the line SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread(); is working as I expect it to, but if the code above looks fine, I'll post the relevant code from that class as well. When thread.recieveTouch(event); is called, the MotionEvent is being sent to another thread.
EDIT: I'll go ahead and post the code for SpeedShooterGameThread:
public class SpeedShooterGameThread extends GameActivity.GameView.GameThread {
//... snip ...
private Queue<MotionEvent> touchEventQueue;
//... snip ...
public synchronized final void newGame() { //called from the constructor, used to go to a known stable state
//... snip ...
touchEventQueue = new LinkedList<MotionEvent>();
//... snip ...
}
//...snip...
public synchronized boolean recieveTouch(MotionEvent event) {
return touchEventQueue.offer(event);
}
private synchronized void processTouchEvents() {
synchronized (touchEventQueue) {
while (!touchEventQueue.isEmpty()) {
MotionEvent event = touchEventQueue.poll();
if (event == null) {
continue;
}
//... snip ....
}
}
}
//... snip ...
}
I fixed the bug by taking the Queue<MotionEvent> out entirely. My code now looks something like this:
The thread no longer uses a Queue, and MotionEvents are immediately processed when recieveTouch() is called:
public class SpeedShooterGameThread extends GameActivity.GameView.GameThread {
//The touchEvent member has been removed.
//... snip ...
public synchronized final void newGame() { //called from the constructor, used to go to a known stable state
// touchEvents is no longer initialized.
//...snip...
}
//...snip...
public synchronized boolean recieveTouch(MotionEvent event) {
//Immediately handle the MotionEvent here,
//or return false if the event isn't processed
}
// The processTouchEvents() method is removed.
//... snip ...
}
The view is unchanged:
public class SpeedShooterGameView extends GameActivity.GameView {
public SpeedShooterGameView(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
#Override
protected GameThread getNewThread(SurfaceHolder holder, Context context) {
return new SpeedShooterGameThread(holder, context);
}
// Program is driven by screen touches
public boolean onTouchEvent(MotionEvent event) {
SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread();
if (thread.isRunning()) {
return thread.recieveTouch(event);
} else {
return false;
}
}
}
Related
I'm using Otto as my event bus in my android application.
I had to make sure that certain events are called in the main thread, or not in the main thread, for that I created my own bus class which uses Otto like so:
class MyEventBus {
private final Bus anyThreadBus;
private final Bus mainThreadBus;
private final Bus notMainThreadBus;
private final Handler mainThreadHandler;
public enum Strategy {
Any,
Main,
NotMain
}
MyEventBus() {
this.anyThreadBus = new Bus(ThreadEnforcer.ANY);
this.mainThreadBus = new Bus(ThreadEnforcer.MAIN);
this.notMainThreadBus = new Bus(ThreadEnforcer.ANY);
this.mainThreadHandler = new Handler(Looper.getMainLooper());
}
public void register(Object object) {
this.register(object, Strategy.Any);
}
public void register(Object object, Strategy strategy) {
switch (strategy) {
case Main:
this.mainThreadBus.register(object);
break;
case NotMain:
this.notMainThreadBus.register(object);
break;
case Any:
default:
this.anyThreadBus.register(object);
}
}
public void unregister(Object object) {
try {
this.anyThreadBus.unregister(object);
} catch (Exception e) {}
try {
this.mainThreadBus.unregister(object);
} catch (Exception e) {}
try {
this.notMainThreadBus.unregister(object);
} catch (Exception e) {}
}
public void post(Object event) {
this.anyThreadBus.post(event);
this.enforceOnMainThread(event);
this.enforceOnNotMainThread(event);
}
public void post(Object event, Strategy strategy) {
switch (strategy) {
case Main:
this.enforceOnMainThread(event);
break;
case NotMain:
this.enforceOnNotMainThread(event);
break;
case Any:
default:
this.anyThreadBus.post(event);
}
}
private void enforceOnNotMainThread(final Object event) {
if (MyEventBus.onMainThread()) {
// MyApplication.pool() returns a shared thread pool for the application
MyApplication.pool().execute(new Runnable() {
#Override
public void run() {
notMainThreadBus.post(event);
}
});
} else {
this.notMainThreadBus.post(event);
}
}
private void enforceOnMainThread(final Object event) {
if (MyEventBus.onMainThread()) {
this.mainThreadBus.post(event);
} else {
this.mainThreadHandler.post(new Runnable() {
#Override
public void run() {
mainThreadBus.post(event);
}
});
}
}
private static boolean onMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
}
I have two questions:
In my post methods I post the event on all of the 3 buses, and that's because I don't know if the posted event has a registered class in a certain bus. Is there a way to know? something like:
if (this.anyThreadBus.has(event)) { ... }
Is there a way to do this other than maintaining a map of event classes to registered classes per bus?
Currently each registered class has an enforcement per call it made to register. But it would be best if I couldn't specify the enforcement per method and not for the entire class, something like:
#Subscribe(enforcement = Strategy.Main)
public void handleMyEvent(MyEvent event) { ... }
Can that be done somehow?
Thanks.
Maybe it's time to switch your EventBus implementation?
Check out if that one would make live easier for you:
http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/
I want to detect when users are pulling down for refresh
(refresh line in under ActionBar is starting expand to its UI width).
I want to replace ActionBar with message ("Swipe down to Refresh") but,
I don't know which event should I use to call my function.
You can extend SwipeRefreshLayout, override onTouchEvent, and make your change on the ACTION_DOWN event, ie:
public class MySwipeRefreshLayout extends SwipeRefreshLayout {
private Runnable onActionDown, onActionUp;
public MySwipeRefreshLayout(Context context, AttributeSet attributeSet) {
super(context,attributeSet);
}
public void setOnActionDown(Runnable onActionDown) {
this.onActionDown = onActionDown;
}
public void setOnActionUp(Runnable onActionUp) {
this.onActionUp = onActionUp;
}
#Override
public boolean onTouchEvent (MotionEvent ev) {
if (ev.getAction() == ev.ACTION_DOWN) {
if (onActionDown != null) {
onActionDown.run();
}
} else if (ev.getAction() == ev.ACTION_UP) {
if (onActionUp != null) {
onActionUp.run();
}
}
return super.onTouchEvent(ev);
}
:
:
}
Make sure you use the extended class in your layout. Then, in your view, you can pass a Runnable to setOnActionDown to update the actionbar or whatever else you want....
I have some trouble with my enum in my touchDown method from InputProcessor. When I try to use it it generates all the possible enums...
public class Memoration implements ApplicationListener, InputProcessor {
public static enum Screen {GAME, MENU}
Screen screen;
#Override
public void create() {
screen = Screen.MENU;
Gdx.app.log("onCreate", "works");
Gdx.input.setInputProcessor(this);
}
#Override
public void dispose() {
}
#Override
public void render() {
// bla bla bla
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Gdx.app.log("touch", "down");
if(screen == null)
Gdx.app.log("screen", "null");
if(screen == Screen.MENU)
Gdx.app.log("screen", "menu");
if(screen == Screen.GAME)
Gdx.app.log("screen", "game");
return false;
}
}
The log show us "onCreate: workds", "touch: down", "screen: null", "screen: menu" and "screen: game"
Your class is called Memoration and implements InputProcessor. However, in your create() callback, you are creating another instance of Memoration and setting that as the input processor, so it's that instance that is getting the callbacks. And, because create() isn't being called for that instance, screen is never initialized.
Try this instead...
#Override
public void create() {
screen = Screen.MENU;
Gdx.app.log("onCreate", "works");
Gdx.input.setInputProcessor(this);
}
In my android application, i have used custom view to load as the view of an activity.Now i want to detect whether user clicks on menu button or not.hope following code will explain the scenario.
My Custom View Class:
public class LibraryHomeView extends LinearLayout implements OnKeyListener {
//variable dec goes here..
public LibraryHomeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
private void init(Context con,AttributeSet att) {
//custom view implementation goes here...
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
Log.d(TAG, "KeyEvent.KEYCODE_MENU");
return true;
}
}
My problem is i do not want to capture this event from activity class i want to captuer this from view class but when i press the menu key it will not come to OnKey() method.Any help??
Try this,
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
// do something...
}
return true;
}
EDIT
public class LibraryHomeView extends LinearLayout {
public LibraryHomeView(Context context) {
super(context);
// TODO Auto-generated constructor stub
setFocusable(true);
}
If you can capture the key event in your activity, then is simple to pass it to the view. Just call from the activity a method inside the view. You may have something like this (I don't have eclipse on this computer, so I code directly in browser - I may have syntax errors):
public class LibraryHomeView extends LinearLayout {
//......
public void keyEventRecieved(String eventMessage) {
Log.d(TAG, eventMessage);
}
//.......
}
and inside the activity:
public class myActivity extends Activity {
LibraryHomeView control;
//......
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
control = (LibraryHomeView)findViewById(R.id.myCustomViewId);
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
Log.d(TAG, "KeyEvent.KEYCODE_MENU");
control.keyEventRecieved("Yahooooo!");
return true;
}
}
//......
}
In Stackview, it seems that OnItemSelectedListener (from superclass
"AdapterView") is never called...
How can I trigger some event when the view on top of the stack is
changed by the user ?
I want to display some text to show the position of the current item
inside the stack, so I need to find a way to update the textview when the user browses through the stack.
Thanks,
A little late for the party but for folks coming here from google. Fortunately I found an easier solution. It still involves extending StackView though.
import android.content.Context;
import android.util.AttributeSet;
import android.widget.StackView;
public class StackViewAdv extends StackView
{
public StackViewAdv(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public StackViewAdv(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
#Override
public void setDisplayedChild(int whichChild)
{
this.getOnItemSelectedListener().onItemSelected(this, null, whichChild, -1);
super.setDisplayedChild(whichChild);
}
}
Please note that this solution only gives the index of the selected view to the listener and view (second parameter on onItemSelected) is null!
Using this.getCurrentView() instead of null unfortunately doesn't work because it returns a sub class of StackView. Maybe someone finds a solution to that.
What i have done is writing a new class extending StackView and writing some code to get the OnItemSelected logics works. When the onTouchEvent gives me a MotionEvent.getAction() == ACTION_UP, i start a Thread that calls himself 'till the StackView.getDisplayedChild() changes. When it changes, i start the OnItemSelected logic, so i can always get the first displayed child.
public boolean onTouchEvent(MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP && this.getAdapter() != null) {
mPreviousSelection = this.getDisplayedChild();
post(mSelectingThread);
}
return super.onTouchEvent(motionEvent);
}
This thread cycles himself untill he gets the new displayedChild:
private class SelectingThread implements Runnable {
CustomStackView mStackView;
public SelectingThread(CustomStackView stackView) {
this.mStackView = stackView;
}
#Override
public void run() {
if(mStackView.getAdapter() != null) {
if (mPreviousSelection == CustomStackView.this.getDisplayedChild()) {
mThisOnItemSelectedListener.onItemSelected(mStackView, mStackView.getAdapter().getView(mPreviousSelection, null, mStackView),
mStackView.mPreviousSelection, mStackView.getAdapter().getItemId(mPreviousSelection));
return;
} else {
mPreviousSelection = mStackView.getDisplayedChild();
mStackView.post(this);
}
}
}
}
This Listener instead sets the Selected flag to true after deselecting them all.
private class StackViewOnItemSelectedListener implements OnItemSelectedListener {
CustomStackView mStackView;
public StackViewOnItemSelectedListener(CustomStackView stackView) {
this.mStackView = stackView;
}
#Override
public void onItemSelected(AdapterView<?> parent, View selectedView, int position, long id) {
deselectAll();
if (mStackView.getAdapter() != null) {
if (mOnItemSelectedListener != null) {
mStackView.mOnItemSelectedListener.onItemSelected(parent, selectedView, position, id);
}
mStackView.getAdapter().getView(position, null, mStackView).setSelected(true);
}
}
private void deselectAll() {
if (mStackView.getAdapter() != null) {
int adapterSize = mStackView.getAdapter().getCount();
for (int i = 0; i < adapterSize; i++) {
mStackView.getAdapter().getView(i, null, mStackView).setSelected(false);
}
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
if (mStackView.getAdapter() != null) {
if (mOnItemSelectedListener != null) {
mStackView.mOnItemSelectedListener.onNothingSelected(parent);
}
deselectAll();
}
}
}
I've tested it a little and it works..