How to horizontal listview scrolling control? - android

In my horizontal listview when I swipe one once, the scrolling is working as it should be, but what I need is after a one swipe, next list item to be shown.(just like trimmedia preview in android )

You have to override the fling method of your ListView.
The method "fling" is used to emulate the inertia of the list when you remove your finger from the screen. If the method is empty, this has the effect to scroll only on the next item.
Here an example:
import android.content.Context;
import android.widget.HorizontalScrollView;
public class MyListView extends HorizontalScrollView {
public MyListView(Context context) {
super(context);
}
#Override
public void fling(int velocityX) {
//super.fling(velocityX);
}
}
I didn't test but it's how I did in a little more complex on another project.

Related

Cancel RecyclerView vertical fling/swipe

I am currently detecting when a specific view becomes visible to the user or goes out of sight and stopping the RecyclerView scroll at that view position. So far I have been able to achieve this with the help of the OnScrollListener together with RecyclerView.stopScroll and RecyclerView.scrollTo, but the only issue now is that if the user flings the list it will get past that view position since the fling is still "acting" after the scroll to position has been called, causing the list to overshoot the item position.
How can I end the user fling action programmatically? Something like recyclerView.fling(0) exists?
I ended up using LinearLayoutManager.scrollToPositionWithOffset with the position of the target and 0 as the offset after calling RecyclerView.stopScroll.
So far the fling behavior is no longer causing the list scroll to overshoot the intended position when using these two together.
public class CustomLayoutManager extends LinearLayoutManager {
private boolean isScrollEnabled = true;
public CustomLayoutManager (Context context) {
super(context);
}
public void setScrollEnabled(boolean flag) {
this.isScrollEnabled = flag;
}
#Override
public boolean canScrollVertically() {
//Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
return isScrollEnabled && super.canScrollVertically();
}
}
Set this as your recyclerview layout manager and toggle the scrolling with setScrollEnabled(true/false) with the layoutManager object
Assuming your Recyclerview rv
CustomLayoutManager lm=new CustomLayoutManager(context of your activity);
rv.setLayoutManager(lm);
lm.setScrollEnabled(true/false);

Unwanted bounce effect on RecyclerView with SnapHelper

I am using a RecyclerView with a Horizontal LinearLayoutManager.
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false));
For the adapter items to snap at the center, I've attached a LinearSnapHelper to the recyclerview.
SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(recyclerView);
Now, I have two scenarios where I would want an item to come to center
When my activity launches, it should launch with a specific item snapped to center.
On tapping on an item in the recyclerview, it should get centered. For this, I've overridden the OnClick method in adapter's ViewHolder.
For both scenarios, I'm using
recyclerView.smoothScrollToPosition(position);
and the item gets centered. This however happens with a bouncy animation where a little extra scroll happens first and post that the item bounces back.
How can I disable this bouncy animation to get a smooth scroll?
Things I've tried -
Used below APIs in place of smoothScrollToPosition
LinearLayoutManager.scrollToPosition()
LinearLayoutManager.scrollToPositionWithOffset()
Both the above APIs don't give a smooth scroll, plus the item doesn't get centered properly (since it's difficult to figure out the correct offset value for items that are not yet created/recycled back during the API call)
I couldn't find any method to disable/override animations in RecyclerView's Documentation. Could someone please help..
The solution is to use extended LinearLayoutManager:
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
public class NoBounceLinearLayoutManager extends LinearLayoutManager {
public NoBounceLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
#Override
protected int getHorizontalSnapPreference() {
return position > findFirstVisibleItemPosition() ? SNAP_TO_START : SNAP_TO_END;
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
}

Transition in ListView to replace all elements

I have a ListView that displays a list of states. Whenever the user selects a state, I will load a list of cities that are in this state. For this, I have a manager that, whenever an item is selected, will replace the underlying list that my custom BaseAdapter reads with the item's children.
The problem is, this works fine, but the items are just replaced on the spot, I'd really want a nice transition for this, that is, the list of states moving to the left and being replaced by the list of cities coming from the other edge
Is there a class or method that could help me animate that? The only option I've thought is to keep two different Listview and fade the first one out while the other one fades in. Not the most elegant solution, I was wondering if this could be achieved somehow with only one ListView
I'm not sure how to do this with ListView, but I know you could use a RecyclerView and add animations in the adapter's onBindViewHolder method.
This method is called once for every visible item when you call notifyDataSetChanged().
Edit:
Something like this:
Some Class:
RecyclerView recyclerv;
MyAdapter mAdapter;
LinearLayoutManager mLayoutManager;
List<String> mDataset;
//instantiate recyclerview, adapter, layoutmanager, dataset. populate dataset.
recyclerv.setDataset(mDataset);
MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> dataset;
public MyAdapter(){}
//extend ViewHolder class here
public class ViewHolder extends RecyclerView.Holder { ... }
//implement onCreateViewHolder
/*implement onBindViewHolder.
*animate your view here, but be sure to add an animation count
else the items will keep getting animated everytime a new item appears */
//implement getItemCount
public void setDataset(List<String> dataset) {
this.dataset = dataset;
notifyDataSetChanged(); //this will trigger onBindViewHolder for every visible item
}
}

Android: how can I create a ListView header outside of a list?

This is my situation:
I would like the list to treat the "header" and tabs sections as a list header so that the "header" and tabs do not stay fixed on the screen and they all scroll together with the list.
I can't simply add the header and tabs as a headerView for the list via mListView.addHeaderView() because the tabs will swap out the "List" content area when pressed. The content area for the other tabs will contain other lists, and the header and tabs should scroll with the new list as well.
I'd appreciate any help.
You can override the getViewType method in order to precise how many kind of rows you listview has.
Have a look here.
Think of the entire thing as the ListView. You can call addHeaderView() as many times as you want so you can have two header views ("Header", and "Tab | Tab | Tab").
Re:
I can't simply add the header and tabs as a headerView for the list via mListView.addHeaderView() because the tabs will swap out the "List" content area when pressed.
Yes you can.
Update the reference to your list data and call mAdapter.notifyDataSetChanged().
I had the same need as you, I found this project that implements exactly what you need, plus some more eye-candy tricks.
https://github.com/kmshack/Android-ParallaxHeaderViewPager
The magic is binding the scrolling that happens in the fragment list to the header defined in the activity. There is also a fundamental binding between the selection of a tab and the scroll position in the list, to adjust lists when swiping.
This code is based upon that github repo but is simpler (therefore less robust), read it just to understand what happens, then read the source in the repo.
public class MyFragment extends Fragment implements OnScrollListener {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
((MyParentActivity) getActivity()).onScrollFragment(view, mTabPosition);
}
public void adjustScroll(int scrollHeight) {
if (scrollHeight == 0 && fragmentListView.getFirstVisiblePosition() >= 1) {
return;
}
officesListView.setSelectionFromTop(1, scrollHeight);
}
}
then in the activity you just need these two specific methods
public class MyParentActivity implements ViewPager.OnPageChangeListener{
#Override
public void onPageSelected(int position)
myFragmentPagerAdapter.getFragmentAt(position)
.adjustScroll((int) (mHeader.getHeight() + ViewHelper.getTranslationY(mHeader)));
}
public void onScrollFragment(AbsListView view, int tabPosition) {
if (mViewPager.getCurrentItem() == tabPosition) {
int scrollY = getScrollY(view);
ViewHelper.setTranslationY(mHeader, Math.max(-scrollY, mMinHeaderTranslation));
}
}
}
this code is good up to API 8 thanks to NineOldAndroids' ViewHelper

android fastScroll only covers part of the list

I have a class which implements expandable list activity.
In the XML code (or I can do it in java), I set fastScrollEnabled to true. This does in deed enable fast scroll. BUT fast scroll only works in the top portion of the list. Like I can use the fastscroll thumb bar to scroll the whole list but only works in the top section of the scroll bar. It's not proportionate to the entire list. I can drag the thumb bar to the bottom of the list but it does no scrolling since the listview is already scrolled to the bottom due to the odd behaviour of it only working in the top portion of the list.
Confusing I know, I can try to clarify more if needed....
I do implement a custom BaseExpandableListAdapter.
I've just found a workaround to prevent the system to display this wrong behaviour.
There are two scenarios which use different code for the SectionIndexer to work.
The first scenario is the case that you use the FastScrollbar-Thumb to navigate to the next section. Assuming that the groups are your sections the overriden methods for implementing the SectionIndexer would look like that:
#Override
public int getPositionForSection(int section) {
return section;
}
// Gets called when scrolling the list manually
#Override
public int getSectionForPosition(int position) {
return ExpandableListView.getPackedPositionGroup(
expandableListView
.getExpandableListPosition(position));
}
The second scenario is the case that you scroll the list manually and the fast scrollbars move according to the sections, not to all items. The code therefore looks like that:
#Override
public int getPositionForSection(int section) {
return expandableListView.getFlatListPosition(
ExpandableListView.getPackedPositionForGroup(section));
}
// Gets called when scrolling the list manually
#Override
public int getSectionForPosition(int position) {
return ExpandableListView.getPackedPositionGroup(
expandableListView
.getExpandableListPosition(position));
}
As one can see these two behaviours can not play together without further adoption.
The workaround to make it both work is to catch the case when someone is scrolling per hand (i.e. scrolling via touch). This can be done with implementing the OnScrollListener interface with the adapter class and set it onto the ExpandableListView:
public class MyExpandableListAdapter extends BaseExpandableListAdapter
implements SectionIndexer, AbsListView.OnScrollListener {
// Your fields here
// ...
private final ExpandableListView expandableListView;
private boolean manualScroll;
public MyExpandableListAdapter(ExpandableListView expandableListView
/* Your other arguments */) {
this.expandableListView = expandableListView;
this.expandableListView.setOnScrollListener(this);
// Other initializations
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.manualScroll = scrollState == SCROLL_STATE_TOUCH_SCROLL;
}
#Override
public void onScroll(AbsListView view,
int firstVisibleItem,
int visibleItemCount,
int totalItemCount) {}
#Override
public int getPositionForSection(int section) {
if (manualScroll) {
return section;
} else {
return expandableListView.getFlatListPosition(
ExpandableListView.getPackedPositionForGroup(section));
}
}
// Gets called when scrolling the list manually
#Override
public int getSectionForPosition(int position) {
return ExpandableListView.getPackedPositionGroup(
expandableListView
.getExpandableListPosition(position));
}
// Your other methods
// ...
}
That fixed the bug for me.
This is a bug in the fast scroller. It does not play well with ExpandableListView.
See my code.
(it also includes a work-around for some cases)

Categories

Resources