It's simple. The OnTouchListener does not work at all.
I'm fairly certain it's being initialized. I'm using a custom view to draw my UI, and as usual, I'm being impeded by Android's touchy API (pun intended):
public class ViewInterface extends View implements OnTouchListener{
public ViewInterface(Context context){
super(context);
...
}
public void update(){
...
}
#Override
public void onDraw(Canvas canvas){
...
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
keyboardEnabled = true;
return true;
case MotionEvent.ACTION_UP:
keyboardEnabled = false;
return true;
default: break;
}
return false;
}
Code for initializing the view in the main activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
//setContentView(R.layout.activity_aidan);
activity = this;
viewInterface = new ViewInterface(this);
setContentView(viewInterface);
initializeSpeechRecognition();
findMe();
run.start();
}
What am I missing? Upon touching the screen and holding it, keyboardEnabled should be set to true (its just a debug value) - yet it does not do anything. The touch events don't seem to be responding at all.
I've attempted to use the onTouchEvent method built into View with the following code:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
keyboardEnabled = true;
return true;
case MotionEvent.ACTION_UP:
keyboardEnabled = false;
return true;
default: break;
}
return false;
}
However this did not work either. The touch events are not responding at all - tapping the screen and releasing should be setting keyboardEnabled to false - which it does not.
You should override the onTouchEvent() method of the View class.
Right now you are simply implementing the OnTouchListener interface which should be used in conjunction with the setOnTouchListener() method.
For example:
#Override
public boolean onTouchEvent(MotionEvent event) {
// do whatever
}
Don't forget to call super where appropriate!
Alternatively, you could call setOnTouchListener(this); somewhere in your code, but that seems redundant.
Related
I have a TextView, with a onLongClickListener and OnClick event, on holding TextView, its color changes to red, and on releasing, its color is supposed to change to white.
Problem:
When I hold the TextView and move my finger outside of it while holding, and then leave my finger, its color does not change to white.
XML
<TextView
android:layout_width="match_parent"
android:text="hello"
android:textColor="#ffff"
android:id="#+id/timer"
android:layout_height="wrap_content"
/>
Java
final TextView t1 = (TextView) findViewById(R.id.timer);
t1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
t1.setTextColor(Color.WHITE);
}
});
t1.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
t1.setTextColor(Color.RED);
return false;
}
});
View.OnClickListener - Interface definition for a callback to be invoked when a view is clicked.
View.OnLongClickListener - Interface definition for a callback to be invoked when a view has been clicked AND held.
So what you are saying is 100% true.It should be red because its being been clicked and held as the way you did.
But when i hold the text view and move my finger outside the text view
while holding , and then leave my finger , it not changes its color to
white
You have given color white to text view when it gets only clicked !! If you want to get that white like you said(when clicked and held), you need to set the white color in OnLongClickListener
To the point if you want to detect your views touch and release and change colors related to that then you need to use OnTouchListener instead of clickListeners
View.OnTouchListener - Interface definition for a callback to be invoked when a touch event is dispatched to this view. The callback will be invoked before the touch event is given to the view
t1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch ( event.getAction() ) {
case MotionEvent.ACTION_DOWN:
t1.setTextColor(Color.RED); // pressed state
break;
case MotionEvent.ACTION_UP:
t1.setTextColor(Color.WHITE); // Released state
break;
}
return true;
}
});
Assign a onTouch listener and look for MotionEvent.ACTION_DOWN and MotionEvent.ACTION_MOVE:
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Construct a rect of the view's bounds
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
// User moved outside bounds
t1.setTextColor(Color.WHITE);
}
}
return false;
}
Use OnTouchListener that way you can register touch down and up events. MotionEvent case MotionEvent.ACTION_DOWN: will set the color to red when the user touches down on your TextView, and case MotionEvent.ACTION_UP: will set the color to white when the user lifts their finger off the TextView.
final TextView t1 = (TextView) findViewById(R.id.timer);
t1.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
t1.setTextColor(Color.RED);
break;
case MotionEvent.ACTION_UP:
t1.setTextColor(Color.WHITE);
break;
}
return true;
}
});
So I have a button that, when held, will play a sound. However, it does not seem to "reset" and never plays that sound again when I hold down the button again. I get a
E/MediaPlayer﹕ start called in state 0
E/MediaPlayer﹕ error (-38, 0)
error in log when I try to hold the button down again. Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
final MediaPlayer bark = MediaPlayer.create(this, R.raw.bark);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playSound = (Button) findViewById(R.id.btn);
playSound.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(!bark.isPlaying()) bark.start();
break;
case MotionEvent.ACTION_UP:
if(bark.isPlaying()) bark.stop();
break;
}
return true;
}
});
}
Instead of calling start/stop you can call start/pause with seekTo():
case MotionEvent.ACTION_DOWN:
if(!bark.isPlaying()) bark.start();
break;
case MotionEvent.ACTION_UP:
if(bark.isPlaying()){
bark.pause();
bark.seekTo(0);
}
break;
If you want to use start/stop, make sure you call prepare() before calling start() again.
The reason why your code isn't working is because according to the state diagram stop() stops playing and puts the MP in a stopped state. Before starting to play for the next time, you should call prepare() and then start() again.
EDIT : Place your music file in the assets folder and use the file descriptor. This way you can reset the MP and replay:
Using START/STOP:
#Override
protected void onCreate(Bundle savedInstanceState) {
AssetManager assetManager=Context.getAssets();
AssetFileDescriptor fileDescriptor = assetManager.openFd("bark.mp3"); //replace with right extension
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playSound = (Button) findViewById(R.id.btn);
playSound.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(!bark.isPlaying()){
bark.reset();
bark.setDataSource(fileDescriptor.getFileDescriptor());
bark.prepare();
bark.start();
}
break;
case MotionEvent.ACTION_UP:
if(bark.isPlaying())
bark.stop();
break;
}
return true;
}
});
}
In a view I want to detect both single tap and double tap. It works fine with me. I can detect both single and double tap. Please find my code
#Override
public boolean onTouch(View view, MotionEvent me) {
// No dragging during animation at the moment.
// TODO: Stop animation on touch event and return to drag mode.
boolean result = true;
result = gestureDetector.onTouchEvent(me);//return the double tap events
if(result)
return false;
switch (me.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
private String fileName;
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
// event when double tap occurs
#Override
public boolean onDoubleTap(MotionEvent e) {
float x = e.getX();
float y = e.getY();
if(player!=null && player.isPlaying())
{
player.stop();
}
else
{
setFileName(x);
player = new AudioPlayer(fileName);
player.play();
}
return true;
}
}
I am doing some code in Action_UP event. So even when I do a double tap, for the first tap the ACTION_UP event is called and the corresponding code is executed. I want to prevent this from happening. How can I do this. I just want to make sure that the ACTION_UP is called only when the user is done with his gesture. Not immediately after his partial gesture . In this case its being called on finger up of first tap.
How can I make this work in android?
Thanks
According to gesture listener documentation, you need to return true for onSingleTapConfirmed & onDoubleTap. Otherwise the event is not consumed and gestureDetector.onTouchEvent(me); returns false.
Why do ViewGroup's only get ACTION_DOWN in the onInterceptTouchEvent? According to the docs, as long as false is returned it should receive all the event types.
http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent%28android.view.MotionEvent%29
Point #3.
Sample code:
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Container(this));
}
private class Container extends LinearLayout {
public Container(Context context) {
super(context);
setBackgroundColor(0xFF0000FF);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent");
int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "onInterceptTouchEvent.ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onInterceptTouchEvent.ACTION_MOVE");
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Log.i(TAG, "onInterceptTouchEvent.ACTION_UP");
break;
}
return super.onInterceptTouchEvent(ev);
}
}
}
I'll answer my own question: onInterceptTouchEvent only get called if the parent has a child view which returns "true" from onTouchEvent. Once the child returns true, the parent now has a chance to intercept that event.
I get the same problem. I had read many posts about it:
onInterceptTouchEvent only gets ACTION_DOWN
onInterceptTouchEvent's ACTION_UP and ACTION_MOVE never gets called
onInterceptTouchEvent, onTouchEvent only see ACTION_DOWN
onInterceptTouchEvent never receives action_move
I also had read android doc:
http://developer.android.com/training/gestures/viewgroup.html
http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)
All answers are same. I tried many times, always not get onInterceptTouchEvent
() be called if not down event.
I read source code, I guess that something is changed:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
According above code, onInterceptTouchEvent(ev) is only be called when MotionEvent.ACTION_DOWN, this is what we tried and found. So, what I guess is, the code is changed, but doc not.
If you want spy or monitor all the events include those been sent to child views, you can override dispatchTouchEvent() like this:
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
MyLog.d(MyLog.DEBUG, "dispatchTouchEvent(): "+event.getAction());
if (isEnabled()) {
MyLog.d(MyLog.DEBUG, "dispatchTouchEvent()2: "+event.getAction());
processEvent(event);//here you get all events include move & up
super.dispatchTouchEvent(event);
return true; //to keep receive event that follow down event
}
return super.dispatchTouchEvent(event);
}
I have the runnable code at: https://github.com/maxyou/gesturebutton/blob/master/src/com/maxproj/gesturebutton/GestureButtonLayout.java
dispatchTouchEvent, onInterceptTouchEvent, requestDisallowInterceptTouchEvent
[Touch event flow]
The official doc
Activity.dispatchTouchEvent(MotionEvent) - This allows your Activity to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent) - This allows a ViewGroup to watch events as they are dispatched to child Views. It is recursively function (from parent to parent)
ViewParent.requestDisallowInterceptTouchEvent(boolean) - Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).
I just registered an OnLongClickListener on my my MapView on an Android app I'm currently writing. For some reason however the onLongClick event doesn't fire.
Here's what I've written so far:
public class FriendMapActivity extends MapActivity implements OnLongClickListener {
private static final int CENTER_MAP = Menu.FIRST;
private MapView mapView;
private MapController mapController;
//...
private boolean doCenterMap = true;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.friendmapview);
this.mapView = (MapView) findViewById(R.id.map_view);
this.mapController = mapView.getController();
mapView.setBuiltInZoomControls(true);
mapView.displayZoomControls(true);
mapView.setLongClickable(true);
mapView.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
//NEVER FIRES!!
return false;
}
});
//...
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_3:
mapController.zoomIn();
break;
case KeyEvent.KEYCODE_1:
mapController.zoomOut();
break;
}
return super.onKeyDown(keyCode, event);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int actionType = ev.getAction();
switch (actionType) {
case MotionEvent.ACTION_MOVE:
doCenterMap = false;
break;
}
return super.dispatchTouchEvent(ev);
}
...
}
May overlays which I'm adding cause the problem?? Any suggestions?
I ran into the same problem and there is a simple solution to your problem actually; it's because you're using the wrong type of listener.
You should use the OnMapLongClickListener() object from the OnMapLongClickListener interface.
Hopefully everything should work properly :)
Please tell me if it works.
I just ran into this problem. I tried the solution above, but it doesn't quite work 100% in that we want the long press action to fire, even if the user is still holding a finger down.
This is how I implemented a solution, using a handler and a delayed task -
As a side note, I used a similar type implementation, but in reverse, to hide/show zoom controls on touch/etc..
private Handler mHandler = new Handler();
private final Runnable mTask = new Runnable() {
#Override
public void run() {
// your code here
}
};
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// record the start time, start the timer
mEventStartTime = ev.getEventTime();
mHandler.postDelayed(mTask, LONG_PRESS_TIME);
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
// record the end time, dont show if not long enough
mEventEndTime = ev.getEventTime();
if (mEventEndTime - mEventStartTime < LONG_PRESS_TIME) {
mHandler.removeCallbacks(mTask);
}
} else {
// moving, panning, etc .. up to you whether you want to
// count this as a long press - reset timing to start from now
mEventStartTime = ev.getEventTime();
mHandler.removeCallbacks(mTask);
mHandler.postDelayed(mTask, LONG_PRESS_TIME);
}
return super.onTouchEvent(ev);
}
In the mean time I found the "solution" (or workaround, call it as you like) by myself. The way I worked through this issue is by using a GestureDetector and forwarding all touch events to that object by implementing an according OnGestureListener interface.
I've posted some code on my blog if anyone is interested:
http://juristr.com/blog/2009/12/mapview-doesnt-fire-onlongclick-event/
Don't ask me why this didn't work by hooking up the OnLongClickListener directly on the MapView. If someone has an explanation let me know :)
UPDATE:
My previously suggested solution using a GestureDetector posed some drawbacks. So I updated the blog post on my site.
In WebView framework code performLongClick() is used to handle long press event, this is how Android copy Text Feature is implemented in Browser, that is why onLongClick is not been fired.