Smooth Scroll Not Working on Initial Scroll for Android RecyclerView - android

I am working on an Android app that runs on only one devicerunning KitKat.
The smooth scrolling feature for a RecylerView I used that was working on other physical tablets and genymotion has unfortunately stopped working on the one device it needs to work on.
Instead of scrolling to a certain position it passes over the target position and scrolls all the way to the bottom and looks really bad.
I am able to track down the error to the abstract SmoothScroller in the RecyclerView class.
if (getChildPosition(mTargetView) == mTargetPosition) {
onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
mRecyclingAction.runIfNecessary(recyclerView);
stop();
} else {
Log.e(TAG, "Passed over target position while smooth scrolling.");
mTargetView = null;
}
I was using a SnappingLinearLayoutManager that I found online, but swapped it out with the normal LinearLayoutManager from Android, and still am having the same problem.
The list is 7 items long (user can see 4 at a time) and I scroll to the 5th item (position 4) item.
When I scroll to the 3rd I don't receive this error.
Also after I scroll the list up and down once, the error stops happening.
EDIT:
I am able to use layoutManager.scrollToPositionWithOffset(); But I am trying to do this with the smooth scroll animation.
Here is some of my code and details:
private void setupMainRecyclerViewWithAdapter() {
mainLayoutManager = new SnappingLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mainListRecyclerView.setLayoutManager(mainLayoutManager);
settingsMainListAdapter = new SettingsListAdapter(SettingsActivity.this,
settingsPresenter.getSettingsItems(),
settingsPresenter);
mainListRecyclerView.setAdapter(settingsMainListAdapter);
mainListRecyclerView.addItemDecoration(new BottomOffsetDecoration(EXTRA_VERTICAL_SCROLLING_SPACE));
}
#Override
public void scrollMainList(boolean listAtTop) {
if(listAtTop) {
mainListRecyclerView.smoothScrollToPosition(4);
moveMainMoreButtonAboveList();
} else {
mainListRecyclerView.smoothScrollToPosition(0);
moveMainMoreButtonBelowList();
}
}

If you call recyclerView.smoothScrollToPosition(pos) will be called immediately on the UI thread and if recyclerView's Adapter is too much busy to generating view items then the calling of smoothScrollToPosition will be missed then because recyclerView has no data to smooth scroll. So it's better to do that in a background thread by recyclerView.post(). By calling this it goes into the Main thread queue and gets executed after the other pending tasks are finished.
Therefore you should do something like this which worked for my case:
recyclerView.post(new Runnable() {
#Override
public void run() {
recyclerView.smoothScrollToPosition(pos);
}
});

Well, I realize it's too late, however I tried some different solutions and found one...
in custom LinearSmoothScroller I override updateActionForInterimTarget
#Override
protected void updateActionForInterimTarget(Action action) {
action.jumpTo(position);
}
It's appears not very smooth, but not instant in contrast with scrollToPositionWithOffset.

Just add one line for smooth scroll
recyclerView.setNestedScrollingEnabled(false);
it will work fine

Take a look at hasPendingAdapterUpdates(). You can use this along with a delay() for coroutines or Thread.sleep() to enable the backing data to be available before doing the scroll.

Related

How to know a user as moved to a new Card in a Google Glass app?

Is there a way for the CardScrollView or CardScrollAdapter to let me know when the user moves from one card to another?
Initially I was using the GestureDetector to detect swipes to the left or right, but in Glass applications users can use, e.g., two-finger swipes to quickly pan and move to a distant card (or even voice controls).
There must be a better way to know exactly when a new card is displayed than to track all these directional events.
I know CardScrollView.getSelectedItemPosition() gives me the current card in view, but not exactly when a new card is displayed.
I also thought that CardScrollAdapter.getView() would run every time a new card is displayed, but in Glass applications it runs once in the beginning of the activity execution.
Any help would be great.
My Glass is somewhere in my storage so I can't test this now but you can try onDetachedToWindow and onAttachedToWindow. Probably something like this:
private View buildView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
card.setText(R.string.whatever);
View view = card.getView();
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
}
#Override
public void onViewDetachedFromWindow(View v) {
}
});
return view;
}
Also Card lifecycle might give you some idea.
The solution is actually in the documentation as suggested by #EntryLevelDev
CardScrollView notifies you with the following listener interfaces that are inherited from AdapterView:
AdapterView.OnItemSelectedListener - An item is selected after the user finishes scrolling through the list and settles on an item.
from: https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/widget/CardScrollView#onAttachedToWindow()

Recycler View Crash onkeyboard closing

I build a recyclerview like so:
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
currentResults = new ArrayList<Stuff>();
stuffAdapter = new StuffAdapter(currentResults, getActivity());
recyclerView.setAdapter(stuffAdapter);
then when the user searches for things and results are returned, they're added to the adapter in the normal way
currentResults.clear();
currentResults.addAll(searchResponse.results);
restaurantAdapter.notifyDataSetChanged();
this is all very standard. the results are obtained via a searchview widget in an appcompat toolbar and a retrofit callback.
the problem is that there is one particular point where the user can close the keyboard, and the UI adjusts to fit, and then the whole thing crashes with
java.lang.UnsupportedOperationException: RecyclerView does not support scrolling to an absolute position. at
android.support.v7.widget.RecyclerView.scrollTo(RecyclerView.java:941) at
android.view.View.setScrollX(View.java) at
android.animation.PropertyValuesHolder.nCallIntMethod(Native Method) at
android.animation.PropertyValuesHolder.access$200(PropertyValuesHolder.java)
at android.animation.PropertyValuesHolder$IntPropertyValuesHolder.setAnimatedValue(PropertyValuesH
Nobody is calling scrollto. I don't get why it's throwing this. Any insight would be appreciated.
Reading from another SO Question :
java.lang.UnsupportedOperationException: RecyclerView does not support scrolling to an absolute position
The problem is with some specific devices only, because of there Implementation of ICS. As the suggested work around, you can make a CustomRecyclerView that extend RecyclerView and override scrollTo that catch the error and stop the application to crash.
#Override
public void scrollTo(int x, int y) {
Log.e(TAG, "CustomRecyclerView does not support scrolling to an absolute position.");
// Either don't call super here or call just for some phones, or try catch it. From default implementation we have removed the Runtime Exception trown
}

Android SwipeRefreshLayout shows twice in Fragment using support library

It seems like a support library bug, but when i make mSwipeRefreshLayout refreshing in fragment onCreateView using this code
mSwipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
circle progress starts twice. First it appears and immediately hides and then appears again and then works like it supposed to do.
It doesn't happen on Android L devices, only when api < 21 with using support library.
Please help me out.

Android gallery auto scroll with threshold values

I have created a custom gallery in which I have implemented the drag and drop mechanism and rearranged the items.Drag happens during a long click event and fling also works fine.Now my problem is I am trying to auto scroll my gallery during a drag operation when a particular threshold value is reached on the left and side of the gallery,for this i tried using scrollto, setselection both of them seems to be working weird,can somebody help me out with this issue.
Instead of :
myGallery.setSelection(position);
Try :
myGallery.post(new Runnable() {
#Override
public void run() {
myGallery.setSelection(position);
}
});
The same thing with scrollTo should also work.
I hope it will help you.

Stop ListView scroll animation

I have a ListView with about 100 entries. When the user does the "fling" from bottom to top it starts scrolling and keeps on scrolling even when the finger does not touch the display any more.
Is there a way to stop the scrolling animation at this point?
and we lookup the android source code (AbsListView), give it a ACTION_CANCEL touchEvent, can stop the fling. it is easy.
listView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0));
I didn't try the solution of Pompe de velo but since smoothScrollToPosition() is not available for API level less than 8 this didnt work for me.
I agree, changing default behaviour is not a good Idea, but sometimes you need to. So here is my (dirty) solution which uses reflection. This is by far not the recommended way since it's a hack but it works for me. There might be a better solution but I didn't found it.
class StopListFling {
private static Field mFlingEndField = null;
private static Method mFlingEndMethod = null;
static {
try {
mFlingEndField = AbsListView.class.getDeclaredField("mFlingRunnable");
mFlingEndField.setAccessible(true);
mFlingEndMethod = mFlingEndField.getType().getDeclaredMethod("endFling");
mFlingEndMethod.setAccessible(true);
} catch (Exception e) {
mFlingEndMethod = null;
}
}
public static void stop(ListView list) {
if (mFlingEndMethod != null) {
try {
mFlingEndMethod.invoke(mFlingEndField.get(list));
} catch (Exception e) {
}
}
}
}
Well there surely is a way to do it. But the point is more whether or not it is advisable to do it, in my opinion.
The list is a standard Android control that behaves constistently across all applications. So I would be surprised if I found a list that did not behave the same in your application. You can stop the fling by putting your finger back on the screen at any time.
That said, if you want to do extra work, you could subclass the list view and override its on touch method. Best way to know what to do is to get the source code of ListView (ListView in Android 1.6).
You can prevent flinging for ListViews in API 8 by overriding onTouchEvent and calling smoothScrollBy.
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
this.smoothScrollBy(0, 0);
break;
}
return super.onTouchEvent(ev);
}
This takes over from the fling scrolling and scrolls 0px instead.
My opinion is that you shouldn't modify this behaviour, since the fling behaviour is what the user expects.
However, to your question. I haven't tried this but in theory it should work.
Implement an OnScrollListener to your ListView and use the onScrollStateChanged() method to check if the current state is SCROLL_STATE_FLING. After you've determined that the scrolling perfoms by a fling you can get your ListView's first visible position by using the getFirstVisiblePosition() method and from there you can use smoothScrollToPosition() where you put in your getFirstVisiblePosition() value as an argument.
if you what disable default animation from list view just need set id for root (main) layout in xml and call void onClickListener in class for root layout

Categories

Resources