To get a better understanding of how views work, I'm trying to make a RelativeLayout
that I can drag around and fling. The idea is to capture onTouch
events, set the layout parameters of the RelativeLayout
during ACTION_MOVE
, and then use a Scroller
to "fling" the the view on ACTION_UP
.
To track Velocity, I am using a VelocityTracker
. While the view moves as expected when I drag it around, the VelocityTracker
is giving seemingly random results.
Below is a log snippet of me sliding my finger from left to right across the screen (the two numbers are x and y velocities). As you can see, there are many negative numbers, and the y velocity seems to be larger than it should be.
Does anyone know what I might be doing wrong?
03-30 20:37:29.857: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 23.123592 11.537558
03-30 20:37:29.873: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 356.74066 55.184505
03-30 20:37:29.888: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 343.06155 43.027973
03-30 20:37:29.904: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 248.62907 32.232735
03-30 20:37:29.927: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 182.43666 22.957638
03-30 20:37:29.943: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 157.40408 54.90605
03-30 20:37:29.959: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 59.81672 15.241951
03-30 20:37:29.974: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 45.65707 -8.753063
03-30 20:37:29.990: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 62.7431 -23.311165
03-30 20:37:30.005: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 82.24246 -20.412537
03-30 20:37:30.029: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 101.6548 4.1870637
03-30 20:37:30.045: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 35.63154 -25.088724
03-30 20:37:30.060: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -65.35024 8.635846
03-30 20:37:30.076: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 34.84411 12.235493
03-30 20:37:30.091: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 137.96663 -32.02561
03-30 20:37:30.107: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 104.81523 4.6049824
03-30 20:37:30.130: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -15.846537 -23.924715
03-30 20:37:30.146: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 2.1034415 19.266556
03-30 20:37:30.162: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 20.578733 29.17785
03-30 20:37:30.177: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -28.217247 -42.907413
03-30 20:37:30.193: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -0.87727404 -1.170224
03-30 20:37:30.209: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -24.889711 8.474885
03-30 20:37:30.232: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 153.30855 23.77272
03-30 20:37:30.248: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 44.412945 17.595121
03-30 20:37:30.263: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -39.40518 -28.735428
03-30 20:37:30.279: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -58.433273 -2.295834
03-30 20:37:30.295: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 51.650055 -26.379906
03-30 20:37:30.310: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 152.88931 -20.75504
03-30 20:37:30.334: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 16.233286 -44.017315
03-30 20:37:30.349: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 23.231287 18.601854
03-30 20:37:30.365: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 7.3124657 38.14189
03-30 20:37:30.380: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 84.67032 -22.859661
03-30 20:37:30.396: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 22.753403 -6.019523
03-30 20:37:30.412: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 59.714558 -35.091564
03-30 20:37:30.435: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 27.547312 24.507784
03-30 20:37:30.451: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 114.54237 29.865501
03-30 20:37:30.466: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 70.55507 3.2689145
03-30 20:37:30.482: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -2.4525054 -6.8937516
03-30 20:37:30.498: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 17.924507 -40.815117
03-30 20:37:30.521: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 43.035046 0.5026546
03-30 20:37:30.537: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 95.18336 -11.013772
03-30 20:37:30.552: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -58.86387 10.808097
03-30 20:37:30.568: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -15.751452 12.716822
03-30 20:37:30.584: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 6.3607893 -19.160402
03-30 20:37:30.599: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 93.13071 12.679931
03-30 20:37:30.623: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 71.644485 -24.96885
03-30 20:37:30.638: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -12.482128 18.495268
03-30 20:37:30.654: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -107.08017 23.484608
03-30 20:37:30.670: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -1.009377 -20.781479
03-30 20:37:30.685: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 48.203453 0.5582556
03-30 20:37:30.701: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 12.369134 -34.194973
03-30 20:37:30.724: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 20.611326 14.374227
03-30 20:37:30.740: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 120.71236 56.88748
03-30 20:37:30.755: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 149.84518 14.843528
03-30 20:37:30.771: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -129.18591 -13.255397
03-30 20:37:30.787: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -110.96849 -42.02827
03-30 20:37:30.802: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -77.30668 -12.200225
03-30 20:37:30.826: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 44.793446 16.331116
03-30 20:37:30.841: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 30.79781 -47.53295
03-30 20:37:30.857: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -53.739525 -2.9649315
03-30 20:37:30.873: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -86.65882 -16.804096
03-30 20:37:30.888: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 47.278873 52.180782
03-30 20:37:30.904: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 25.044767 32.227722
03-30 20:37:30.927: DEBUG/SlidingListViewRow(21863): ACTION_MOVE 40.374264 -65.27872
03-30 20:37:30.927: DEBUG/SlidingListViewRow(21863): ACTION_MOVE -12.49039 -48.878017
Code:
package com.example.SlidingListViewRow;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;
public class SlidingListViewRow extends RelativeLayout {
VelocityTracker mVelocityTracker;
Scroller mScroller = new Scroller(getContext());
float mStartX, mStartY;
public SlidingListViewRow(Context context) {
super(context);
}
public SlidingListViewRow(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingListViewRow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
{
mVelocityTracker.clear();
mScroller.abortAnimation();
Log.d("SlidingListViewRow", "ACTION_DOWN: " + " x:" + event.getX() + " y:" + event.getY());
mStartX = event.getX();
mStartY = event.getY();
mVelocityTracker.addMovement(event);
return true;
}
case MotionEvent.ACTION_MOVE:
{
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
float vx = mVelocityTracker.getXVelocity();
float vy = mVelocityTracker.getYVelocity();
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
Log.d("SlidingListViewRow", "ACTION_MOVE\t" + vx + "\t" + vy);
lp.leftMargin = (int)(lp.leftMargin + event.getX() - mStartX);
lp.topMargin = (int)(lp.topMargin + event.getY() - mStartY);
setLayoutParams(lp);
return true;
}
case MotionEvent.ACTION_UP:
{
mVelocityTracker.computeCurrentVelocity(1000);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
Log.d("SlidingListViewRow", "ACTION_UP: " + mVelocityTracker.getXVelocity() + " " + mVelocityTracker.getYVelocity());
mScroller.forceFinished(true);
mScroller.startScroll(lp.leftMargin, lp.topMargin,
(int) mVelocityTracker.getXVelocity(),
(int) mVelocityTracker.getYVelocity(), 1000);
invalidate();
mVelocityTracker.recycle();
mVelocityTracker = null;
return true;
}
case MotionEvent.ACTION_CANCEL:
{
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
Log.d("SlidingListViewRow", "computeScroll " + mScroller.getCurrX() + " " + mScroller.getCurrY());
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
lp.leftMargin = mScroller.getCurrX();
lp.topMargin = mScroller.getCurrY();
setLayoutParams(lp);
invalidate();
}
}
}
EDIT: I think I've come closer. What appears to be happening is that the motion events are relative to the view, and because I'm moving the view, they just jitter around where I touch the view initially. Logging event.getX()
and event.getY()
, I get the following when dragging my finger from top to bottom. Note that x and y are pretty much always the same. So obviously, this approach does not work. Now the question is, how do I get around it?
04-06 10:55:13.460: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:241.85272 vx: 0.008770505 vy:7.7523403
04-06 10:55:13.476: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:244.04398 vx: -0.0014130835 vy:7.125545
04-06 10:55:13.491: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:242.04156 vx: -0.0018627803 vy:-8.498776
04-06 10:55:13.515: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:243.98645 vx: -0.0011581925 vy:-10.778463
04-06 10:55:13.530: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:243.67465 vx: 0.003123357 vy:-0.96067995
04-06 10:55:13.546: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:244.90335 vx: 0.0012589534 vy:20.408503
04-06 10:55:13.562: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:245.38507 vx: 0.0031079662 vy:42.45428
04-06 10:55:13.577: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:246.9231 vx: 0.00393455 vy:20.763577
04-06 10:55:13.616: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:245.3899 vx: 0.0024473427 vy:-15.408336
04-06 10:55:13.632: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:246.62701 vx: 1.6782524E-4 vy:12.024636
04-06 10:55:13.648: DEBUG/SlidingListViewRow(29988): ACTION_MOVE: x:174.0 y:247.16913 vx: 0.0032229617 vy:5.3146386
I'll have to see your movements with your finger to see if the number matches. Some notes: - The results in emulators are completely inaccurate and unexpected in some cases. - Note that you are not tracking pointers. If at some point there is more than one pressure point in the screen, they might affect the result of the calculation of the velocity tracker.
EDIT: You are right. The events are relative to its view in its initial state (unless you change its layoutParams) what I wouldn't recommend. For what you want to do, scroll should work perfectly, which is somehow what you are doing. What is that you want to achieve? We've recently released a library I've been working on in the last months and my first approach was similar to yours. Instead explore this option:
mScroller.startScroll(sx, sy, dx, dy, duration);
Actually the code you have seems to be enough to achieve what you want. You could just avoid changing the layout params of the view which is not the goal there. It'll help to know what is your goal. Check our library out to see if it's somehow closer: https://github.com/6wunderkinder/android-sliding-layer-lib
You can find a demo app in the appstore here: https://play.google.com/store/apps/details?id=com.slidinglayersample
Let's retake it from here.
EDIT 2: Sneaking into the code of AbsListView of Android's source I saw a way that could work for you. Line 3502:
mFlingRunnable.startOverfling(-initialVelocity);
mFlingRunnable is an object of the class FlingRunnable which is defined in the same AbsListView class and implements from Runnable (http://code.metager.de/source/xref/android/1.6/frameworks/base/core/java/android/widget/AbsListView.java - line 2237). It's a huge bunch of code. There's another example of it in the Gallery class (http://code.metager.de/source/xref/android/1.6/frameworks/base/core/java/android/widget/Gallery.java) in line 1287.
The idea is to use a Runnable as a means of something that re-runs on every frame recalculating and drawing the position of the flinged object.
This might go too complex for your goal. Another idea would be to take the velocity from the velocity tracker on MotionEven.UP or CANCEL and create an animation of the position of the object calculating the destination based on the last velocity registered by the VelocityTracked: sending the object further or nearer depending on the velocity at the time the user move her finger up.
I struggled with same problem. You are on right track that translating your view affects velocity tracker computation. To fix the problem please call
MotionEvent.offsetLocation(diffX, diffY)
where diffX, diffY are corresponding to the offset you made on layout params - so diffX in your case would be event.getX() - mStartX. Please also add motion event to tracker as last line of handling ACTION_MOVE, after you applied the offset. So your ACTION_MOVE handling should be:
case MotionEvent.ACTION_MOVE:
{
float vx = mVelocityTracker.getXVelocity();
float vy = mVelocityTracker.getYVelocity();
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
Log.d("SlidingListViewRow", "ACTION_MOVE\t" + vx + "\t" + vy);
lp.leftMargin = (int)(lp.leftMargin + event.getX() - mStartX);
lp.topMargin = (int)(lp.topMargin + event.getY() - mStartY);
setLayoutParams(lp);
event.offsetLocation(event.getX() - mStartX, event.getY() - mStartY);
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With