Refreshing the Android application

Jyotishgher Astrology
By -
10 minute read
0

Implementing Pull-to-Refresh in Android

The SwipeRefreshLayout is a versatile Android component designed to enhance the user experience by adding a "pull-to-refresh" gesture to a single child view. This component acts as a listener, notifying its parent (typically an Activity) about refresh events. Consequently, the parent Activity must implement a specific interface to receive these notifications and handle the refresh logic, which usually involves updating the corresponding view. Upon receiving a refresh event, the listener can initiate a "refresh animation" by calling setRefreshing(true) or dismiss it with setRefreshing(false).

Implementing Pull-to-Refresh with SwipeRefreshLayout in Android

How to Use SwipeRefreshLayout

Let's illustrate the usage of SwipeRefreshLayout with a simple example where a vertical swipe gesture triggers the generation of a random number.

Typically, SwipeRefreshLayout serves as the root element in your layout file:

XML
<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/lbl"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Random number:"/>

            <TextView
                android:id="@+id/rndNum"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@id/lbl"/>

            <TextView
                style="@android:style/TextAppearance.Medium"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/lbl"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:text="Swipe to Refresh"/>

        </RelativeLayout>
    </ScrollView>

</android.support.v4.widget.SwipeRefreshLayout>

As evident in the layout XML, SwipeRefreshLayout encloses a single child, in this case, a ScrollView containing other views. Now, let's look at the corresponding Activity code:

Java
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final SwipeRefreshLayout swipeView = findViewById(R.id.swipe);
        final TextView rndNum = findViewById(R.id.rndNum);

        swipeView.setColorSchemeResources(android.R.color.holo_blue_dark,
                android.R.color.holo_blue_light,
                android.R.color.holo_green_light,
                android.R.color.holo_green_light);

        swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                swipeView.setRefreshing(true);
                Log.d("Swipe", "Refreshing Number");
                (new Handler()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        swipeView.setRefreshing(false);
                        double f = Math.random();
                        rndNum.setText(String.valueOf(f));
                    }
                }, 3000);
            }
        });
    }
}

In the onCreate method, we obtain references to the SwipeRefreshLayout and the TextView that will display the random number. We then set the color scheme for the refresh indicator. Crucially, we set an OnRefreshListener on the SwipeRefreshLayout. Within the onRefresh() callback, we first call setRefreshing(true) to show the refresh animation. After a simulated delay of 3 seconds, we generate a random number, update the TextView, and then call setRefreshing(false) to hide the animation.

SwipeRefreshLayout with ListView

A common use case for SwipeRefreshLayout is to enable pull-to-refresh functionality in a ListView. When the ListView is the sole child of the SwipeRefreshLayout, the implementation is straightforward. However, scenarios might involve a more complex UI where the ListView isn't the only element. Consider a layout like this:

XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="Some Header Information"/>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </android.support.v4.widget.SwipeRefreshLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="Some Footer Information"/>

</LinearLayout>

In such cases, a potential issue arises: when scrolling down the ListView, the pull-to-refresh gesture might be inadvertently triggered even if the user intends to scroll the list. To address this, we can employ a technique to temporarily disable the refresh listener and re-enable it only when the ListView is scrolled to the top.

Here's the modified Activity code:

Java
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private SwipeRefreshLayout swipeView;

    private List<String> createItems(int count, int start) {
        List<String> items = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            items.add("Item " + (start + i));
        }
        return items;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        swipeView = findViewById(R.id.swipe);
        ListView lView = findViewById(R.id.list);
        ArrayAdapter<String> adp = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, createItems(40, 0));
        lView.setAdapter(adp);

        swipeView.setEnabled(false); // Initially disable swipe to refresh

        swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                swipeView.setRefreshing(true);
                (new Handler()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        swipeView.setRefreshing(false);
                        // In a real application, you would fetch new data here
                        ArrayAdapter<String> newAdapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, createItems(40, 40));
                        lView.setAdapter(newAdapter);
                    }
                }, 3000);
            }
        });

        lView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // Not needed for this implementation
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                // Enable/disable pull to refresh based on the first visible item
                swipeView.setEnabled(firstVisibleItem == 0);
            }
        });
    }
}

In this code, we initially disable the SwipeRefreshLayout using swipeView.setEnabled(false). Then,

 we set an OnScrollListener on the ListView. In the onScroll() method of the listener, we check if the firstVisibleItem is 0 (meaning the list is scrolled to the top). If it is, we re-enable the SwipeRefreshLayout; otherwise, we disable it. This ensures that the pull-to-refresh gesture is 

only active when the user is at the top of the ListView.

Tags:

Post a Comment

0Comments

Post a Comment (0)