Usando Web Service JSON con Android (Infinite Scroll, NetworkImage, RecyclerView, CardView...)

Usando Web Service JSON  con Android (Infinite Scroll, NetworkImage, RecyclerView, CardView...)

Un servicio web o web service es un conjunto de datos publicados a travĆ©s de una URL y que pueden ser utilizados por otras aplicaciones, comĆŗnmente el formato de salida es XML o JSON, de esta manera las aplicaciones consumen informaciĆ³n de un servidor.


Este ejemplo en Android utiliza el siguiente web service https://www.itcha.edu.sv/android-json/feed.php?page=1 que genera los datos en formato JSON (JavaScript Object Notation) un formato que empareja los valores en una especie de nodos con un patrĆ³n de clave => valor.

El parĆ”metro page=1 puede incrementarse para mostrar mĆ”s informaciĆ³n, incrementando este valor automĆ”ticamente se logra un efecto de Infinite Scroll cada vez que llega al limite inferior el contador aumenta en uno (page=2...), trayendo nuevos registros en la consulta, el proyecto usa controles como RecyclerView en lugar de ListView, Android Volley y NetworkImage para aumentar la velocidad de descarga de la informaciĆ³n.


AndroidJSONAndroidJSON


MainActivity.java



package com.android_json;

import com.android.volley.Cache;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;

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

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.os.Bundle;

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

public class MainActivity extends AppCompatActivity {

    //Creating a List of notices class
    private List<Notice> listNotices;

    //Creating Views
    private RecyclerView mLista;
    private RecyclerView.Adapter mAdapter;

    StaggeredGridLayoutManager mStaggeredGridLayoutManager;

    int mCount = 0;

    SwipeRefreshLayout mSwipeRefreshLayout;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);

        mLista = (RecyclerView) findViewById(R.id.listaNoticias);

        mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(
                1, //number of grid columns
                GridLayoutManager.VERTICAL);

        //Sets the gap handling strategy for StaggeredGridLayoutManager
        mStaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);

        mLista.setLayoutManager(mStaggeredGridLayoutManager);

        //Initializing our notices list
        listNotices = new ArrayList<>();

        mLista.setOnScrollListener(new EndlessRecyclerOnScrollListener(mStaggeredGridLayoutManager) {
            @Override
            public void onLoadMore(int current_page) {
                // do something...
                getData(current_page);
            }
        });

        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mSwipeRefreshLayout.setRefreshing(true);
                listNotices = new ArrayList<>();
                mCount = 0;
                getData(1);

                mAdapter = new NoticesAdapter(listNotices, getApplicationContext());

                mLista.setAdapter(mAdapter);

                mAdapter.notifyDataSetChanged();

                mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(
                        1, //number of grid columns
                        GridLayoutManager.VERTICAL);

                //Sets the gap handling strategy for StaggeredGridLayoutManager
                mStaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
                mLista.setLayoutManager(mStaggeredGridLayoutManager);

            }
        });


        mSwipeRefreshLayout.post(new Runnable() {
                                     @Override
                                     public void run() {
                                         mSwipeRefreshLayout.setRefreshing(true);
                                         getData(1);
                                         mAdapter = new NoticesAdapter(listNotices, getApplicationContext());

                                         mLista.setAdapter(mAdapter);

                                     }
                                 }

        );



    }
    private JsonArrayRequest getDataFromServer(final int requestCount) {

        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Config.DATA_URL + String.valueOf(requestCount),
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        if (requestCount == mCount + 1) {
                            mCount++;
                            parseData(response);
                        }
                        mSwipeRefreshLayout.setRefreshing(false);


                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        mSwipeRefreshLayout.setRefreshing(false);
                    }
                }) {
            @Override
            protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
                Response<JSONArray> resp = super.parseNetworkResponse(response);
                if (!resp.isSuccess()) {
                    return resp;
                }
                long now = System.currentTimeMillis();
                Cache.Entry entry = resp.cacheEntry;
                if (entry == null) {
                    entry = new Cache.Entry();
                    entry.data = response.data;
                    entry.responseHeaders = response.headers;
                    entry.ttl = now + 24 * 60 * 60 * 1000;  //keeps cache for 1 day
                }
                entry.softTtl = 0; // refresh in 0 minute

                return Response.success(resp.result, entry);
            }
        };
        return jsonArrayRequest;
    }


    //This method will get data from the web api on scroll increment the page in 1 value
    private void getData(int requestCount) {
        if (requestCount == mCount + 1) {
            MySocialMediaSingleton.getInstance(getApplicationContext()).addToRequestQueue(getDataFromServer(requestCount));
        } else {
            MySocialMediaSingleton.getInstance(getApplicationContext()).addToRequestQueue(getDataFromServer(mCount + 1));
        }


    }

    //This method will parse json data
    private void parseData(JSONArray array) {
        for (int i = 0; i < array.length(); i++) {
            //Creating the notice object
            Notice notice = new Notice();
            JSONObject json = null;
            try {
                //Getting json
                json = array.getJSONObject(i);

                //Adding data to the notice object
                notice.setId(json.getInt(Config.TAG_ID));
                notice.setImagen(json.getString(Config.TAG_IMAGE));
                notice.setTitulo(json.getString(Config.TAG_TITULO));
                notice.setDescripcion(json.getString(Config.TAG_DESCRIPCION));
                notice.setFecha(json.getString(Config.TAG_FECHA));
                notice.setNombrecorto(json.getString(Config.TAG_NOMBRECORTO));
                notice.setFechaPub(json.getString(Config.TAG_FECHAPUB));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            //Adding the notice object to the list
            listNotices.add(notice);
        }

        mAdapter.notifyDataSetChanged();
    }
}


Config.java


package com.android_json;
/**
 * Created by Vladimir Salguero on 16/08/2016.
 */
public class Config {
    //Data URL the parameter page is received from GetData method in Main Activity
    public static final String DATA_URL = "https://www.itcha.edu.sv/android-json/feed.php?page=";
    //JSON TAGS
    public static final String TAG_ID = "id";
    public static final String TAG_IMAGE = "IMG";
    public static final String TAG_TITULO = "titulo";
    public static final String TAG_DESCRIPCION = "descripcion";
    public static final String TAG_NOMBRECORTO = "nombreCorto";
    public static final String TAG_FECHA = "fecha";
    public static final String TAG_FECHAPUB = "fechaPub";

}

EndlessRecyclerOnScrollListener.java



package com.android_json;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;


public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
    private int scrolledDistance = 0;
    private boolean controlsVisible = false;


    private int previousTotal = 0; // The total number of items in the dataset after the last load
    private boolean loading = true; // True if we are still waiting for the last set of data to load.
    private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
    //int firstVisibleItem, visibleItemCount, totalItemCount;
    private int pastVisibleItems, visibleItemCount, totalItemCount;


    private int current_page = 1;

    private LinearLayoutManager mLinearLayoutManager;

    private GridLayoutManager mGridLayoutManager;

    private StaggeredGridLayoutManager mStaggeredGridLayoutManager;


    public EndlessRecyclerOnScrollListener(StaggeredGridLayoutManager staggeredGridLayoutManager) {
        this.mStaggeredGridLayoutManager = staggeredGridLayoutManager;

    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = mStaggeredGridLayoutManager.getItemCount();
        //firstVisibleItem = mStaggeredGridLayoutManager.findFirstVisibleItemPosition();
        int[] firstVisibleItems = null;
        firstVisibleItems = mStaggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
        if (firstVisibleItems != null && firstVisibleItems.length > 0) {
            pastVisibleItems = firstVisibleItems[0];
        }


        if (loading) {
            if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount)
                <= (pastVisibleItems + visibleThreshold)) {
            // End has been reached

            // Do something
            current_page++;

            onLoadMore(current_page);

            loading = true;
        }

        if (scrolledDistance > 1 && controlsVisible) {

            controlsVisible = false;
            scrolledDistance = 0;
        } else if (scrolledDistance < -1 && !controlsVisible) {

            controlsVisible = true;
            scrolledDistance = 0;
        }

        if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
            scrolledDistance += dy;
        }
    }

    public abstract void onLoadMore(int current_page);
}



MySocialMediaSingleton.java


package com.android_json;

/**
 * Created by Vladimir Salguero on 11/08/2016.
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.util.LruCache;

import com.android.volley.Cache;
import com.android.volley.Network;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.ImageLoader;


public final class MySocialMediaSingleton {
    // Atributos
    private static MySocialMediaSingleton singleton;
    private RequestQueue requestQueue;
    private static Context context;
    private ImageLoader imageLoader;

    private MySocialMediaSingleton(Context context) {
        MySocialMediaSingleton.context = context;
        requestQueue = getRequestQueue();

        imageLoader = new ImageLoader(requestQueue,
                new ImageLoader.ImageCache() {
                    private final LruCache<String, Bitmap>
                            cache = new LruCache<String, Bitmap>(20);

                    @Override
                    public Bitmap getBitmap(String url) {
                        return cache.get(url);
                    }

                    @Override
                    public void putBitmap(String url, Bitmap bitmap) {
                        cache.put(url, bitmap);
                    }
                });
    }

    public static synchronized MySocialMediaSingleton getInstance(Context context) {
        if (singleton == null) {
            singleton = new MySocialMediaSingleton(context);
        }
        return singleton;
    }

    public ImageLoader getImageLoader() {
        return imageLoader;
    }

    public void addToRequestQueue(Request req) {
        getRequestQueue().add(req);
    }


    public RequestQueue getRequestQueue() {
        if (requestQueue == null) {
            Cache cache = new DiskBasedCache(context.getCacheDir(), 10 * 1024 * 1024);
            Network network = new BasicNetwork(new HurlStack());
            requestQueue = new RequestQueue(cache, network);
            requestQueue.start();

        }
        return requestQueue;
    }


}


Notice.java


package com.android_json;

public class Notice {
    private String titulo;
    private String descripcion;
    private String img;
    private String fecha;
    private String fechaPub;
    private String nombrecorto;
    private int id;


    public Notice() {
    }

    public Notice(String titulo, String descripcion, String img, String fechaPub, String fecha, String nombrecorto, int id) {
        this.titulo = titulo;
        this.descripcion = descripcion;
        this.img = img;
        this.fechaPub = fechaPub;
        this.fecha = fecha;
        this.nombrecorto = nombrecorto;
        this.id = id;
    }

    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    public String getDescripcion() {
        return descripcion;
    }

    public void setDescripcion(String descripcion) {
        this.descripcion = descripcion;
    }

    public String getImagen() {
        return img;
    }

    public void setImagen(String imagen) {
        this.img = imagen;
    }

    public String getFechaPub() {
        return fechaPub;
    }

    public void setFechaPub(String fechaPub) {
        this.fechaPub = fechaPub;
    }

    public String getFecha() {
        return fecha;
    }

    public void setFecha(String fecha) {
        this.fecha = fecha;
    }

    public String getNombrecorto() {
        return nombrecorto;
    }

    public void setNombrecorto(String nombrecorto) {
        this.nombrecorto = nombrecorto;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

}


NoticesAdapter.java


package com.android_json;

import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import androidx.recyclerview.widget.RecyclerView;


/**
 * Creado por Vladimir Salguero.
 */
public class NoticesAdapter extends RecyclerView.Adapter<NoticesAdapter.ViewHolder> {

    private Context context;

    //root url for images
    private static final String URL = "https://www.itcha.edu.sv/publicaciones/";

    //List to store all notices
    List<Notice> notices;

    //Constructor of this class
    public NoticesAdapter(List<Notice> notices, Context context) {
        super();
        //Getting all notices
        this.notices = notices;
        this.context = context;
        notifyDataSetChanged();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_notice, parent, false);
        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        //Getting the particular item from the list
        Notice notice = notices.get(position);

        //Loading image from url

        String url_new = URL + notice.getNombrecorto() + "/" + notice.getId() + "-" + notice.getFecha() + "/thumbnail/" + notice.getImagen();

        // PeticiĆ³n el image loader
        ImageLoader imageLoader = MySocialMediaSingleton.getInstance(context).getImageLoader();
// Request

        //Showing data on the views
        holder.imageView.setImageUrl(url_new, imageLoader);
        holder.titulo.setText(notice.getTitulo());
        holder.descripcion.setText(notice.getDescripcion() + "...");
        holder.fechaPub.setText(notice.getFechaPub());
    }


    @Override
    public int getItemCount() {
        return notices.size();
    }


    class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        //Views
        public NetworkImageView imageView;
        public TextView titulo;
        public TextView descripcion;
        public TextView fechaPub;

        //Initializing Views
        public ViewHolder(final View itemView) {
            super(itemView);
            imageView = (NetworkImageView) itemView.findViewById(R.id.img);
            titulo = (TextView) itemView.findViewById(R.id.titulo);
            descripcion = (TextView) itemView.findViewById(R.id.descripcion);
            fechaPub = (TextView) itemView.findViewById(R.id.fechaPub);

            imageView.setOnClickListener(this);


        }

        @Override
        public void onClick(View view) {

        }
    }
}

Layout


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:app="http://schemas.android.com/apk/res-auto"
             android:layout_width="match_parent"
             android:id="@+id/notices_fragment"
             android:layout_centerHorizontal="true"
             android:paddingBottom="0dp"
             android:layout_height="match_parent"
        >


    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">


        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/listaNoticias"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"/>


    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>






</LinearLayout>



item_notice.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:padding="0dp"
        android:layout_margin="0dp"
        android:layout_height="wrap_content">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:background="#ffffff"
                  android:orientation="vertical"
                  android:padding="0dp"
                  android:layout_margin="0dp"
                  android:layout_height="match_parent"
    >

        <TextView
                android:id="@+id/titulo"
                android:textStyle="bold"
                android:layout_margin="16dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:clickable="true"
                android:focusable="true"
                android:textSize="26sp"/>

        <RelativeLayout android:layout_width="match_parent"
                        android:background="#f2f2f2"
                        android:layout_height="wrap_content">

            <ProgressBar
                    android:id="@+id/loadingImg"
                    style="@android:style/Widget.Holo.ProgressBar"
                    android:layout_width="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_centerVertical="true"
                    android:layout_centerInParent="true"
                    android:layout_height="wrap_content"
                    android:indeterminate="true"/>


            <com.android.volley.toolbox.NetworkImageView
                    android:id="@+id/img"
                    android:layout_width="match_parent"
                    android:layout_height="232dp"
                    android:clickable="true"
                    android:focusable="true"
                    style="?android:borderlessButtonStyle"
                    android:scaleType="centerCrop"/>


        </RelativeLayout>


        <TextView
                android:id="@+id/descripcion"
                android:layout_marginLeft="16dp"
                android:layout_marginRight="16dp"
                android:layout_marginTop="16dp"
                android:layout_marginBottom="0dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/img"
                android:background="?android:selectableItemBackground"
        />

        <LinearLayout android:layout_width="match_parent"
                      android:layout_marginTop="4dp"
                      android:layout_marginLeft="12dp"
                      android:orientation="horizontal"
                      android:layout_height="wrap_content">

            <ImageView android:layout_width="wrap_content"
                       android:layout_gravity="right"
                       android:contentDescription="ic_calendar_clock"
                       android:layout_marginRight="4dp"
                       android:layout_height="wrap_content"
                       android:src="@drawable/ic_calendar_clock"
            />
            <TextView
                    android:id="@+id/fechaPub"
                    android:layout_width="wrap_content"
                    android:textSize="12dp"
                    android:textStyle="italic"
                    android:layout_gravity="right"
                    android:layout_marginRight="12dp"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/descripcion"
                    android:background="?android:selectableItemBackground"
                    android:clickable="true"
                    android:focusable="true"/>

        </LinearLayout>


    </LinearLayout>
</androidx.cardview.widget.CardView>



Github Vladimir Salguero

Visita el Repositorio en GitHub




Support the project YoLeo App

buymeacoffee