I have a list view and I want to make it so I can swipe each item to reveal a delete button:
I've figured out how to recognise the swipe event, I am using this code (listItem is of type View):
listItem.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
_xSwipe1 = event.getX();
break;
case MotionEvent.ACTION_UP:
_xSwipe2 = event.getX();
float deltaX = _xSwipe2 - _xSwipe1;
if (deltaX < 0)
{
Log.e("SWIPE", "Right to Left swipe");
}
else if (deltaX >0)
{
Log.e("SWIPE", "Left to right swipe");
}
break;
}
return false;
}
});
And when I swipe, I can see in the logs that the swipe event is being recognised.
However, I'm not sure how to physically make the list item start disappearing to the left?
Any help would be greatly appreciated.
If you know when the swipe is happening and how much the user has swiped you could call View.setTranslationX() depending on how much the user has swiped. Alternatively, you could forego the swipe detection logic altogether and use a ViewPager with only 2 pages. You would then override PagerAdapter.getPageWidth(...) in your PagerAdapter so that the delete button only takes up a limited amount of space.
Related
I have WebView(s) inside the RecyclerView. In order to get smooth scrolling experience such that when user scrolls, the RecyclerView will be responsible for scrolling (WebView should not scroll) I called getParent().requestDisallowInterceptTouchEvent(false); inside webview#onTouchEvent(event) when there is only one touch point and is moving vertcially (scrolling up and down).
private void handleSingleFingerTouch(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = ev.getX();
y1 = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
x2 = ev.getX();
y2 = ev.getY();
// -ve param for canScrollHorizontally is to check scroll left. +ve otherwise
if (Math.abs(x1 - x2) >= Math.abs(y1 - y2)
&& canScrollHorizontally((int) (x1 - x2))) {
// scrolling horizontally. Retain the touch inside the webView.
getParent().requestDisallowInterceptTouchEvent(true);
} else {
// scrolling vertically. Share the touch event with the parent.
getParent().requestDisallowInterceptTouchEvent(false);
}
x1 = x2;
y1 = y2;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
boolean multiTouch = ev.getPointerCount() > 1;
if (multiTouch) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
handleSingleFingerTouch(ev);
}
return super.onTouchEvent(ev);
}
It works as expected with just one bug, I found that while RecyclerView(and webview) scrolling and I touch inside the WebView, then RecyclerView stops scrolling as expected, then if I don't lift up my finger but keep finger on the screen and try to zoom, the webview would not zoom and actually it wouldn't receive touch event at all. I have to lift my fingers and touch again to zoom. I know this is because getParent().requestDisallowInterceptTouchEvent(false); won't cancel unless UI receive CANCEL or UP event. I tried to implement an interface that call getParent().requestDisallowInterceptTouchEvent(true); when multi-touch happen. Though it did get called, but seems it doesn't work. Zoom still not happen and onTouchEvent inside the WebView still not get triggered. Any idea to solve this?
So basically the solution is to override the onInterceptTouchEvent of the recyclerView
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
// When recyclerview is scrolling this will stop scrolling and allow touch event passed to child views.
if (e.action == MotionEvent.ACTION_DOWN && this.scrollState == RecyclerView.SCROLL_STATE_SETTLING) {
this.stopScroll()
}
return super.onInterceptTouchEvent(e)
}
I have a custom view, assume it looks like this:
I would like for my custom view to respond to the onClicks, however the catch is that I would like it to respond to the clicks ONLY on the red portion/circle. Not the whole view.
Is it possible to make to make the text above and the grey portion not clickable?
Thank you.
In a custom view, you handle clicks by overriding the onTouchEvent method of android's View class. First check that the location the user has clicked is within the circle. Then normally you would give some feedback on the MotionEvent.ACTION_DOWN event to let user know they have clicked, such as highlight the circle. Then on MotionEvent.ACTION_UP you can call your onClick method.
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean isTouchInCircle = checkTouchInCircle(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isTouchInCircle) {
circleColor = highlightColor;
invalidate();
}
break;
case MotionEvent.ACTION_MOVE:
if (isTouchInCircle) {
circleColor = highlightColor;
} else {
circleColor = normalColor
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if (isTouchInCircle) {
onClickCircle();
}
break;
}
return true;
}
// Circle click zone approximated as a square
private boolean checkTouchInCircle(float touchX, float touchY) {
if (touchX < circleCenterX + circleRadius
&& touchX > circleCenterX - circleRadius
&& touchY < circleCenterY + circleRadius
&& touchY > circleCenterY - circleRadius) {
return true;
} else {
return false;
}
}
Unfortunately the answer Carson posted was not exactly what I was looking for as my example was only a simple one, with the reality sometimes it being a lot more difficult and checking the touch locations would be convoluted (imagine multiple views/shapes within the custom view being the click locations).
What I did was in the custom view do a find view by id on the elements of the custom view. Then I did setOnClickListener(this) on each element that I would like to be clicked able rather than on the whole view itself, so mCircle.setOnClickListener(this); and mInput.setOnClickListener(this);, then did implements View.OnClickListener for the custom view to handle the actions.
I have a ViewPager of images, and I want to make it slide in the following way:
If I click on the right (left) side of the screen, go to previous (next) image. It's like the screen is two halves and each half moves to a direction on click.
How can I implement this?
You can get Disaplay width
Then you can override onTouchEvent in your viewpager.
When you get MotionEvent object - you can get 'tap' coordinates and you will be able to understand on wich side of screen user clicked and handle it.
Set on touch listener for your ViewPager or contentview of ViewPager item.
Take the half of the screen/view width, check if event.getX() is greater than the half value, if so, go to next, otherwise to the previous.
#Override
public boolean onTouch(View v, MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
mDownX = ev.getX();
mDownY = ev.getY();
isOnClick = true;
}else if(ev.getAction() == MotionEvent.ACTION_UP ){
if (isOnClick ) {
if(ev.getX() > mViewPager.getWidth()/2) {
//go to next
}else{
//go to previous
}
return true;
}
}else if(ev.getAction() == MotionEvent.ACTION_MOVE){
if (isOnClick && (Math.abs(mDownX - ev.getX()) > DRAG_THRESHOLD || Math.abs(mDownY - ev.getY()) > DRAG_THRESHOLD)) {
isOnClick = false;
return false;
}
}
return false
}
I have some ImageViews inside a HorizontalScrollView.
I would like to be able to drag and drop the ImageViews somewhere else, but still maintain scrolling capability. Dragging should only be activated when the user starts a mostly vertical motion with their finger.
For now, I have drag and drop activate on long-press, but that is not a good solution.
To illustrate:
I had to do exactly this as well. After reading http://techin-android.blogspot.in/2011/11/swipe-event-in-android-scrollview.html I adapted the code as follows:
class MyOnTouchListener implements View.OnTouchListener {
static final int MIN_DISTANCE_Y = 40;
private float downY, upY;
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
downY = event.getY();
return true;
}
case MotionEvent.ACTION_MOVE: {
upY = event.getY();
float deltaY = downY - upY;
// swipe vertical?
if (Math.abs(deltaY) > MIN_DISTANCE_Y) {
if (deltaY < 0) {
//Start your drag here if appropriate
return true;
}
if (deltaY > 0) {
//Or start your drag here if appropriate
return true;
}
}
}
}
return false;
}
}
And then set the listener on the ImageViews:
imageView.setOnTouchListener(new MyOnTouchListener());
In this version of the code I am only checking for movement in the vertical direction (I also changed the minimum movement to be 40 instead of 100 as in the original code). If a vertical movement is detected, the specific ImageView can begin to drag or do any other actions you want. If a vertical movement is not detected, the ImageView's MyTouchListener returns false which means the ImageView does not consume the touch event. This allows the parent ScrollView to eventually get the touch event and consume it (for scroll detection). The answers here are helpful for understanding touch events: MotionEvent handling in ScrollView in Android.
I have a ViewFlipper and in it, four scrollviews with layouts in those.
I use the following code (for each scrollview) to swipe:
ScrollView View1 = (ScrollView) findViewById(R.id.View1);
View1.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_DOWN) {
FirstX = (int) event.getX();
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
int LastX = (int) event.getX();
if (FirstX - LastX > SWIPE_MIN_DISTANCE) {
viewFlipper.setInAnimation(slideLeftIn);
viewFlipper.setOutAnimation(slideLeftOut);
viewFlipper.showNext();
} else if (LastX - FirstX > SWIPE_MIN_DISTANCE) {
viewFlipper.setInAnimation(slideRightIn);
viewFlipper.setOutAnimation(slideRightOut);
viewFlipper.showPrevious();
}
}
return true;
}
});
It works, but it looks like if I swipe from View 1 to View 2, I see a views 2 and 3 mixed together and it finally shows view 4.
So it seems the OnTouchListeners for each view are called after eachother.
How can I prevent this from happening?
A short and fast swipe does what it is supposed to do.
rg,
Eric
You should move your code you have in MotionEvent.ACTION_MOVE to MotionEvent.ACTION_UP to get a swipe. Now, if you don't want swipe, and want to move the screen along with the finger, you should implement ViewPager as JafarKhQ just mentioned.
the setOnTouchListener will called multible time (while you sliding your finger), so the
viewFlipper.setInAnimation(slideLeftIn);
viewFlipper.setOutAnimation(slideLeftOut);
viewFlipper.showNext();
will called multiple time
i suggest you to use the ViewPager instead of ViewFlipper.