Pull To Refresh Layout by external library in Android


    Pull To Refresh is an exciting topic in Android programming. Pull to refresh is the hottest android UI pattern used by developers in their applications to improve the usability. You can see this pull to refresh feature in many android applications  like Gmail, Facebook, Twitter etc…
    As you can search on Internet, there are many libraries help us to dealing with this problem, I also had a post about using a library written by Chris Banes in Eclipse project format, you can take a glance.
    Moreover, with Material Design technology, Google has released SwipeRefreshLayout, that working well, although it don't have many customs. You can see how to use it at this POST.
    In this  tutorial I’ll show you how to use pull to refresh feature in your android application by using an another powerful external library called Ultra-Pull-To-Refresh, it support all containers and widgets.

Importing library to Android project

    After starting Android Studio project, open your app/build.gradle and add this lib dependency:
dependencies {
compile 'in.srain.cube:ultra-ptr:1.0.11'
}

XML declaring

    Firstly, we must put PtrClassicFrameLayout or PtrClassicFrameLayout to the activity layout file (xml) to use pull to refresh feature. These classes extend ViewGroup, so you can put any view containers (FrameLayout, RelativeLayout,...) or widgets (ListView, GridView,...) inside them.
    Important NOTE: Both of containers can contain only one directly child view.
    For example, creating an activity layout like this:
activity_main.xml
<in.srain.cube.views.ptr.PtrClassicFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cube_ptr="http://schemas.android.com/apk/res-auto"
    android:id="@+id/pt_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    cube_ptr:ptr_duration_to_close="200"
    cube_ptr:ptr_duration_to_close_header="1000"
    cube_ptr:ptr_keep_header_when_refresh="true"
    cube_ptr:ptr_pull_to_fresh="false"
    cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2"
    cube_ptr:ptr_resistance="1.7">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_horizontal_margin"
        android:paddingTop="100dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.35"
                android:text="@string/country"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/city"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.65"
                android:textColor="@android:color/holo_green_light" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.35"
                android:text="@string/location"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/location"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.65"
                android:textColor="@android:color/holo_blue_dark" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.35"
                android:text="@string/temperature"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/temperature"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.65"
                android:textColor="@android:color/holo_orange_dark" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.35"
                android:text="@string/humidity"
                android:textAppearance="?android:attr/textAppearanceSmall"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/humidity"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.65"
                android:textColor="@android:color/holo_purple" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.35"
                android:text="@string/pressure"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/pressure"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.65"
                android:textColor="@android:color/holo_red_dark" />
        </LinearLayout>

    </LinearLayout>

</in.srain.cube.views.ptr.PtrClassicFrameLayout>

Coding project

    In this example, at programmatically code, I use an AsyncTask to get data from Internet after user pull the layout, the view will be refreshed when AsyncTask done:
GetWeatherTask.java
package info.devexchanges.ultrapulltorefresh;

import android.app.ProgressDialog;
import android.os.AsyncTask;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class GetWeatherTask extends AsyncTask<Void, Void, String> {

    private HttpURLConnection urlConnection;
    private final String JSON_URL = "http://api.openweathermap.org/data/2.5/weather?q=hanoi,vn&appid=44db6a862fba0b067b1930da0d769e98";
    private MainActivity activity;
    private ProgressDialog progressDialog;

    public GetWeatherTask(MainActivity activity) {
        this.activity = activity;
        progressDialog = ProgressDialog.show(activity, "Connecting...", "Downloading JSON...", true);
    }

    @Override
    protected String doInBackground(Void... params) {

        StringBuilder result = new StringBuilder();

        try {
            URL url = new URL(JSON_URL);
            urlConnection = (HttpURLConnection) url.openConnection();
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            urlConnection.disconnect();
        }

        return result.toString();
    }

    @Override
    protected void onPostExecute(String result) {
        progressDialog.dismiss(); //dismiss dialog
        activity.parsingJSON(result); //call back data to UI thread
    }
}

    Your Activity must implements PtrHandler interface and there are 2 methods need to override:
  • onRefreshBegin(): when user release pulling action, this will be invoked.
  • checkCanDoRefresh(): set anable/disable for pull to refresh feature.
    Full code of this Activity:
MainActivity.java
package info.devexchanges.ultrapulltorefresh;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import org.json.JSONException;
import org.json.JSONObject;

import java.math.BigDecimal;
import java.math.RoundingMode;

import in.srain.cube.views.ptr.PtrClassicFrameLayout;
import in.srain.cube.views.ptr.PtrFrameLayout;
import in.srain.cube.views.ptr.PtrHandler;

public class MainActivity extends AppCompatActivity implements PtrHandler {

    private PtrClassicFrameLayout pullToRefreshLayout;
    private TextView location;
    private TextView city;
    private TextView pressure;
    private TextView humidity;
    private TextView temperature;

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

        temperature = (TextView) findViewById(R.id.temperature);
        city = (TextView) findViewById(R.id.city);
        location = (TextView) findViewById(R.id.location);
        humidity = (TextView) findViewById(R.id.humidity);
        pressure = (TextView) findViewById(R.id.pressure);
        pullToRefreshLayout = (PtrClassicFrameLayout) findViewById(R.id.pt_frame);

        //set handler for pull to refresh layout
        pullToRefreshLayout.setPtrHandler(this);
        //set last update time in header
        pullToRefreshLayout.setLastUpdateTimeRelateObject(this);
    }

    @SuppressLint("SetTextI18n")
    public void parsingJSON(String data) {
        // parsing json and set the custom dialog components - text, image and button
        try {
            JSONObject jsonObject = new JSONObject(data);

            //get location
            JSONObject locationObject = jsonObject.getJSONObject("coord");
            location.setText("[ " + locationObject.getString("lon") + ", " + locationObject.getString("lat") + " ]");

            //get temperature, humidity and pressure
            JSONObject tempObject = jsonObject.getJSONObject("main");
            temperature.setText(kelvinToCelcius(tempObject.getString("temp")) + " " + (char) 0x00B0 + "C");
            humidity.setText(tempObject.getString("humidity") + "%");
            pressure.setText(hPaToatm(tempObject.getString("pressure")) + " atm");

            //get city name
            city.setText(jsonObject.getString("name"));
        } catch (JSONException e) {
            e.printStackTrace();
        }

        pullToRefreshLayout.refreshComplete(); //stop Refreshing
    }

    @Override
    public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
        return true;
    }

    @Override
    public void onRefreshBegin(PtrFrameLayout frame) {
        new GetWeatherTask(this).execute();
    }

    public double kelvinToCelcius(String kelvin) {
        BigDecimal bd = new BigDecimal(Double.parseDouble(kelvin) - 273);
        bd = bd.setScale(2, RoundingMode.HALF_UP);

        return bd.doubleValue();
    }

    public double hPaToatm(String hPa) {
        BigDecimal bd = new BigDecimal((Double.parseDouble(hPa) / 1013.25));
        bd = bd.setScale(2, RoundingMode.HALF_UP);

        return bd.doubleValue();
    }
}
    Strings resource for this project:
strings.xml
<resources>
    <string name="app_name">"Ultra Pull To Refresh"</string>
    <string name="notice">Pull down to refresh</string>
    <string name="location">Location</string>
    <string name="country">City:</string>
    <string name="temperature">Temperature:</string>
    <string name="humidity">Humidity:</string>
    <string name="pressure">Pressure:</string>
    <string name="weather">Weather</string>
</resources>

    Running application, you'll have this result:

Customizing Pull To Refresh Header

    This library provide a simplest way to custom the pull to refresh layout header text is set new value for it's default strings resource. Rewrite your strings.xml like this:
strings.xml
<resources>
    <string name="app_name">"Ultra Pull To Refresh"</string>
    <string name="notice">Pull down to refresh</string>
    <string name="location">Location</string>
    <string name="country">City:</string>
    <string name="temperature">Temperature:</string>
    <string name="humidity">Humidity:</string>
    <string name="pressure">Pressure:</string>
    <string name="weather">Weather</string>

    <!--Customizing pull to refresh layout header texts -->
    <string name="cube_ptr_pull_down_to_refresh">Kéo xuống để làm mới</string>
    <string name="cube_ptr_release_to_refresh">Thả để làm mới</string>
    <string name="cube_ptr_refreshing">Đang cập nhật...</string>
    <string name="cube_ptr_refresh_complete">Đã cập nhật!</string>
    <string name="cube_ptr_pull_down">Kéo xuống</string>
    <string name="cube_ptr_last_update">Cập nhật lần cuối:&#160;</string>
    <string name="cube_ptr_seconds_ago">&#160;giây trước</string>
    <string name="cube_ptr_minutes_ago">&#160;phút trước</string>
    <string name="cube_ptr_hours_ago">&#160;giờ trước</string>
</resources>

    And this is new output:
    Of course, if you want to make more complicated header, use setHeader(View view) of PtrFrameLayout or PtrClassicFrameLayout with view is inflating from a XML file. For example:
LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View header = inflater.inflate(R.layout.layout_header, null);
pullToRefreshLayout.setHeaderView(header);

Conclusions & References

   Ultra-Pull-To-Refresh may be the best library in making a pull to refresh layout. From here, you can visit it's page on @Github to read it's documents to find out more exciting features of this powerful library. Further, please read my previous post about another lib written by Chris Banes or visit this post to learn about using the official widget provided by Google.


Share


Previous post
« Prev Post
Next post
Next Post »