How to detect draglike behavior without ACTION_DOWN and ACTION_UP - java

My problem concerns onTouch. As far as I can tell, there is no way to detect an ACTION_DOWN for one view, and than detect the ACTION_UP for another view at the same onTouch event or swipe, as every onTouch event is linked to one view.
I have a view (A) with some graphics, and on EVENT_DOWN the coordinates are detected and make a "popup" that consists of a view (B) that ownes some more views ("buttons"). B's position and size may vary form time to time. I would prefer to detect ACTION_UP on one of B's children (the "buttons").
I guess using onInterceptTouchEvent is no good. After all, B is not A's parent. Its the other way around.
Using androids drag-and-drop functionality seems a bit too much. Looks like it's intended for actually dragging graphics and transfering data. Dragging invisible graphics around to detect the finger leaving the screen is not very elegant.
Another way would be detecting B's children's positions (and size), every time the popup is shown, but that is not very smooth either.
What is the best way to detect a view A's ACTION_DOWN, and then its child B's (or it's childrens) ACTION_UP?
Or are there other ways to detect these events?

I did struggle with something very similar for a day or two, and I finally concluded that as far as I know onInterceptTouchEvent is the only way to go. I just created a container view 'C' for A and B, and then when A received an ACTION_DOWN, I instructed 'C' to intercept all further events in the swipe, test the coordinates against B's known location, and then forward the event to 'B' through a custom method if the ACTION_UP tested positive against B's bounds (be careful manually passing MotionEvents from one class to another, as the coordinates will be all wrong).
Not a very satisfying solution, but it was the only thing I could think of to do.
Also just in case you're using it, android 3.0+ has some drag/drop helper methods which I'm sure are very helpful, though I was developing for 2.x so they were useless to me.

Related

How can I receive DragEvents of an ongoing drag for newly added Views?

As far as I can tell, Views only receive DragEvents if they had implemented onDragEvent() or had set an OnDragListener before startDrag() (or startDragAndDrop() for API 24+) is called. They will then continue to receive additional drag events if they return true for DragEvent.ACTION_DRAG_STARTED.
However, I am looking for a way to receive DragEvents after a drag operation had already started, for Views that got added to the layout during the drag operation.
To illustrate why, my situation is roughly the following:
I have a ViewPager2 with ListView fragments whose list items can be dragged. If I drag an item over another item, I "enter" that item and a new ListView fragment is shown with new child items. This works fine.
However, since these new child items didn't exists at the time of starting the drag operation, they don't receive DragEvents when I continue to drag the item over those new items.
So, basically, I want to be able to enter multiple levels deep in one continuous drag operation.
Is it possible to have newly added Views receive DragEvents for an ongoing drag operation?
Okay, I've managed to solve it by re-dispatching the original DragEvent (the one with action DragEvent.ACTION_DRAG_STARTED) to my main component, which is an instance of ViewGroup.
On inspecting the source code for ViewGroup.dispatchDragEvent() I found that ViewGroup has a member variable called mChildrenInterestedInDrag that gets filled with children interested in drag events, when DragEvent.ACTION_DRAG_STARTED is received.
So when I called ViewGroup.dispatchDragEvent() with the original DragEvent, after I entered an item in the list to view its child items, those new ListView child items were now responding to additional DragEvents.
I'm a bit worried that this approach might yield unexpected side effects. If I come across such effects upon further testing, I'll update this answer. But for now it will do.
If somebody knows that this indeed might yield unexpected side effects and/or has a better solution I'd love to hear them.
Addendum:
Upon storing the original DragEvent for re-dispatching, I had to "clone" the event, because, while it worked properly in an API 19 emulator, on an API 19 phone the original DragEvent's action were continuously altered during dragging, so its action wouldn't reflect DragEvent.ACTION_DRAG_STARTED anymore and re-dispatching didn't have the desired effect of registering newly added Views as interested in the ongoing drag operation.
Since DragEvents cannot be cloned or constructed, I had to "clone" it with the help of a Parcel:
void storeStartEvent(DragEvent startEvent) {
Parcel parcel = Parcel.obtain();
startEvent.writeToParcel(parcel, 0);
// important to "rewind" the Parcel first
parcel.setDataPosition(0);
this.startEvent = DragEvent.CREATOR.createFromParcel(parcel);
}
Other than that, I didn't experience any noticeable problems yet, so it may have just solved my issue nicely.

RecyclerView with swipe action menu

I have a RecyclerView with the implementation of ItemTouchHelper. I am dragging and sorting the items in RecyclerView using ItemTouchHelper.
Also I am performing different actions on different direction of swipe. If user goes from left to right (Swipe) I just Deactivate status of item. If he perform right to left swipe I activate the relevant item.
What I want:
But now here comes the change, What I want is that I need to show some icons after I perform right or left swipe. And By clicking on those icons/buttons I want to perform action then.
Problem:
Problem is as I told you I have implementation of ItemTouchHelper, then how I am doing to perform what I wanted (as described above)
How to solve this? I have seen many libraries but they have limitations, also as the app has very much things going and there are some more implementations on RecyclerView used in our app, so I really do not want to risk the rest of implementations for the sake of this..
UPDATE 1:
To clear my question more I am going to add on more. Right now My implementation is something like this. But this is not what I want. I want to show swipe menu for any type of swipe I mean either it is left or right I want to show some icons on left and right side of item
You can take iOS swipe menu as an example (see here). I want exactly same behavior in android.
You're asking the impossible: suggest a library that doesn't exist (as you have already found out).
You're asking people to also find a solution for a problem you only briefly describe. You're not providing any code whatsoever, nor any specific issue you're facing when trying to come up with a solution; you're essentially expecting magic to happen.
How can you make this better?
Provide something like: "I'm attempting to implement a swipe solution for a recycler view's row; when I try to do YYYY is happening instead. Hhere's my piece of code where I do , calculated like this , am I missing something?"
Anyway...
You claim to be using an ItemTouchHelper already, so, if you look at how a very basic one works, you'll notice that the helper will ultimately draw on the canvas directly via:
#Override
public void onChildDraw(
final Canvas canvas,
final RecyclerView recyclerView,
final RecyclerView.ViewHolder viewHolder,
final float deltaX,
final float deltaY,
final int actionState,
final boolean isCurrentlyActive) {
This is derived from the platform ItemTouchHelper.SimpleCallback. So...
If you want to implement your solutions, you'll need to implement it there to do things like stop dragging, draw the content (icons), etc.
It's not a trivial solution and getting the whole thing right is difficult; more so if you introduce "some more implementations on RecyclerView".
I hope this answer points you in the right direction, and that your next related question is more about what you've tried (and failed) to do, than a "please do this for me" scenario. (If your intentions weren't those, please apologize, but you spent 5 minutes typing this question and it would take hours/days to implement this, so set your expectations this time).
Finally, when I wrote this, I realized most people wanted iOS swipe behavior; unfortunately, said behavior is not pre-implemented (like on iOS) on Android and you have to deal with it yourself; it's not impossible nor the most difficult task on earth to do on Android, but will give you a few headaches if you're hacking RecyclerView too much.
UPDATE
You've realized now, that ItemTouchHelper has a problem. It draws on the Canvas directly, so it has no knowledge of a Layout, View, Margin, etc. These are all things that live outside the realm of the touch helper. The helper is given a canvas, and drawing capabilities and that's it.
Where does this canvas size come from?
Well, it's calculated during the layout/measure pass(es) for the RecyclerView and its views.
How do I then stop dragging "at exactly the center of the screen" then?
Pass the values to your Helper; remember that the responsibility of calculating where the middle of the screen is, is not in the TouchHelper's contract; but your Activity/Fragment does know this. Provide the information the Touch Helper needs to perform the things you want it to perform.
Revert "back" to the original position, means knowledge of what the initial state was, etc. All this information is known by the RecyclerView and beyond, not the TouchHelper.
So you'd need to measure your layouts, save some values, pass them to the touchhelper so it can operate, etc.
As you can see, the full picture starts to become more and more complicated. My advice to you is:
Try to push this feature OUT as "the platform doesn't do it it will take time, it's not worth" (the worthiness and discoverability of swipe actions is dubious at best, but it's an ubiquitous action nowadays so you may have to do it regardless).
If you really have to do this, abstract things as much as you can, create all the classes/interfaces you think you can need, even if you end up with a "MiddleScreenCalculatorDelegate" kind of thing. It will be easier to fix later, but at least have each component do a very small subset of things.
Lastly, your item touch helper will have to calculate how much offset (delta) has the view been swiped already and stop when it reaches a known threshold.
Good luck :)
p.s.: I suggest you post a different question with specific issue(s) to get better help, this answer is very unhelpful as it is. :)

Android button latency?

I've been working on a drum machine app, and the latency between the time you press the button and the time the sound plays is unbearable. I have seen some people use multitouch and gridviews, and make several buttons able to be pressed at the same time, but I honestly have no knowledge of those. How could I set up multitouch or gridviews to reduce the latency?
I would guess the multitouchable buttons are a very custom implementation. You won't ever be able to touch two ordinary buttons simultaneosly, since they are made for single touch and are based on focus gain etc.
Here's my idea behind a multitouchable implementation:
You create a very custom view which will draw all buttons you need. This view should override onTouchEvent and react on multitouch. I never tried that, but this is the only option I can think of.

How to differentiate between sliding and clicking in android?

In pure Java there is MouseInputListener which I can use to work with those 2 events.
How do I do it with Android?
If I implement both events then only one is fired (onClickListener) and not the other.
Updated:
The question is not about detecting the finger movement.
I have a View (ImageView for example). I need to detect the click on this view which is onClickListener() and finger movement on this view (i.e. press, move then release the finger).
The problem here is that only onClickListener() is called and MotionEvent handler is not caught.
I need to be able to differentiate those 2 events as the main event should be finger movement and onClickListener() should just say "Don't click this view. Spin this view."
Hopefully this is more clear.
OnClickListener and OnTouchListener kinda obstruct each other, since they both consume the MotionEvents that get caught on the View.
Basically you can write a single OnTouchListener that checks for both things. You'll get supplied with the MotionEvent as an argument. Check it's action via MotionEvent.getAction(), e.g. if it equals MotionEvent.ACTION_DOWN (the user put the finger on the display). If the user releases the finger at approx. the same position (ACTION_UP), you may want to interpret that as a click. Otherwise interpret the positions that you get with the ACTION_MOVE event as a gesture.
But the framework already has some classes that do this interpreting work for you, check out the SimpleGestureDetector class and it's SimpleOnGestureListener. That has some callbacks for common events, e.g. onSingleTapConfirmed() and onFling(). All you need to do is supply the MotionEvents from your OnTouchListener to the GestureDetector.

How to implement drag-and-drop between lists in android?

I'm trying to make it so that the user can drag a textview from one listview and then drop it into another, and I'm finding it to be very difficult.
The biggest problem I have found so far is that onTouchEvents seem to only be heard in the view that the ACTION_DOWN event originated in. I'll click in one list and the ACTION_DOWN is heard in there. Then I'll drag outside the list and let go over another list. But the onTouchEvent is only being called for the original list, no matter where I'm going. I thought it was going to be as simple as listening for an ACTION_UP event in the receiving list; but that's called on the original list even though I'm outside it.
What I have so far is a bit messy. When onLongTouchEvent is called, the list tells the main activity to start dragging a draggableTextView (which has a moveTo method). Since the move events continue to be called on the original list, I have it continually setting the position of this draggableTextView. But I can't insert it into another list because I can't figure out what I'm letting go on top of.
Yes, you are right, this is not an easy task. However it is possible: you might check commonsguy's cwac-touchlist example.

Categories