I've a scrollview in my activity . When I scroll down , it doesn't scroll much and I want to increase the the amount of scroll .
How can I do so?
Flinging is the type of scrolling that occurs when a user drags and lifts her finger quickly.
#Override
public void fling(int velocityY) {
int topVelocityY = (int) ((Math.min(Math.abs(velocityY), MAX_SCROLL_SPEED) ) * Math.signum(velocityY));
super.fling(topVelocityY);
}
I copied it from here.
Related
I had extended recyclerview so i can scale it. It works the way I wanted it but now I wanted to programmatically scroll by x amount of pixels. The user story for this is that, if I tap on the upper half part of the screen, it should scroll by x amount of pixel, same goes to the lower half part of the screen.
Here's what I did:
Activity:
#Override
public boolean dispatchTouchEvent(MotionEvent event)
{
// if(mComicViewerFragment != null)
// mComicViewerFragment.consumeEvent(event);
mScaleDetector.onTouchEvent(event);
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
int y = (int) event.getY();
if(y >= mScreenSize.y / 2)
{
mComicViewerFragment.scrollBy((int)(mScreenSize.y * 0.10f));
Log.i(ComicApplication.TAG, "ComicViewerActivity.onTouchEvent : You are tapping the lower part : " + y);
}
else
{
mComicViewerFragment.scrollBy((int)(-mScreenSize.y * 0.10f));
Log.i(ComicApplication.TAG, "ComicViewerActivity.onTouchEvent : You are tapping the upper part : " + y);
}
}
return super.dispatchTouchEvent(event);
}
Fragment
public void scrollBy(int by)
{
mScalingRecyclerView.smoothScrollBy(0, by);
}
Every time I tap, it doesn't scroll by x amount of pixels. Is this because I created a custom view extending from RecyclerView? Where should I properly call smoothScrollBy? This is suppose to be easy but I am stuck.
As recyclerView.smoothScrollBy(0, pixels); is not working for your custom view you can try an alternative way.
What you can do is scroll by position by doing some Math but it wont be exact to the pixel.
Hypothetically speaking if your Items are of equal height of 100dp you can convert dp to pixels for any screen type by code see here
Lets say 100dp comes to 100px per item and you want to scroll 400px down the Recycler. That's 4 positions (400 / 100).
All you need then is the current in View bottom item position number, add 4 and scroll or smooth scroll by position.
Here is a helper class to show you how to get the Top or Bottom item position in View if you wish to go both up or down the Recycler.
For a complete Solution Check here
I'm automatically scrolling the scrollView to the view's bottom.
scrollView.smoothScrollTo(0, scrollView.getBottom());
If the user touches the layout, I need the scrolling to stop & stay at the current position.
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//// TODO: 01/08/16 STOP SCROLLING
}
return false;
}
});
I've tried to smoothScrollBy(0,0); but it's not working.
Well I solved it by using an ObjectAnimator. It not only elegantly worked as a solution but also gave me control on the scrolling speed.
I replaced
scrollView.smoothScrollTo(0, scrollView.getBottom());
with
objectAnimator = ObjectAnimator
.ofInt(scrollView, "scrollY", scrollView.getBottom())
.setDuration(3000);
objectAnimator.start();
and then
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
objectAnimator.cancel();
}
return false;
}
});
One approach may be to use smoothScrollToPosition, which stops any existing scrolling motion. Note this method requires API level >= 8 (Android 2.2, Froyo).
Note that if the current position is far away from the desired position, then the smooth scrolling will take quite a long time and look a bit jerky (at least in my testing on Android 4.4 KitKat). I also found that a combination of calling setSelection and smoothScrollToPosition could sometimes causes the position to "miss" slightly, this seems to happen only when the current position was very close to the desired position.
In my case, I wanted my list to jump to the top (position=0) when the user pressed a button (this is slightly different from your use case, so you will need to adapt this to your needs).
I used the following method to
private void smartScrollToPosition(ListView listView, int desiredPosition) {
// If we are far away from the desired position, jump closer and then smooth scroll
// Note: we implement this ourselves because smoothScrollToPositionFromTop
// requires API 11, and it is slow and janky if the scroll distance is large,
// and smoothScrollToPosition takes too long if the scroll distance is large.
// Jumping close and scrolling the remaining distance gives a good compromise.
int currentPosition = listView.getFirstVisiblePosition();
int maxScrollDistance = 10;
if (currentPosition - desiredPosition >= maxScrollDistance) {
listView.setSelection(desiredPosition + maxScrollDistance);
} else if (desiredPosition - currentPosition >= maxScrollDistance) {
listView.setSelection(desiredPosition - maxScrollDistance);
}
listView.smoothScrollToPosition(desiredPosition); // requires API 8
}
In my action handler for the button I then called this as follows
case R.id.action_go_to_today:
ListView listView = (ListView) findViewById(R.id.lessonsListView);
smartScrollToPosition(listView, 0); // scroll to top
return true;
The above does not directly answer your question, but if you can detect when the current position is at or near your desired position, then maybe you could use smoothScrollToPosition to stop the scrolling.
I am working on an application which looks somewhat like this:
The RecyclerView uses a custom LayoutManager with a custom implementation of "scrollVerticallyBy"
First of. The recyclerview scroll butter smooth and renders in way less than the required 16 ms. I think I can max i out around 5 ms on a Nexus 5.
However, I also want to be able to scroll the RecyclerView from touching the View above it (it's a RelativeLayout).
Here is the problem:
1) If I just forward the onTouchEvents the scrolling is butter smooth. However, RecyclerView.fling(...) is never invoked and scrolling only works as long as the finger is touching the screen. No flinging which feels very awkward.
#Override
public boolean onTouchEvent(MotionEvent event) {
return mRecyclerView.onTouchEvent(event);
}
2) If I override .fling(...) in the RecyclerView I can get "flinging" to work by using a Scroller. This works, but does cause some strange large lags.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mScroller.computeScrollOffset()) {
Log.d("RecyclerView", "(ondraw) Y: " + mScroller.getCurrY());
scrollBy(0, mScroller.getCurrY()- mLastY);
mLastY = mScroller.getCurrY();
postInvalidateDelayed(16); // 60 Hz
}
}
#Override
public boolean fling(int velocityX, int velocityY) {
mLastY = 0;
mScroller.fling(0, 0, velocityX, velocityY, 0, 0, -10000 ,10000);
return true;
}
I am unsure what the correct way to do this is. I feels like that if I am not overriding .fling() and force my app to repeatedly call scrollBy() by using the Scroller the scrolling is working differently.
I suspect this may have something to do with "startSmoothScroll" and "smoothScrollToPosition" which I have not overwritten. I honestly don't know what smoothscrolling is but it sounds relevant to this problem :)
I have a scrollview with a lot of content. Now when user do a fling or scroll down, I want the scrollview to stop at a particular view location, where I am doing some animation, and then user can again fling or scroll down.
I have tried the disabling of scrollview as mentioned here but It only disables when the scrollview completely stops and cannot stop in the middle of a fling.
Is there any way I can stop a fling of the scrollview when a certain view location or a certain y value is reached?
I had the same problem my solution was.
listView.smoothScrollBy(0,0)
This will stop the scrolling.
To stop a fling at a particular point simply call
fling(0)
If you are only concerned about flings this is the most logical way to do so in my opinion, because velosityYis set to 0 and thereby the fling is stopped immediately.
Here is the javadoc of the fling method:
/**
* Fling the scroll view
*
* #param velocityY The initial velocity in the Y direction. Positive
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
One approach may be to use smoothScrollToPosition, which stops any existing scrolling motion. Note this method requires API level >= 8 (Android 2.2, Froyo).
Note that if the current position is far away from the desired position, then the smooth scrolling will take quite a long time and look a bit jerky (at least in my testing on Android 4.4 KitKat). I also found that a combination of calling setSelection and smoothScrollToPosition could sometimes causes the position to "miss" slightly, this seems to happen only when the current position was very close to the desired position.
In my case, I wanted my list to jump to the top (position=0) when the user pressed a button (this is slightly different from your use case, so you will need to adapt this to your needs).
I used the following method to
private void smartScrollToPosition(ListView listView, int desiredPosition) {
// If we are far away from the desired position, jump closer and then smooth scroll
// Note: we implement this ourselves because smoothScrollToPositionFromTop
// requires API 11, and it is slow and janky if the scroll distance is large,
// and smoothScrollToPosition takes too long if the scroll distance is large.
// Jumping close and scrolling the remaining distance gives a good compromise.
int currentPosition = listView.getFirstVisiblePosition();
int maxScrollDistance = 10;
if (currentPosition - desiredPosition >= maxScrollDistance) {
listView.setSelection(desiredPosition + maxScrollDistance);
} else if (desiredPosition - currentPosition >= maxScrollDistance) {
listView.setSelection(desiredPosition - maxScrollDistance);
}
listView.smoothScrollToPosition(desiredPosition); // requires API 8
}
In my action handler for the button I then called this as follows
case R.id.action_go_to_today:
ListView listView = (ListView) findViewById(R.id.lessonsListView);
smartScrollToPosition(listView, 0); // scroll to top
return true;
The above does not directly answer your question, but if you can detect when the current position is at or near your desired position, then maybe you could use smoothScrollToPosition to stop the scrolling.
You need to disable ScrollView handling of the fling operation. To do this simply override the fling method in ScrollView and comment super.fling(). Let me know if this works !
public class CustomScrollView extends ScrollView {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
return false;
}
#Override
public void fling (int velocityY)
{
/*Scroll view is no longer gonna handle scroll velocity.
* super.fling(velocityY);
*/
}
}
I'm trying to achieve a similar situation. I have a partial solution:
I've managed to stop the ScrollView at a particular y coordinate by overriding onScrollChanged and then calling smoothScrollTo. I allow the user to continue scrolling down past that point by keeping a boolean that indicates if this was the first fling/drag or not. And the same goes for scrolling from bottom to top - I stop the scroll at the same point again. And then allow the user to continue scrolling upward again.
The code looks something like this:
boolean beenThereFromTop = false;
boolean beenThereFromBottom = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView);
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
TextView whereToStop = (TextView) findViewById(R.id.whereToStop);
final int y = whereToStop.getBottom();
int scrollY = scrollView.getScrollY();
// manage scrolling from top to bottom
if (scrollY > y) {
if (!beenThereFromTop) {
beenThereFromTop = true;
scrollView.post(new Runnable() {
#Override
public void run() {
scrollView.smoothScrollTo(0, y);
}
});
}
}
if (scrollY < y && beenThereFromTop) {
beenThereFromTop = false;
}
// manage scrolling from bottom to top
if (scrollY < y) {
if (!beenThereFromBottom) {
beenThereFromBottom = true;
scrollView.post(new Runnable() {
#Override
public void run() {
scrollView.smoothScrollTo(0, y);
}
});
}
}
if (scrollY > y && beenThereFromBottom) {
beenThereFromBottom = false;
}
}
});
}
Well im actualy using this method:
list.post(new Runnable()
{
#Override
public void run()
{
list.smoothScrollToPositionFromTop(arg2, 150);
}
});
Imagine a grid (implemented using a canvas) that is infinite to the right and bottom, but has a bound on left and top. I want to be able to scroll this grid using fling gestures. I implemented this using the GestureDetector and Scroller classes like below. While this is mostly acceptable and looks good, the distance and speed of the scrolling is rather short / slow when doing a strong fling.
This is good when doing a less powerful fling, for example like you would do when you only wanted to scroll your contacts list or browser a little. For a fling that would scroll your contacts list a lot however, this method only scrolls my grid a little more than the previous one does.
I'm not sure what to set the minX/maxX/minY/maxY parameters to in order to achieve those effects, or what else to change. The value I chose so far is a good compromise for my purposes: it never scrolls too much or too fast, but it does scroll too little and too slow for strong flings.
How can I achieve a similar fling behavior to the contacts list (I'm thinking about the ICS / JB one, but I think all of them behave roughly the same in this regard) and browsers in my infinite grid?
Relevant code listing:
private int mFlingPrevX, mFlingPrevY;
private void moveSurface() {
if (mFlinger.computeScrollOffset()) {
mScroll.x = -mFlinger.getCurrX() + mFlingPrevX;
mScroll.y = -mFlinger.getCurrY() + mFlingPrevY;
mFlingPrevX = mFlinger.getCurrX();
mFlingPrevY = mFlinger.getCurrY();
invalidate();
post(new Runnable() {
#Override
public void run() {
moveSurface();
}
});
}
}
#Override
public boolean onFling(final MotionEvent motionEvent1, final MotionEvent motionEvent2, final float velocityX, final float velocityY) {
ViewConfiguration config = ViewConfiguration.get(mContext);
if (Math.abs(velocityX) < config.getScaledMinimumFlingVelocity() || Math.abs(velocityY) < config.getScaledMinimumFlingVelocity()) {
return true;
}
mFlinger = new Scroller(mContext);
mFlingPrevX = getScrollX();
mFlingPrevY = getScrollY();
// 380 is a magic number that works OK, but it doesn't do what I'm asking here
int distance = (int)(GameGlobals.getScreenDensityFactor(mContext) * 380);
mFlinger.fling(mFlingPrevX, mFlingPrevY, -(int)velocityX, -(int)velocityY, -distance, distance, -distance, distance);
post(new Runnable() {
#Override
public void run() {
moveSurface();
}
});
return true;
}
Well, this was easier than expected. The first two parameters actually give information about where the fling started and where it ended, so we can find the distance variable based on that.
float dx = motionEvent1.getRawX() - motionEvent2.getRawX();
float dy = motionEvent1.getRawY() - motionEvent2.getRawY();
int distance = 3 * (int)Math.sqrt(dx * dx + dy * dy);
mFlinger.fling(mFlingPrevX, mFlingPrevY, -(int)velocityX, -(int)velocityY,
-distance, distance, -distance, distance);
I like this one best so far, but obviously it can be changed to anything you like.