Background: I'm trying to detect edge swipes in my app (to bring up a menu), and searching stackoverflow seems to indicate that the way to detect swipes is to start with fling detection.
I'm trying to detect a fling event in my app. I can override dispatchTouchEvent() or onTouchEvent() and pass the event to a gesture listener and everything works as you would expect.
However, my app has button widgets in it and I can't both detect the fling and use the widgets.
If I call the gesture detector from onTouchEvent(), flings are not detected if the gesture starts over a widget that consumes the event. If I call the gesture detector from dispatchTouchEvent(), the widgets don't get the events they need.
I've also tried attaching to the container's onTouchEvent() but the result was the same.
Finally, I've looked at the source code to ViewPager to see how they do it, and what they do is override onInterceptTouchEvent(). I can see how and why this works, but I was hoping there was a simpler solution that didn't require me to implement my own container widget.
Source code:
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
public class Fling extends Activity {
private static final String TAG = "FlingTest";
protected GestureDetector gestureDetector;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gestureDetector = new GestureDetector(this, listener);
}
/*
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
return super.dispatchTouchEvent(event);
}
*/
#Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final SimpleOnGestureListener listener =
new SimpleOnGestureListener() {
public boolean onDown(MotionEvent e1) { return true; }
public boolean onFling(MotionEvent e1, MotionEvent e2, float vx, float vy) {
Log.d(TAG, "Fling: " + vx + "," + vy);
return true;
}
};
}
The layout.xml file, although pretty much any layout will show these issues:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/buttonPane"
android:background="#446"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:text="btn"
android:layout_width="wrap_content" android:layout_height="wrap_content"
/>
<Button
android:text="btn"
android:layout_width="wrap_content" android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:text="btn"
android:layout_width="wrap_content" android:layout_height="wrap_content"
/>
<Button
android:text="btn"
android:layout_width="wrap_content" android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>
I've found one answer that seems to work. I change dispatchTouchEvent() to both call the gesture detector and pass the event up the chain.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
return super.dispatchTouchEvent(event);
}
It's not pretty, and I'm not sure it's robust, but it seems to work.
Still hoping for a better answer, but I think I'll give this method a try.
Related
I am developing a game application using android.
The Object(a box) slides up and down. And it has to hit the
objects(orange and pink balls) coming towards it from the right end of
the screen that would increase his score.
There will be black balls as well(shot from the right end of the
screen) which he should avoid hitting.
I am having problem with
onTouchEvent(MotionEvent me)
function while implementing the code.
I am following this tutorial in this series of tutorials.
My Questions:
To use the onTouchEvent(MotionEvent me) function do I need to import any class?
The tutorial has declared theonTouchEvent(MotionEvent me) outside the onCreate method. Which is okay. But the program has not called it anywhere. How does it work then?
After writing the code as mentioned in the tutorial, the program is not working as intended. The box appears when the activity starts. However, it disappears as soon as I click on the screen. What could be the problem?
ActivityMain.XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/scoreLabel"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text=" : 300"
android:paddingLeft="10dp"
android:gravity="center_vertical" />
<FrameLayout
android:id="#+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/startLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="130dp"/>
<ImageView
android:id="#+id/box"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/box"
android:layout_gravity="center_vertical" />
<ImageView
android:id="#+id/orange"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="#drawable/orange" />
<ImageView
android:id="#+id/black"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="#drawable/black" />
<ImageView
android:id="#+id/pink"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="#drawable/pink" />
</FrameLayout>
</RelativeLayout>
MainActivity.java
package com.example.catcheggs1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView scoreLabel;
private TextView startLabel;
private ImageView box;
private ImageView orange;
private ImageView black;
private ImageView pink;
//Position
private int boxY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scoreLabel=(TextView)findViewById(R.id.scoreLabel);
startLabel=(TextView)findViewById(R.id.startLabel);
box=(ImageView)findViewById(R.id.box);
orange=(ImageView)findViewById(R.id.orange);
pink=(ImageView)findViewById(R.id.pink);
black=(ImageView)findViewById(R.id.black);
//Move To Out of Screen
orange.setX(-80);
orange.setY(-80);
pink.setX(-80);
pink.setY(-80);
black.setX(-80);
black.setY(-80);
//Temporary
startLabel.setVisibility(View.INVISIBLE);
boxY=500;
}
public boolean onTouchEvent(MotionEvent me)
{
if(me.getAction()==MotionEvent.ACTION_DOWN)
{
boxY -= 1 ;
}
box.setY(boxY);
return true;
}
}
1.) No, you do not need to import anything.
2.) For detailed information you can see the link.
It is an event method, it is automatically called, you do not need to call it.
https://developer.android.com/guide/topics/ui/ui-events#java
3.) you use boxY variable when touched and it is assigned an arbitrary number. Your problem is much likely to be caused by that. Rather than that, you should first get the current position than tweak it.
You can get current position with this method.
int[] location = new int[2];
imageView.getLocationOnScreen(location);
https://developer.android.com/reference/android/view/View.html#getLocationOnScreen(int[])
Bonus.) onTouchEvent is an activity method, so you should use view's own event listeners for specific tasks rather than onTouchEvent.
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
im new with Android, but have experiance in c# and c++.
Im just trying to display whenever a button is pressed. I searched and found out, that the onClick event cant do that, therefore i use the onTouch-event.
For the first button it was fine and worked. Then I added a sencond and a third one, but now the application crashes, when I execute it.
If I delete the second and the third button with their events, the application still crashes. Not before I delete the first Button and his event the application stops crashing.
here the code of my main.code and the layout-xml:
package com.android.kurve;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity{
boolean left=false;
boolean right=false;
final Button buttonLeft = (Button) findViewById(R.id.button1);
final Button buttonRight = (Button) findViewById(R.id.button2);
final Button buttonMove = (Button) findViewById(R.id.button3);
final TextView text = (TextView) findViewById(R.id.textView1);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonLeft.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
left=true;
text.setText("left");
} else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
left=false;
}
return true;
}
});
buttonRight.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
right=true;
text.setText("right");
} else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
right=false;
}
return true;
}
});
buttonMove.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
text.setText("move");
return true;
}
});
}
}
and here the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="28dp"
android:layout_marginTop="207dp"
android:text="Left" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="#+id/button1"
android:layout_marginRight="31dp"
android:text="Right" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/button2"
android:layout_centerHorizontal="true" />
<Button
android:id="#+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView1"
android:layout_marginTop="24dp"
android:layout_toLeftOf="#+id/textView1"
android:text="Move" />
</RelativeLayout>
Thanks for the help.
Maybe the problem could be that you are initializing the layout element globally instead to initializing them in the "onCreate" method. This method is called when the activity is created, so this is the place where all the layout behaviour or values should be initialized.
I had tried a code for Switching Screen by dragging over the touch screen..but it gives an error...can anyone please check what mistake i have done in the code..and error arise at
vf.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_in));
vf.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_out));
code for .xml file
<ViewFlipper android:id="#+id/details"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<TextView android:id="#+id/tv_country"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textStyle="bold"
android:textSize="18px"
android:text="Country" >
</TextView>
<Spinner android:text=""
android:id="#+id/spinner_country"
android:layout_width="200px"
android:layout_height="55px">
</Spinner>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<TextView android:id="#+id/tv_income"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textStyle="bold"
android:textSize="18px"
android:text="Income" >
</TextView>
<EditText android:text=""
android:id="#+id/et_income"
android:layout_width="200px"
android:layout_height="55px">
</EditText>
</LinearLayout>
</ViewFlipper>
code for .java file
package com.examples.switchactivtybydragging;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.*;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.ViewFlipper;
public class SwitchActivityByDraggingActivity extends Activity implements OnTouchListener {
/** Called when the activity is first created. */
private float downXValue;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layMain = (LinearLayout) findViewById(R.id.layout_main);
layMain.setOnTouchListener((OnTouchListener) this);
Spinner spinnerCountries = (Spinner) findViewById(R.id.spinner_country);
#SuppressWarnings({ "unchecked", "rawtypes" })
ArrayAdapter countryArrayAdapter = new ArrayAdapter(this,
android.R.layout.simple_spinner_dropdown_item,
new String[] { "Canada", "USA" });
spinnerCountries.setAdapter(countryArrayAdapter);
}
public boolean onTouch(View arg0, MotionEvent arg1) {
// Get the action that was done on this touch event
switch (arg1.getAction())
{
case MotionEvent.ACTION_DOWN:
{
// store the X value when the user's finger was pressed down
downXValue = arg1.getX();
break;
}
case MotionEvent.ACTION_UP:
{
// Get the X value when the user released his/her finger
float currentX = arg1.getX();
// going backwards: pushing stuff to the right
if (downXValue < currentX)
{
// Get a reference to the ViewFlipper
ViewFlipper vf = (ViewFlipper) findViewById(R.id.details);
// Set the animation
vf.setAnimation(AnimationUtils.loadAnimation(this,R.anim.push_left_out));
// Flip!
vf.showPrevious();
}
// going forwards: pushing stuff to the left
if (downXValue > currentX)
{
// Get a reference to the ViewFlipper
ViewFlipper vf = (ViewFlipper) findViewById(R.id.details);
// Set the animation
vf.setInAnimation(AnimationUtils.loadAnimation(this,R.anim.push_left_in));
// Flip!
vf.showNext();
}
break;
}
}
// if you return false, these actions will not be recorded
return true;
}
}
Your problem is that you haven't created the animations. For your code to work, you require animations in res/anim/push_left_in.xml and res/anim/push_left_out.xml. It is the absence of these files that is causing your error.
I have written a blog series about Simple Animations which may help you to understand how to create these files, and what you need to put in them.
Why will this code not draw a circle where the user touches in an imageview and output the coordinates to a textview?
package com.smallbore.smallbore;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.TextView;
public class targetenter extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.targetenter);
}
public class ImageView1 extends ImageView {
public int x;
public int y;
public ImageView1(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public boolean dispatchtouchevent(MotionEvent event){
x = (int) event.getX();
y = (int) event.getY();
TextView t1 = (TextView)findViewById(R.id.textView2);
t1.setText("x="+x);
TextView t2 = (TextView)findViewById(R.id.textView3);
t2.setText("y="+y);
invalidate();
return true;
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawCircle(x,y,10, null );
}
}
}
and the xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="#+id/linearLayout1" android:layout_height="fill_parent" android:layout_width="fill_parent">
<TextView android:layout_height="wrap_content" android:text="#string/cposition" android:id="#+id/textView1" android:layout_width="wrap_content"></TextView>
<com.smallbore.smallbore.targetenter.Imageview1 android:id="#+id/imageView1" android:layout_width="300dip" android:layout_height="300dip" android:background="#drawable/target" android:layout_gravity="center_horizontal"></com.smallbore.smallbore.targetenter.Imageview1>
<TextView android:text="TextView" android:id="#+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<TextView android:text="TextView" android:id="#+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
</LinearLayout>
Change onTouchEvent to dispatchTouchEvent. Also, make sure that you are using imageView1 in your XML, and not just ImageView.
You are not calling invalidate() in your onTouchEvent. When ever you change something on a canvas it must be redrawn. fyi, if you are not on UI thread you must call postinvalidate(). refer to this api demo to learn more about dealing with drawing on canvas. http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.html
I'm not sure that Cristian's suggestion of using dispatchTouchEvent() is a good one, that method makes sense in ViewGroup descendants to dispatch touch events to its children. Yet his recommendation of using the custom view in xml layout declaration is important. You do that like:
<package.that.contains.targetenter.imageView1 android:id ....
that is, you need to specify the full package to your custom ImageView class (btw, in Java there's the convention of using capital letter for class names: imageView1 -> ImageView1). You simply have to use it were you were using ImageView, and I think you will have to make it public or better declare it in another file.
To have your onDraw() method called, you'll need to call invalidate() as Veeresh suggests, so that your imageView1 will redraw. But you have to call its super, or you will only get the circles drawn:
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawCircle(x,y,10, null );
}
If you're still having problems it might be useful to see your targetenter.xml layout.
So I am a complete newb, and am currently taking an intro to Mobile Programming course in which we use Android (I have some experience with Java). I am trying to do a simple assignment which displays a text field and an image, and upon entering the correct "password" and pressing enter, the image changes.
Should be so simple! But I am having a really hard time with this and can't figure out what I am doing wrong, even after doing a good bit of searching (I assume it is something super obvious and I'm missing it).
Here is my code:
package CS285.Assignment1;
import android.app.Activity;
import android.os.Bundle;
import android.widget.EditText;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.ImageView;
public class DisplayImage extends Activity
implements OnKeyListener{
private EditText enteredText;
private String pass = "monkey";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
enteredText = (EditText)findViewById(R.id.passField);
enteredText.setOnKeyListener(this);
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)){
// Perform action on key press
switchImage();
return true;
}
return false;
}
public void switchImage(){
if(enteredText.getText().toString() == pass){
ImageView imgView = (ImageView)findViewById(R.id.Image);
imgView.setImageResource(R.drawable.marmoset);
}
}
}
and my main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="#+id/textPrompt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ff993300"
android:text="Please enter password to see my real picture:"
>
</TextView>
<EditText android:id="#+id/passField"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</EditText>
<ImageView
android:id="#+id/Image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:src="#drawable/airplane"
/>
</LinearLayout>
I thought at first that I was not properly extracting the String from "enteredText" so the comparison to the "password" wasn't happening correctly, but I have since tried just printing the enteredText and it works fine.
Totally frustrated--Any help would be greatly appreciated!
Daniel
if(enteredText.getText().toString() == pass) should be if(enteredText.getText().toString().equals(pass)).
As a stylistic matter, you should not do the checking within the switch image function - you should check that the password matches and then call the switch image function.