I'm a little confused by polymorphism and interfaces in Java.
Basically I want to make a class called gestureRecogniser which has the methods:
-onLeftwardsSwipe();
-onRightwardsSwipe();
-onUpwardsSwipe();
-onDownwardsSwipe();
The gestureRecogniser will call these methods as needed. A second class implements gestureRecogniser and specifies what must be done on these events. Is this possible in Java?
This is the class that I've written:
public abstract class GestureRecogniser implements OnTouchListener {
Activity activity;
private float XWherePressed;
private float XWhereReleased;
private float YWherePressed;
private float YWhereReleased;
private static final int MINIMUM_DISTANCE_FOR_SWIPE = 100;
public GestureRecogniser(Activity activity)
{
this.activity = activity;
}
public abstract void onLeftwardsSwipe();
public abstract void onRightwardsSwipe();
public abstract void onUpwardsSwipe();
public abstract void onDownwardsSwipe();
public boolean onTouch(View v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
this.XWherePressed = event.getX();
this.YWherePressed = event.getY();
break;
case MotionEvent.ACTION_UP:
this.XWhereReleased = event.getX();
this.YWhereReleased = event.getY();
float deltaX = XWhereReleased - XWherePressed;
float deltaY = YWhereReleased - YWherePressed;
if (Math.abs( deltaX ) > this.MINIMUM_DISTANCE_FOR_SWIPE)
{
//HORIZONTAL SWIPE
if(deltaX > 0 )
{
this.onRightwardsSwipe();
return true;
}
if(deltaX < 0)
{
this.onLeftwardsSwipe();
return true;
}
}else return false;
if (Math.abs( deltaY) > this.MINIMUM_DISTANCE_FOR_SWIPE)
{
//VERTICAL SWIPE
if( deltaY < 0 )
{
this.onDownwardsSwipe();
return true;
}
if ( deltaY > 0 )
{
this.onUpwardsSwipe();
return true;
}
}else return false;
break;
}
return false;
}
}
I managed to achieve the desired result by doing the following:
1 - Create a public interface with the methods:
public interface OnSwipeListener
{
public void onUpwardsSwipe();
public void onDownwardsSwipe();
public void onLeftwardsSwipe();
public void onRightwardsSwipe();
}
2 - Create a class that implements onTouchListener. The class' constructor takes an OnSwipeListener object. The onTouch() method calls the methods as needed:
public class SwipeRecogniser implements OnTouchListener {
private OnSwipeListener onSwipeListener;
private float XWherePressed;
private float XWhereReleased;
private float YWherePressed;
private float YWhereReleased;
private static final int MINIMUM_DISTANCE_FOR_SWIPE = 100;
public SwipeRecogniser(OnSwipeListener onSwipeListener)
{
this.onSwipeListener = onSwipeListener;
}
public boolean onTouch(View v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
this.XWherePressed = event.getX();
this.YWherePressed = event.getY();
break;
case MotionEvent.ACTION_UP:
this.XWhereReleased = event.getX();
this.YWhereReleased = event.getY();
float deltaX = XWhereReleased - XWherePressed;
float deltaY = YWhereReleased - YWherePressed;
if (Math.abs( deltaX ) > this.MINIMUM_DISTANCE_FOR_SWIPE)
{
//HORIZONTAL SWIPE
if(deltaX > 0 )
{
onSwipeListener.onRightwardsSwipe();
return true;
}
if(deltaX < 0)
{
onSwipeListener.onLeftwardsSwipe();
return true;
}
}
else return true;
if (Math.abs( deltaY) > this.MINIMUM_DISTANCE_FOR_SWIPE)
{
//VERTICAL SWIPE
if( deltaY < 0 )
{
onSwipeListener.onDownwardsSwipe();
return true;
}
if ( deltaY > 0 )
{
onSwipeListener.onUpwardsSwipe();
return true;
}
}
else return true;
break;
}
return true;
}
}
3 - In the custom class
public class MyClass extends Activity implements OnSwipeListener
{
protected void onCreate(Bundle stuff)
{
//
this.setOnTouchListener(new SwipeRecogniser(this));
}
public void onUpwardsSwipe(){/*methods*/}
public void onDownwardsSwipe(){/*methods*/}
public void onLeftwardsSwipe(){/*methods*/}
public void onRightwardsSwipe(){/*methods*/}
}
//Declare below inner class
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
// right to left swipe
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
////swapped left
}
else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
////swapped right
}
} catch (Exception e) {
// nothing
}
return false;
}
}
//in your activtiy do below stuff and override onTouchEvent
private static final int SWIPE_MIN_DISTANCE = 50;
private static final int SWIPE_MAX_OFF_PATH = 400;
private static final int SWIPE_THRESHOLD_VELOCITY = 250;
private GestureDetector gestureDetector;
View.OnTouchListener gestureListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Do your stuff
gestureDetector = new GestureDetector(new MyGestureDetector());
gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return false;
}
};
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}
Related
I had a recyclerview with an adapter and everything was cool. I added view.setOnTouchListener so that I could scroll this recyclerview along with animation and other elements on 1 screen from left to right. But after that, scrolling up and down broke in recyclerview, and onclicklistener on the elements inside it stopped working. What to do and how to fix this conflict?
At the moment I can put return false in the public boolean onTouch (View v, MotionEvent event) method; instead of return gestureDetector.onTouchEvent (event); and get a working scrolling up and down back, but left-right stops working. I can do simultaneous scrolling as in ios, when scrolling of tablecloths and collections did not break when adding the svayp to the left-right.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {
public void onSwipeRight() {
choseold = chosenow;
chosenow = chosenow - 1;
if (chosenow <= 0) {
chosenow = 5;
choseold = 6;
}
swipe();
}
public void onSwipeLeft() {
choseold = chosenow;
chosenow = chosenow + 1;
if (chosenow >= 6) {
chosenow = 1;
choseold = 0;
}
swipe();
}
});
view.setFocusableInTouchMode(true);
MyAdapterlang = new MyAdapterLang(MainActivity.this, yaziki1, yaziki2, flagi);
RVlang.setAdapter(MyAdapterlang);
mLayoutManager = new LinearLayoutManager(this);
RV1.setLayoutManager(mLayoutManager);
MyAdapter = new MyAdapterApps(MainActivity.this, childs, childs2);
RV1.setAdapter(MyAdapter);
}
}
class OnSwipeTouchListener implements View.OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
//return false;
}
/*public boolean onTouch(final View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
y1 = event.getY();
break;
case MotionEvent.ACTION_MOVE:
y2 = event.getY();
float deltaY = y2 - y1;
if (Math.abs(deltaY) > MIN_DISTANCE) {
return false;
} else {
return gestureDetector.onTouchEvent(event);
}
}
return gestureDetector.onTouchEvent(event);
}
private float y1, y2;
private static final int MIN_DISTANCE = 50;*/
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
return true;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return false;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
}
Here in ios, for example, is very simple and does not break the scrolling of collections:
- (void)viewDidLoad {
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeleft:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeleft];
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swiperight:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
-(void)swipeleft:(UISwipeGestureRecognizer*)gestureRecognizer
{
if (self.currentnew <= 3) {
self.whatpress = 1;
[self.buttonnew sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
-(void)swiperight:(UISwipeGestureRecognizer*)gestureRecognizer
{
if ((self.currentnew >= 1) && (self.currentnew <= 3)) {
self.whatpress = 2;
[self.buttonnew sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
Here is a video:
https://2ch.hk/pr/src/1314926/15464190246250.mp4 https://2ch.hk/pr/src/1314926/15464193253190.mp4
If you only want to hook into default touch handling implementation you must return false here
#Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);//Idk if it is needed or not - out of scope
return false;
}
Otherwise touch event will be considered as consumed and wont be propagated to other handlers (that handles scrolling, clicking etc. - all that stuff that stops working for you)
I have a fragment called ProfileFragment.java which I want to listen for touch gestures by calling a class OnSwipeTouchListener.
Here is a snippet of code from ProfileFragment.java:
rootLayout.setOnTouchListener(new OnSwipeTouchListener(ProfileFragment.this) {
#Override
public void onSwipeTop() {
super.onSwipeTop();
//folder_gridview.setVisibility(View.INVISIBLE);
// do something
}
});
From the above code I get this error:
OnSwipeTouchListener (android.content.Context) in OnSwipeTouchListener cannot be applied to (com.mydomain.sample.ProfileFragment)
Here is the code for OnSwipeTouchListener.java class:
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener(Context ctx) {
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 1;
private static final int SWIPE_VELOCITY_THRESHOLD = 1;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
result = true;
} else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
result = true;
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeTop() {
}
public void onSwipeBottom() {
}
}
I was able to get the above code to work on an activity but cannot get it to work on a fragment. How do I get it to work on a fragment?
You have to pass the context param on your OnSwipeTouchListener, but instead you pass the fragment, ProfileFragment.this which does not inherit from context.
You should pass the getActivity() as param on your OnSwipeTouchListener like this:
rootLayout.setOnTouchListener(new OnSwipeTouchListener(getActivity()) {
/*rest of your code*/
});
In my new program I need objects that can be swiped to the side. I already have my animation, which is working and I tried to detect the swiping of the user in another class. The problem I have is that I don't know how to connect them. When the swipe gesture is correctly recognized a specific animation should start.
My AnimatedViewClass:
private Runnable r = new Runnable() {
#Override
public void run() {
if(continueAnimation) {
invalidate();
}
}
};
protected void onDraw(Canvas c) {
if (x<0) {
x = this.getWidth()/2-100;
y = this.getHeight()/2-100;
}
else {
x += xVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
boolean continueAnimation = false;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
if(continueAnimation)
{
h.postDelayed(r, FRAME_RATE);
}
else {
x = this.getWidth()-ball.getBitmap().getWidth();
}
}
My SwipeTouchListener:
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
result = true;
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
result = true;
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
}
You can add GestureDetector to your view class just like this, and replace your code inside onFling()
public class AnimatedViewClass extends View {
GestureDetector gestureDetector;
public AnimatedViewClass(Context context) {
super(context);
gestureDetector = new GestureDetector(getContext(), new GestureDetectorListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
private void onSwipeRight(){
// swipe right detected
// do stuff
invalidate();
}
private void onSwipeLeft(){
// swipe left detected
// do stuff
invalidate();
}
private class GestureDetectorListener extends
GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// onSwipeRight()
// onSwipeLeft()
return super.onFling(e1, e2, velocityX, velocityY);
}
#Override
public boolean onDown(MotionEvent e) {
return super.onDown(e);
}
}
private Runnable r = new Runnable() {
#Override
public void run() {
if(continueAnimation) {
invalidate();
}
}
};
protected void onDraw(Canvas c) {
if (x < 0) {
x = this.getWidth() / 2 - 100;
y = this.getHeight() / 2 - 100;
} else {
x += xVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
boolean continueAnimation = false;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
if (continueAnimation) {
h.postDelayed(r, FRAME_RATE);
} else {
x = this.getWidth() - ball.getBitmap().getWidth();
}
}
}
I'm making a list with a linearLayout and adding TextViews depending on how many items I got in the database. Problem is that I want to be able to delete an item when I swipe left on the list. I was just wondering how to get the element (view) that you're swiping? Here's the code I got for adding a TextView.
for(int i = 0; i < nrOfTodos; i++) {
TextView v = new TextView(this);
String title = todos.get(i).getTitle();
System.out.println(title);
v.setText(title);
v.setHeight(listItemSize);
v.setAllCaps(true);
v.setTextColor(Color.parseColor("#FFD9A7"));
v.setTextSize(listItemSize/6);
v.setHorizontallyScrolling(false);
v.setMaxLines(1);
v.setEllipsize(TruncateAt.END);
v.setPadding(30, 50, 0, 0);
v.setId(i);
todoViews.add(v);
v.setOnTouchListener(new OnSwipeTouchListener(this) {
#Override
public void onSwipeLeft() {
/*
*
*
* TODO!!! NEXT PART OF MY PLAN TO WORLD DOMINATION!
*
*
*/
}
});
if(i%2 == 1) {
v.setBackgroundColor(Color.parseColor("#11FFFFFF"));
}
ll.addView(v, i);
}
and I also have this
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener(Context context) {
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void onSwipeLeft() {
}
public void onSwipeRight() {
}
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_DISTANCE_THRESHOLD = 50;
private static final int SWIPE_VELOCITY_THRESHOLD = 10;
#Override
public boolean onDown(MotionEvent e) {
//always return true since all gestures always begin with onDown and <br>
//if this returns false, the framework won't try to pick up onFling for example.
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float distanceX = e2.getX() - e1.getX();
float distanceY = e2.getY() - e1.getY();
if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (distanceX > 0)
onSwipeRight();
else
onSwipeLeft();
return true;
}
return false;
}
}
}
but I can't figure out how to actually get the view, get the titletext and remove that from the database (I can remove from the database, but I can't get what item was swiped ^^).
Any ideas would be greatly appreciated.
You may refer to this sample application Swipe_To_Delete
You may use ListView to list your data items which are got from the db.
Then modify the getSwipeItem() method in the MainActivity of the example, as follows.
#Override
public void getSwipeItem(boolean isRight, int position) {
[List_Of_Items].remove(position);
[Your_Adapter].notifyDataSetChanged();
}
I would like to have a OnTouchListener for the entire screen. I've tried attaching all views to an onTouchListener but generates bad touchEvents. I know this can be achived by overriding methods, what i am looking for is a listener solution. Thanks!
Can this be done using a gesture listener?
You can insert a onSwipeListener class with methods that listen to the swipe action. You can then set view.OnTouchListener for the layout of the activity(LinearLayout/RelativeLayout) and then override the various methods of the onSwipeListener and insert the various tasks.
Below is the onSwipeListener class that you can create.
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
result = true;
}
else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
result = true;
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
public void onSwipeTop() {
}
public void onSwipeBottom() {
}
}
After creating this class, you can call it as follows:
relativeLayout.setOnTouchListener(new OnSwipeTouchListener(context) {
public void onSwipeRight() {
//do something
}
public void onSwipeLeft() {
//do something
}
}
Hope this helps!