is it possible to merge stickylistviewheader with crisbanes pulltorefresh? - android

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);

Related

Android TabHost Auto Scroll vertically in side ScrollView on TabChange?

I am using TabHost inside ScrollView in my Activity but when ever I select tab it automatically scrolls my view vertically to end.
In this case child view getting focus due to that it get scrolled upward.
for resolve this you need to create custom ScrollView that extend ScrollView.
code snipt will look like this.
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void requestChildFocus(View child, View focused) {
// if (focused instanceof TabHost) // here
return;
//super.requestChildFocus(child, focused);
// here you need to return instead of **super.requestChildFocus(child, focused);**
}
and xml looks like this
<com.views.widget.MyScrollView
android:focusable="false"
android:focusableInTouchMode="false"
android:id="#+id/root_scroll_view"
android:layout_width="match_parent"
android:fillViewport="true"
android:layout_height="wrap_content">
</com.views.widget.MyScrollView >
Based on Er Pragati Singh's answer I did not override requestChildFocus(View child, View focused) but computeScrollDeltaToGetChildRectOnScreen(Rect rect).
Overriding requestChildFocus will also prevent activating the on screen keyboard when touching an EditText which already has focus, while computeScrollDeltaToGetChildRectOnScreen is only used to calculate the delta scroll inside requestChildFocus to bring the View in sight. So overriding this function keeps all other routines intact.
Java:
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
// This function calculates the scroll delta to bring the focused view on screen.
// -> To prevent unsolicited scrolling to the focued view we'll just return 0 here.
//
return 0;
}
}
XML:
<YOUR.PAKAGE.NAME.MyScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
</YOUR.PAKAGE.NAME.MyScrollView>

Scrollup not working in StickyListviewHeader with SwipeRefreshLayout

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.

Combining PullToRefreshListView and StickyHeaderListView, list view doesn't remember scroll position on fragment change

I need a ListView that combines the functionality of a PullToRefreshListView and a StickyHeaderListView by creating a class called PullToRefreshStickyListView which is an extension of PullToRefreshBase the class is here:
public class PullToRefreshStickyListView extends PullToRefreshBase<StickyListHeadersListView>{
public PullToRefreshStickyListView(Context context) {
super(context);
}
public PullToRefreshStickyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
#Override
protected StickyListHeadersListView createRefreshableView(Context context, AttributeSet attrs) {
StickyListHeadersListView view = new StickyListHeadersListView(context, attrs);
return view;
}
#Override
protected boolean isReadyForPullEnd() {
return false;
}
#Override
protected boolean isReadyForPullStart() {
StickyListHeadersListView view = getRefreshableView();
if (view.getWrappedList().getChildCount() == 0)
return true;
return view.getWrappedList().getChildAt(0).getTop() == 0;
}
}
All is fine, but my problem is, when the fragment that contains this class is shown for the first time, hidden, and shown again for a second time, the list view doesn't seem to remember its last vertical scroll position. Am I missing something? I'm thinking I should be doing something with onSaveInstance, but I am not overly familiar with this so I don't really know why. Maybe someone can give me pointers? Thanks.
Nevermind, I've discovered how to fix this. Turns out that a list view needs to have an ID in order for its scroll state to be remembered. I've just added this line in this method:
#Override
protected StickyListHeadersListView createRefreshableView(Context context, AttributeSet attrs) {
StickyListHeadersListView view = new StickyListHeadersListView(context, attrs);
view.setId(R.id.sticky_header);
return view;
}
created the file ids.xml in res, and added an id for the sticky header view like so:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="sticky_header"></item>
</resources>
and that certainly did the trick.
Remember kids, give your ListViews their own id's.

How to treate some xml structure as a View?

I have a LinearLayout with some Views in it. Then I want to treat this element as a View, so I created a new class extending from LinearLayout. Now when I dynamically add a new instance of this class into the layout I see nothing. I believe I have to get the View somehow from my class, but don't know how. Is it possible to somehow assocciate this new class with an xml?
Update:
public class Task extends LinearLayout {
public Task(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
public Task(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
public Task(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
}
Then:
Task newTask = new Task(getActivity());
someLinearLayout.addView((View) newTask); // happens nothing
You can use different approaches like:
Inflating it into a View
Using <include> tag
Inflating:
public class InflatedView extends LinearLayout
{
public InflatedView(Context c)
{
super(c);
LayoutInflater.from(this).inflate(R.layout.your_other_layout, this);
}
//override other constructors too.
}
Now you can use this in your xmls like this:
<com.your.package.InflatedView android:layout_height="etc" other:attribute="here" />
Include:
Very simple, use include tag:
<include layout="#layout/your_other_layout"/>
Here are RomainGuy's layout tricks.

In ListView Which function is called while scrolling

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.

Categories

Resources