public class SynchronisedScrollView extends ListView {
private ScrollViewListener scrollViewListener = null;
public SynchronisedScrollView(Context context) {
super(context);
}
public SynchronisedScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public SynchronisedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
Log.d("Hello", "I am scrolled");
// super.onScrollChanged(x, y, oldx, oldy);
}
}
I want to catch how much the listView is scrolled, so I extend ListView. I am able to scroll the list. But OnScrollChanged is not called. For ScrollView and HorizontalScrollView OnScrollChanged is called whenever it scrolls. Which function is triggered when we scroll a ListView.
In ListView you must explicitly register an onScrollListener to receive onScroll() events.
I think that the method you are looking for is:
public void offsetChildrenTopAndBottom(int offset) {
// ...
}
from ViewGroup. What you can do is extend ListView to gather the offset given to one of the ListViews and pass it though to the other ListView. Make sure that you call super.offsetChildrenTopAndBottom on the recieven end or you could easily end up with a stackoverflow.
I would create a private method and have the new Observable views.
public class ObservableListView extends ListView {
private ObservableListView peer;
// [Constructors] make sure you override all the super constructors.
// Can have trouble with layouts otherwise
public void setPeer(ObservableListView peer) {
this.peer = peer;
}
#Override
public void offsetTopAndBottom(int offset) {
super.offsetTopAndBottom(offset);
if (peer != null) {
peer.internalVerticalOffset(offset);
}
}
private void internalVerticalOffset(int offset) {
super.offsetTopAndBottom(offset);
}
}
If you feel brave you can also define an xml property to give one of the ListViews id and let that one set up the peer relation. (I can expand if you are interested)
My bad, this is non working code. I'm trying to figure out how to do this with the hidden method. In this code I'm overriding the wrong method. Will update shortly.
Update:
Ok, after some research I think that the best option is to combine the extension of ListView with a TouchDelegate.
Related
I have a ViewPager that utilizes a RecyclerView for each page and shares ViewItem rows across pages. Accordingly I share a single RecyclerViewPool between them. However, the ViewPager loads each RecyclerView whether or not it is the page on screen. Is there a way to indicate to the RecyclerView that all of its items are offscreen and force its views to be returned to the Recycler?
My sense is that subclassing LinearLayoutManager and overriding its onLayoutChildren method is the way to go, but I don't have much experience with LayoutManager and would like some guidance.
So here is a subclass of LinearLayoutManager that operates the way I described:
public class PageVisibleLinearLayoutManager extends LinearLayoutManager {
public PageVisibleLinearLayoutManager(Context context) {
super(context);
}
public PageVisibleLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public PageVisibleLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private boolean pageVisible = true;
void setPageVisible(boolean pageVisible) {
boolean change = (this.pageVisible != pageVisible);
this.pageVisible = pageVisible;
if(change) requestLayout();
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if(pageVisible) {
super.onLayoutChildren(recycler, state);
} else {
removeAndRecycleAllViews(recycler);
}
}
}
It works nicely and gives up its views if requested. As dsh mentioned, it is important to mark adjacent pages as being on screen (and I really don't know why setOffscreenPageLimit doesn't limit the number of pages loaded as expected). My previous solution was to use ViewStub and inflate a page only when it was on screen or adjacent. The layout manager method is slightly faster upon initial turning to an unloaded page, but ViewStub has the advantage of pages staying in memory once loaded (making subsequent scrolling more smooth), so I decided to stick with that.
Thank you all. Next question...
I want to create an Excel type layout where there is infinite scroll vertically. There are fixed number of columns horizontally, but they should be scrollable.
I tried the below code
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<android.support.v7.widget.RecyclerView
android:id="#+id/table_data_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</HorizontalScrollView>
If I used the above code, then I am able to scroll vertically, but the columns are not scrollable.
Even giving fixed height in the xml for HorizontalScrollView and RecyclerView, the columns do not scroll.
Finally, I found an answer here, where it was advised to extend the RecyclerView and calculate the height dynamically.
public class MySmartRecyclerView extends RecyclerView {
public MySmartRecyclerView(Context context) {
super(context);
}
public MySmartRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySmartRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean canScrollHorizontally(int direction) {
return false;
}
#Override
public int getMinimumWidth() {
return computedWidth;
}
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
setMeasuredDimension(computedWidth, getMeasuredHeight());
}
#Override
protected int getSuggestedMinimumWidth() {
return computedWidth;
}
}
So is this the only way, this can be achieved? Or is there any other way to achieve this. For RecyclerView inside ScrollView there is NestedScrollView, but no such thing for HorizontalScrollView.
Any pointers will be appreciated. TIA.
There is one alternate solution. You can use this library.
You want to do Ehhhh? Like the guy said, use library. You need and have normal easy to use solutions.
EDIT:
Okay my bad.
You want to infinite scroll vertically, not excel. I'm back from excel topic:
at onScroll listener, you should incriment endlessly the totalItemCount, you do it with double recurssion. From within the function of listener, you call to another listner whereas you call it by a function in the middle, which also increments that number before calling to the same listener again. You simply edit the view and re-attach the listener.
I am using StickyHeaderListview in my project to display contents and for refreshing the list, I am using SwipeRefreshLayout.
The problem here is, when I try to scroll up the list, it starts refreshing the list and not allowing to view the previous items of list.
I want the behavior should be such as the list get refresh only when I've reached to the first item and I try to scroll up , not everytime when i scroll up the list.
Can anyone help on this?
P.s. For implementing SwipeRefreshLayout, I am refering this example
I faced the same problem when using StickyHeaderListview as a direct child of SwipeRefreshLayout. StickyHeaderListview is in fact a FrameLayout wrapping a ListView inside. As nitesh goel explained, this would lead to problems with canChildScrollUp(). Based on nitesh goel's example, this is a full version of CustomSwipeRefreshLayout that works well for me:
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
/**
* A StickyListHeadersListView whose parent view is this SwipeRefreshLayout
*/
private StickyListHeadersListView mStickyListHeadersListView;
public CustomSwipeRefreshLayout(Context context) {
super(context);
}
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setStickyListHeadersListView(StickyListHeadersListView stickyListHeadersListView) {
mStickyListHeadersListView = stickyListHeadersListView;
}
#Override
public boolean canChildScrollUp() {
if (mStickyListHeadersListView != null) {
// In order to scroll a StickyListHeadersListView up:
// Firstly, the wrapped ListView must have at least one item
return (mStickyListHeadersListView.getListChildCount() > 0) &&
// And then, the first visible item must not be the first item
((mStickyListHeadersListView.getFirstVisiblePosition() > 0) ||
// If the first visible item is the first item,
// (we've reached the first item)
// make sure that its top must not cross over the padding top of the wrapped ListView
(mStickyListHeadersListView.getListChildAt(0).getTop() < 0));
// If the wrapped ListView is empty or,
// the first item is located below the padding top of the wrapped ListView,
// we can allow performing refreshing now
} else {
// Fall back to default implementation
return super.canChildScrollUp();
}
}
}
Ok I have got it working. If the SwipeRefreshLayout is the root of the layout and the ListView resides deep into the hierarchy (I had put the ListView inside a RelativeLayout along with the empty TextView) and not the direct child of the SwipeRefreshLayout, it won’t detect a swipe up on the list view properly.
You should create a custom class that extends SwipeRefreshLayout and override canChildScrollUp() method in SwipRefreshLayout
Here is a example :
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout{
private AbsListView view;
public CustomSwipeRefreshLayout(Context context) {
super(context);
}
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setView(AbsListView view){
this.view=view;
}
#Override
public boolean canChildScrollUp() {
return view.getFirstVisiblePosition()!=0;
}
}
I have had a similar problem, the direct child should be an instance of ScrollView (or ListView). The SwipeRefreshLayout will only take in account the direct child's scroll and not the child's of that direct child. I managed to solve this by using two SwipeRefreshLayouts.
I posted the code on github.
Hi i think i made something for a generally use :
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private View v;
public CustomSwipeRefreshLayout(Context context) {
super(context);
}
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setView(View v) {
this.v = v;
}
#Override
public boolean canChildScrollUp() {
return this.v.canScrollVertically(-1);
}
}
With that solution, only set the view you want to scroll inside the SwipeRefreshLayout, after call canChildScrollUp(). like this :
this.refreshLayout.setView(aView);
this.refreshLayout.canChildScrollUp();
I don't test it a lot, but if i'm right it will work for every view at every place (direct child or not) in the SwipeRefreshLayout.
(for me it was SwipeRefreshLayout => RelativeLayout => SrcollView => linearLayout)
This is very simple solution:
list.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int topRowVerticalPosition = (list == null || list.getChildCount() == 0) ?
0 : list.getChildAt(0).getTop();
swipeRefreshLayout.setEnabled((topRowVerticalPosition >= 0));
}
});
So, if you're on the top of the listview you will be enabled to do refresh.
I building an app where pulltorefresh and stickylistHeaders are both need.i have implemented the pulltorefresh in the app but am not able to make it work with stickyListHeaders.Is it possible to merge the two libraries?
Or is there any alternative?any Ideas?
My implementation was broken after updating both libraries, too. This is my quick fix to make it work again. Any suggestions and improvements are welcome!
Make a new class and extend the SticklistListHeadersListView and implement the ViewDelegate interface from ActionBar-PullToRefresh:
public class PtrStickyListHeadersListView extends StickyListHeadersListView
implements ViewDelegate {
public PtrStickyListHeadersListView(Context context) {
super(context);
}
public PtrStickyListHeadersListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PtrStickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean isReadyForPull(View view, float v, float v2) {
View childView = getWrappedList().getChildAt(0);
int top = (childView == null) ? 0 : childView.getTop();
return top >= 0;
}
}
And in your layout.xml replace
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
...>
with
<com.yourapp.package.foo.PtrStickyListHeadersListView
...>
And at last, add the delegate: (listView is an instance of PtrStickyListHeadersListView)
ActionBarPullToRefresh.from(getActivity())
// We need to insert the PullToRefreshLayout into the Fragment 's ViewGroup
.insertLayoutInto(viewGroup)
// We need to mark the ListView and it 's Empty View as pullable
// This is because they are not dirent children of the ViewGroup
.theseChildrenArePullable(R.id.your_list_id)
// We can now complete the setup as desired
.listener(...)
.useViewDelegate(PtrStickyListHeadersListView.class, listView)
.setup(mPullToRefreshLayout);
Similar to Helden's answer, you can also achieve this using an anonymous inner class without extending StickyListHeadersListView
myList = (StickyListHeadersListView) v.findViewById(R.id.your_list_id);
ActionBarPullToRefresh.from(getActivity())
.allChildrenArePullable()
.listener(this)
.useViewDelegate(StickyListHeadersListView.class, new ViewDelegate() {
#Override
public boolean isReadyForPull(View view, float v, float v2) {
return ... //check if list is scrolled to the top or not
}
})
.setup(mPullToRefreshLayout);
long time reader, first time asker so please be gently ;)
I have an Android code that displays around 10 pictures in second on ImageView.
(calling setImageBitmap on ImageView to set new bitmap)
This causes display lags so I'm trying to speed it as much as I can.
I found that (with traceview) that app spend a lot of time on methods:
ViewRoot.performTraversals
View.measure
FrameLayout.onmeasure
ViewGrou.measureChildWithMargins
I suspecting on ImageView measuring so i have create CustomImageView and overide onMeasure just to call super method and write sysout.
It seems that after every setImageBitmap onmeasure is called 3 times.
Bitmaps are allways of the same size so there is no change. Actually it can be change from time to time when image source is changed but that should be remeasured only then.
What can I change, overide, do to eliminite this measurement impact on application speed?
If you need further information tell me and I will provide it.
Sample code
final Bitmap bitmapScaled = Bitmap.createScaledBitmap(bitmap2, width, height, false);
imageView2.setScaleType(ScaleType.CENTER_INSIDE);
if (setSize) {
setSize(imageView2, width, height);
}
imageView2.setImageBitmap(bitmapScaled);
Method that I change to make it work faster (possibly not safe?!)
boolean layout=true;
int noL=1;
#Override
public void requestLayout() {
if (layout){
super.requestLayout();
if (noL>10){
layout=false;
}else{
noL++;
}
}
}
When I had a similar problem I did the following:
public class ImageViewEx extends ImageView{
private boolean makeRequest;
public ImageViewEx(Context context){
super(context);makeRequest=true;
}
public ImageViewEx(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ImageViewEx(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
makeRequest=true;
}
public void setImageBitmapNoRequestLayout(Bitmap bitmap) {
makeRequest = false;
setImageBitmap(bitmap);
makeRequest = true;
}
#Override public void requestLayout(){
if(makeRequest)super.requestLayout();
}
}
Now changing the bitmap with setImageBitmapNoRequestLayout(...) does not call requestLayout().