Choosing a Pull To Refresh Tool

        Recently I encountered the problem of introducing the Pull To Refresh mechanism for updating lists into the project . Due to the specificity of the available lists (lists of different lengths, from 0 to ~ 100; loading items on demand; the set of lists is located in the self-written component, ala ViewPager ) this really turned out to be problematic. Read about all my research in this direction under the cut.

       Pull To Refresh - a chip, as far as I know, migrated to Android from the iPhone. A convenient way to update the list.
        Consider it as an example of our news application (in fact, it was necessary to implement this feature in it): there is a list of news that is updated through the news server. The update is manual, so the Update button sticks out below, which takes up some space on the screen. And why waste precious screen space on a button that is not so often used if you can use the Pull To Refresh technique: being at the top of the list, drag the list down and then release it to update the list. New news (pun intended) will be uploaded and displayed. It looks like this:

        The idea is quite successful, which is why it is used in many applications, including popular Facebook and Twitter clients. So, we decided to introduce such a feature into our news project.
        But why write from scratch what is already ready-made? A quick Google search, the great and mighty StackOverFlow - and here is the most popular android-pulltorefresh tool from Johan Nielson. I took the latest version from GitHub and got it on my own. There it was! The project seems to have been developing for almost a year, but ... this is what I see in the case of small lists:

        And such a question immediately arises: WTF? The project watch'at 418 people, already 71 people forked it, and here it ’sincorrect behavior. And why? Because this one, “Tap to refresh ...”, is the ListView header. And he hides, in an implementation from Johan, a banal ListView. setSelection (1) . And in the case of short lists, this setSelection (1) politely sends to the feck does not work.
        But then I notice that the project turns out to have two more branches: enhancedpull (which was already adjacent to the main branch) and, ultimately, scrollfix_for_short_list :)
        I pull the latest version of the scrollfix_for_short_list branch, screw it into the project: the short list seems to look fine, but why did it start to slow down the UI? But the thing is this: my list is not simple, but with on-demand loading, i.e. First, the first 10 items are shown, and if you scroll to the end, the next portion is loaded into the list. But what is fix for short lists according to Johan?
        “And let's add to the footer ListView an empty view of exactly such height that setSelection (1)again he was able to normally hide the header, ”Johan said and proceeded to subtract the footer’s height. To calculate its height, you need to know the total height of all elements in the list (except for the header, of course). Then we subtract this height from the height of the ListView and get the height for the footer. And in order to find out the height of each element of the list, from somewhere the “ingenious” decision was made to iterate over all the elements with the adapter (using getView () ) and make measure () each, i.e. essentially draw them (even if they are invisible). As a result, my list thought that everyone was rolling it and rolling it - and it would load and load new portions until the elements were finished. And I usually have elements over 50 in the list, and several lists (scroll through like the recently appeared ViewPager 's). In general, here is an implementation for calculating the total height of list items:
    private int getTotalItemHeight() {
            ListAdapter adapter = getAdapter();
            int listviewElementsheight = 0;
            for(int i = 0; i < adapter.getCount(); i++) {
                View mView  = adapter.getView(i, null, this); // wtf?
                mView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
                                                   MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                listviewElementsheight += mView.getMeasuredHeight();
            }
            return listviewElementsheight;
        }
    

    absolutely not suitable for on-demand lists. Anyway, you should avoid pulling getView () from the adapter manually. Does anyone know what kind of logic is laid there.
        As a result, I went surfing the Internet in search of a more adequate tool. Here is what I found:

         Guillermo, in the best tradition of OOP, implemented Pull To Refresh through the State pattern . The result was 13 classes, and the ability to do not only pull-down-to-refresh, but also pull-up-to-refresh. I don’t know why, but the implementation turned out to be not very fast: well, I didn’t have time to edge the list behind a frisky finger. Yes, and the operation required a rather sharp movement down to cause the appearance of the header. And animation is not observed ... We went further.
        Tim Mahoney did not deceive anyone and immediately wrote in README: "Current Status: Buggy."No, it’s not suitable for a serious project. But I looked at the version graph - some kind Daniel Wang forked the project and fixed bugs. Take, fasten. Ease of use is poor. If in previous implementations, it was enough to update the list upon the onRefresh event and then call onRefreshComplete () :
    ((PullToRefreshListView) getListView()).setOnRefreshListener(new OnRefreshListener() {
        @Override
        public void onRefresh() {
            new GetDataTask().execute();
        }
    });
    
    private class GetDataTask extends AsyncTask {
        ...
        @Override
        protected void onPostExecute(Void result) {
        ...
        ((PullToRefreshListView) getListView()).onRefreshComplete();
        }
    }
    

    then here you need to manually create a header, and upon the arrival of events, change the text in it, well, of course, update the list:
    mRefreshHeader = new TextView(this);
    mRefreshHeader.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    mRefreshHeader.setGravity(Gravity.CENTER);
    mRefreshHeader.setText("Pull to refresh...");
    mContainerView = (PullRefreshContainerView) findViewById(R.id.container);
    mContainerView.setRefreshHeader(mRefreshHeader);
    mContainerView.setOnChangeStateListener(new OnChangeStateListener() {
        @Override
        public void onChangeState(PullRefreshContainerView container, int state) {
            switch(state) {
             case PullRefreshContainerView.STATE_IDLE:
             case PullRefreshContainerView.STATE_PULL:
                    mRefreshHeader.setText("Pull to refresh...");
                 break;
                case PullRefreshContainerView.STATE_RELEASE:
                    mRefreshHeader.setText("Release to refresh...");
                    break;
                case PullRefreshContainerView.STATE_LOADING:
                    mRefreshHeader.setText("Loading...");
                    new GetDataTask().execute(); // в onPostExecute() - mContainerView.completeRefresh();
                    break;
            }
        }
    });
    

        I don’t know, maybe it’s more flexible, but ... inconvenient. In addition, it turned out that this implementation does not work very well in Pager: it has become possible to scroll the list vertically and Pager horizontally at the same time.
        As a result, with tears in my eyes, I began to put on the crutches a version of Johan from the scrollfix_for_short_list   branch so that it would not force the lists to be loaded indefinitely. Somehow it was done, but it worked, to put it mildly, unstable. On the horizon loomed the prospect of writing a component myself, and I decided once again to re-surf the Internet. And, lo and behold, I came across another implementation- from Chris Baines. The project was based on a version of Johan, but has since been significantly improved (as the author writes). The test confirmed: this implementation is really devoid of all the bugs inherent in the version of Johan, and looks more nice (due to additional animation).

    So, why am I so spread here? And then to give out morality :
    If you want to use the Pull To Refresh mechanism in your project , use an implementation from Chris Baines . In my opinion, at the moment this is the highest quality implementation of Pull To Refresh.

    PS A little later I found a similar article on Habré , also criticizing the above projects and promoting its version of the implementation of Pull To Refresh . But ... for some reason, when using this component, I just started to crash the application: some problems with the cursor when trying to load a new portion into the list. So this option is not for me. But the demo looks good: better than Johan.

    Also popular now: