New Update: Adding Loading More in FooterView
Several weeks ago, I created a IOS tutorial about how to implement pull to refresh in IOS UITableView. In my Rss reader android app, I also want to add pull to refresh feature so that user can easily update the ListView content. I searched online and spend several to integrate one open source project in my Android ListView example. After that, I find it is quite hard to make some change basing on the open source project because of its poor flexibility. Therefore, I decide to create my own drag to refresh ListView (or called pull to refresh ListView).

In my app, I will create a subclass of ListView which is called RefreshableListView. The new ListView class will allow user to drag down and trigger loading event. So my RefreshableListView will implement the interface OnScrollListener and override the interface functions onScroll and onScrollStateChanged; To track user interactions, I will override the function onTouchEvent as well. Once we drag down the ListView, we will see the headerView which gives a tip “Pull Down to Update”. If we release ListView, the headerView will stay on the top and wait for updating. Once the updating processing finishes, the headerView will shrink back and disappear. Now let’s see the example source code step by step.

This is the No.4 Android tutorial about how to create a Rss Reader Android App. Before staring to read this Android tutorial, you may want to read the following android tutorial as well.

First of all, we will initialize the header view in the constructor. In the header view, there is arrow image, a progress bar and two tip text boxes. This is the layout xml of the header view.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:paddingLeft="5dp">

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" >

        <ImageView
            android:id="@+id/head_arrowImageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/ic_arrow_refresh"
            android:contentDescription="@string/head_arrowImage"/>

        <ProgressBar
            android:id="@+id/head_progressBar"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone" />
    </FrameLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:gravity="center_horizontal"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/head_tipsTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:textSize="15sp" />

        <TextView
            android:id="@+id/head_lastUpdatedDateTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:textSize="12sp" />
    </LinearLayout>

</RelativeLayout>

Now, let’s see how I initialize the header view in ListView constructor.

	private void init(Context context) {
		inflater = LayoutInflater.from(context);
		headerRelativeLayout = (RelativeLayout)inflater.inflate(context, R.layout.refresh_header_view, null);
		arrowImage = (ImageView)headerRelativeLayout.findViewById(R.id.head_arrowImageView);
		progressBar = (ProgressBar)headerRelativeLayout.findViewById(R.id.head_progressBar);
		headerTextView = (TextView)headerRelativeLayout.findViewById(R.id.head_tipsTextView);
		lastUpdateDateTextView = (TextView)headerRelativeLayout.findViewById(R.id.head_lastUpdatedDateTextView);
		
		headerRelativeLayout.setPadding(0, -1 * HEADER_HEIGHT, 0, 0);
		this.addHeaderView(headerRelativeLayout, null, false);
	}

The first step is done. We create the header view and add it in the ListView. In our second step, I will override the onTouchEvent function, which will handle the users interaction, such as touch, pull down, and release. I will monitor three MotionEvent:

  • ACTION_DOWN: User is starting to pull the ListView down
  • ACTION_MOVE: User is pulling the ListView
  • ACTION_UP: User released ListView

Once user touches the screen, the ACTION_DOWN event will be triggered. In this state, I will record the start point from which user is starting to pull the ListView.

pull to refresh state ACTION_DOWN
pull to refresh state ACTION_DOWN

When user is pulling the ListView, the ACTION_MOVE event will keep being triggered. In this state, I will change the header view top padding to show the indicator.

pull to refresh state ACTION_MOVE
pull to refresh state ACTION_MOVE

After user released the ListView, the ACTION_UP event is triggered. In this state, I will check the final top padding of header view and decide if triggering the refresh function.

pull to refresh state ACTION_UP
pull to refresh state ACTION_UP

Now, let’s see the android example code.


@Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//isDragging = true;
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (!isLoading) {
deltaY = ev.getY() – startY;

Log.d(“debug”, String.valueOf(deltaY));

headerRelativeLayout.setPadding(
headerRelativeLayout.getPaddingLeft(), -1
* HEADER_HEIGHT + (int) deltaY, 0,
headerRelativeLayout.getPaddingBottom());

if(headerRelativeLayout.getPaddingTop() >= HEADER_HEIGHT && currentState == STATE_PULL_TO_REFRESH) {
//change state
currentState = STATE_RELEASE_TO_UPDATE;
arrowImage.clearAnimation();
arrowImage.startAnimation(rotateAnimation);
headerTextView.setText(R.string.release_to_refresh);
} else if (headerRelativeLayout.getPaddingTop() < HEADER_HEIGHT && currentState == STATE_RELEASE_TO_UPDATE) { currentState = STATE_PULL_TO_REFRESH; arrowImage.clearAnimation(); arrowImage.startAnimation(reverseRotateAnimation); headerTextView.setText(R.string.pull_to_refresh); } } break; case MotionEvent.ACTION_UP: //isDragging = false; if (!isLoading) { if (headerRelativeLayout.getPaddingTop() < HEADER_HEIGHT) { // come back headerRelativeLayout.setPadding( headerRelativeLayout.getPaddingLeft(), -1 * HEADER_HEIGHT, 0, headerRelativeLayout.getPaddingBottom()); } else { // come to HEADER_HEIGHT and start the trigger headerRelativeLayout.setPadding( headerRelativeLayout.getPaddingLeft(), HEADER_TOP, 0, headerRelativeLayout.getPaddingBottom()); headerTextView.setText(R.string.loading); progressBar.setVisibility(View.VISIBLE); arrowImage.clearAnimation(); arrowImage.setVisibility(View.GONE); //START LOADING isLoading = true; if (refreshDelegate != null) { refreshDelegate.startFresh(); } } } break; default: break; } return super.onTouchEvent(ev); } [/java] In this android example, I has shown how to implement the pull to refresh feature by yourself. Actually it is very easy to implement. The whole feature is only two files. The refresh header view layout file and RefreshableListView class java file. Therefore, it will be very easy to use in different project.

New Update: Add Loading More in Footer View

This update includes several improvement. First, I make a change on the pull to refresh logic. So the updating content will appear on the top of the ListView. It makes more sense for pull to update action. Furthermore, I add another feature in the footer view. When users push the list to the bottom, they will see a “Click to load more…”. This new feature will allow user to load old content from the server. Then, all old content title will put in the end of ListView.

click to load more in footerview
click to load more in footerview

How to Monetize Your Android App by ListView

The most simple way to monetize your android applications is add a AdMob Ads Banner. If you are using ListView, it will be very easy for you accomplish that. Especially when you add the AdMob banner between your real content in the ListView, the opportunity for users to click the Ads is increased unbelievable. Here is another example to show you how to add AdMob Banner in the ListView.

Get Pull to Refresh Project Source Code Under $1.99

You can get this confirmed working pull to refresh project source code under $1.99. Once you get the source code, you can just import the project source code in your Eclipse and run it. This source code is very easy to customized. There are two files involved.


Within the project, you will get:

  • MainActivity.java: the example project main activity java file;
  • RefreshableListView.java: the pull to refresh ListView class file;
  • res/layout/main.xml: the example project main layout xml
  • res/layout/refresh_header_view.xml: the pull to refresh header view layout xml
  • res/layout/refresh_footer_view.xml: the pull to refresh footer view layout xml

Download Android Example APK to Try
chart

Next: Show WebSite Content in WebView

In this example, I demonstrate how to drag ListView up and down to refresh the content. In next tutorial, I will show you how to show the web content in a new activity by clicking the item in ListView.
Go to next tutorial: Show WebSite Content in WebView

Previous PostNext Post

8 Comments

  1. I think –
    (RelativeLayout)inflate(context, R.layout.refresh_header_view, null);

    should be-
    (RelativeLayout)inflater.inflate(context, R.layout.refresh_header_view, null);

    Correct?

  2. Could you show the full RefreshableListView class? I can’t see where you override the onScrollStateChanged() method as well as some other methods.
    You’ve also defined a few constants but that hasn’t been posted so I don’t know what values those have.
    I’m getting several errors when trying to implement this. Have you purposefully left out code from the guidelines or am I making some mistakes on my end?

    Any help would be appreciated. Thanks

    1. I only put the core code in the article. For learning purpose, it is enough. If you really cannot get it working, you can get the full source code at a small cost.

Leave a Reply

Your email address will not be published. Required fields are marked *