endless recyclerview along with load onscroll from server in recyclerview using cardview to load images and text
Asked Answered
J

2

1

I have implemented recyclerview using cardview to fetch images and text from my server using this tutorial: http://www.simplifiedcoding.net/android-custom-listview-with-images-using-recyclerview-and-volley/.

I have successfully fetched the data and modified the code according to my requirement, now i want to implement swipe to refresh and endless scrolling similar to Instagram application. I have tried a lot of tutorials and SO questions however i am unable to implement any of them.

I am partially successfully implementing swipe-to-refresh but the app exits with an inconsistency error . kindly guide

MainActivity.java

public class MainActivity extends AppCompatActivity  implements SwipeRefreshLayout.OnRefreshListener{

    SwipeRefreshLayout swipeLayout;

    LinearLayoutManager mLayoutManager;

    // initially offset will be 0, later will be updated while parsing the json
    private int offSet = 0;

    private boolean loading = true;
    int pastVisiblesItems, visibleItemCount, totalItemCount;

//Creating a List of superheroes
private List<SuperHeroes> listSuperHeroes;

//Creating Views
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;
public String Img;

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

    //Initializing Views
    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    recyclerView.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);

    //Initializing our superheroes list
    listSuperHeroes = new ArrayList<>();


        mLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(mLayoutManager);

        swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
        swipeLayout.setOnRefreshListener(this);
        swipeLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);

        swipeLayout.post(new Runnable() {
                             @Override
                             public void run() {
                                 swipeLayout.setRefreshing(true);

                                 getData();
                             }
                         }
        );

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                if (dy > 0) //check for scroll down
                {
                    visibleItemCount = mLayoutManager.getChildCount();
                    totalItemCount = mLayoutManager.getItemCount();
                    pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

                    if (loading) {
                        if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
                            loading = false;
                            Log.v("...", "Last Item Wow !");


                            //Do pagination.. i.e. fetch new data


                            loading = true;
                        }
                    }
                }
            }
        });
    }



    //This method will get data from the web api
    private void getData(){
        //Showing a progress dialog
        // final ProgressDialog loading = ProgressDialog.show(this,"Loading Data", "Please wait...",false,false);

// appending offset to url
        String url = Config.DATA_URL;
        String url1 = url + offSet;
        //Creating a json array request
        JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(url1,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        //Dismissing progress dialog
                        // loading.dismiss();

                        //calling method to parse json array
                        parseData(response);
                        adapter.notifyDataSetChanged();
                        // stopping swipe refresh
                        swipeLayout.setRefreshing(false);
                    }

                },

                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });



        //Creating request queue
        RequestQueue requestQueue = Volley.newRequestQueue(this);

        //Adding request to the queue
        requestQueue.add(jsonArrayRequest);


    }

    //This method will parse json data
    private void parseData(JSONArray array){
        for(int i = 0; i<array.length(); i++) {
            SuperHeroes superHero = new SuperHeroes();
            CardAdapter car = new CardAdapter();
            JSONObject json = null;
            try {
                json = array.getJSONObject(i);
                superHero.setImageUrl(json.getString(Config.TAG_IMAGE_URL));
                Img =json.getString(Config.TAG_IMAGE_URL);
                superHero.setName(json.getString(Config.TAG_NAME));
                superHero.setRank(json.getInt(Config.TAG_RANK));
                // superHero.setRealName(json.getString(Config.TAG_REAL_NAME));
                //superHero.setCreatedBy(json.getString(Config.TAG_CREATED_BY));
                //superHero.setFirstAppearance(json.getString(Config.TAG_FIRST_APPEARANCE));
                int rank = json.getInt("pid");

                // updating offset value to highest value
                if (rank >= offSet)
                    offSet = rank;

                //  ArrayList<String> powers = new ArrayList<String>();

                //JSONArray jsonArray = json.getJSONArray(Config.TAG_POWERS);

               /* for(int j = 0; j<jsonArray.length(); j++){
                    powers.add(((String) jsonArray.get(j))+"\n");
                }*/
                //superHero.setPowers(powers);
                Log.d("test",Img);
                car.setImageUrl(Img);


            } catch (JSONException e) {
                e.printStackTrace();
            }
            listSuperHeroes.add(superHero);

        }

        //Finally initializing our adapter
        adapter = new CardAdapter(listSuperHeroes, this);

        //Adding adapter to recyclerview
        recyclerView.setAdapter(adapter);

    }


    @Override
    public void onRefresh() {
        listSuperHeroes.clear();
        getData();
    }

}

CardAdapter.java

/**
 * Created by Belal on 11/9/2015.
 */
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {
    private String imageUrl;
    private ImageLoader imageLoader;
    private Context context;
    String Load;
    static int count = 0;
    public static final String uu = "uu";
    String number;
 String  user1;
    public static final String UserNum = "UserNum";
    SharedPreferences sharedPref;


    // JSON parser class
    JSONParser jsonParser = new JSONParser();

    //testing from a real server:
    private static final String LIKE_URL = "myurl";

    //ids
    private static final String TAG_SUCCESS = "success";
    private static final String TAG_MESSAGE = "message";

    //List of superHeroes
    List<SuperHeroes> superHeroes;



    public CardAdapter() {
    }

    public CardAdapter(List<SuperHeroes> superHeroes, Context context) {
        super();
        //Getting all the superheroes
        this.superHeroes = superHeroes;
        this.context = context;
        sharedPref  =context.getSharedPreferences(UserNum, 0);
        number =  sharedPref.getString(uu, "");


    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

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


    }


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

        SuperHeroes superHero = superHeroes.get(position);
       Log.d("position", String.valueOf(position));
        Log.d("url", superHero.getImageUrl());
        imageLoader = CustomVolleyRequest.getInstance(context).getImageLoader();
        imageLoader.get(superHero.getImageUrl(), ImageLoader.getImageListener(holder.imageView, R.mipmap.ic_launcher, android.R.drawable.ic_dialog_alert));

        holder.imageView.setImageUrl(superHero.getImageUrl(), imageLoader);
        holder.textViewName.setText(superHero.getName());
        holder.textViewRank.setText(String.valueOf(superHero.getRank()));
        // holder.textViewRealName.setText(superHero.getRealName());
        //  holder.textViewCreatedBy.setText(superHero.getCreatedBy());
        // holder.textViewFirstAppearance.setText(superHero.getFirstAppearance());
        // Load =superHero.getImageUrl();
        // String powers = "";

        //  for(int i = 0; i<superHero.getPowers().size(); i++){
        //    powers+= superHero.getPowers().get(i);
        // }


        View.OnClickListener clickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                count++;
                ViewHolder h = (ViewHolder) view.getTag();
                //   int p=h.getAdapterPosition();
                SuperHeroes s = superHeroes.get(position);
                //  Toast.makeText(context,s.getImageUrl(),Toast.LENGTH_LONG).show();
                Load = s.getImageUrl();
                Log.d("test", Load);
                String s1 = new Integer(count).toString();
                Log.d("count", s1);
                new LikeIt().execute();
            }
        };
        // holder.textViewPowers.setText(powers);
        holder.like.setOnClickListener(clickListener);

    }


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



    public class ViewHolder extends RecyclerView.ViewHolder {
        public NetworkImageView imageView;

        public TextView textViewName;
        public TextView textViewRank;
        public TextView textViewRealName;
        public TextView textViewCreatedBy;
        public TextView textViewFirstAppearance;
        public TextView textViewPowers;
        public Button like;

        public ViewHolder(View itemView) {
            super(itemView);
            imageView = (NetworkImageView) itemView.findViewById(R.id.imageViewHero);
            textViewName = (TextView) itemView.findViewById(R.id.textViewName);
            textViewRank = (TextView) itemView.findViewById(R.id.textViewRank);
            // textViewRealName= (TextView) itemView.findViewById(R.id.textViewRealName);
            // textViewCreatedBy= (TextView) itemView.findViewById(R.id.textViewCreatedBy);
            // textViewFirstAppearance= (TextView) itemView.findViewById(R.id.textViewFirstAppearance);
            // textViewPowers= (TextView) itemView.findViewById(R.id.textViewPowers);
            like = (Button) itemView.findViewById(R.id.button_like);

        }


    }

    class LikeIt extends AsyncTask<String, String, String> {

        /**
         * Before starting background thread Show Progress Dialog
         */
        boolean failure = false;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

        }

        @Override
        protected String doInBackground(String... args) {
            // TODO Auto-generated method stub
            // Check for success tag

             int success;

            String Imgurl = Load;
            Log.d("request!", number);

          //  try {
                // Building Parameters
                HashMap<String, String> Params = new HashMap<String, String>();
                Params.put("Imgurl", Imgurl);
           Params.put("user", number);


                Log.d("request!", "starting");

                //Posting user data to script
               /* JSONObject json = jsonParser.performPostCall(LIKE_URL,Params);

                // full json response
                Log.d("Login attempt", json.toString());

                // json success element
                success = json.getInt(TAG_SUCCESS);
                if (success == 1) {
                    Log.d("User Created!", json.toString());
                    //finish();
                    return json.getString(TAG_MESSAGE);
                }else{
                    Log.d("Login Failure!", json.getString(TAG_MESSAGE));
                    return json.getString(TAG_MESSAGE);

                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;*/
                String encodedStr = getEncodedData(Params);

                //Will be used if we want to read some data from server
                BufferedReader reader = null;

                //Connection Handling
                try {
                    //Converting address String to URL
                    URL url = new URL(LIKE_URL);
                    //Opening the connection (Not setting or using CONNECTION_TIMEOUT)
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();

                    //Post Method
                    con.setRequestMethod("POST");
                    //To enable inputting values using POST method
                    //(Basically, after this we can write the dataToSend to the body of POST method)
                    con.setDoOutput(true);
                    OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
                    //Writing dataToSend to outputstreamwriter
                    writer.write(encodedStr);
                    //Sending the data to the server - This much is enough to send data to server
                    //But to read the response of the server, you will have to implement the procedure below
                    writer.flush();

                    //Data Read Procedure - Basically reading the data comming line by line
                    StringBuilder sb = new StringBuilder();
                    reader = new BufferedReader(new InputStreamReader(con.getInputStream()));

                    String line;
                    while((line = reader.readLine()) != null) { //Read till there is something available
                        sb.append(line + "\n");     //Reading and saving line by line - not all at once
                    }
                    line = sb.toString();           //Saving complete data received in string, you can do it differently

                    //Just check to the values received in Logcat
                    Log.i("custom_check","The values :");
                    Log.i("custom_check",line);

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(reader != null) {
                        try {
                            reader.close();     //Closing the
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                //Same return null, but if you want to return the read string (stored in line)
                //then change the parameters of AsyncTask and return that type, by converting
                //the string - to say JSON or user in your case
                return null;
            }

        }
    private String getEncodedData(Map<String,String> data) {
        StringBuilder sb = new StringBuilder();
        for(String key : data.keySet()) {
            String value = null;
            try {
                value = URLEncoder.encode(data.get(key), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            if(sb.length()>0)
                sb.append("&");

            sb.append(key + "=" + value);
        }
        return sb.toString();
    }

        /**
         * After completing background task Dismiss the progress dialog
         **/
        protected void onPostExecute() {
            // dismiss the dialog once product deleted

            }
        }

During swipe on refresh if i scroll down immediately after refresh the app crashes but if i wait till refresh happens i do not get the inconsistency error

Logcat error

11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime: FATAL EXCEPTION: main
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime: Process: com.example.tatson.bila, PID: 14987
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime: java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{278460a2 position=3 id=-1, oldPos=-1, pLpos:-1 no parent}
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4247)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4378)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4359)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1161)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1018)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:3807)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.view.Choreographer.doCallbacks(Choreographer.java:580)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.view.Choreographer.doFrame(Choreographer.java:549)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.os.Handler.handleCallback(Handler.java:739)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:95)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:135)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5253)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:372)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
11-21 16:04:28.209 14987-14987/com.example.tatson.bila E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

also the load on scroll gets activated when i scroll to the end quickly and not when i am on the last item and scroll down

Thank You in advance!!! :)

Jailer answered 21/11, 2015 at 10:8 Comment(3)
checkout this great library : github.com/cymcsg/UltimateRecyclerViewKasten
hi vipul, i have tried using this library but the same issue persists i cannot understand the logic of fetching data from the server side on swiperefresh and on load more. I dont know how the php should look pls helpJailer
am using recyclerview initially calling async task with page 1 with items count 10,set to adapter and then addonscrolllistener how can i call next call with page no 2 and how to add new data to existing adapterdata please help meAqualung
K
5

The concept behind Pagination :

  1. You pass additional variable/value to backend(in your case PHP) let's say "last_seen". For initial fetching of values you will pass the last_seen value as 0, this means php will have a logic of getting values after this value upto some limit(eg. PHP will send the results to android from index 1 to 20).
  2. And as you know your upper limit is 20, so next time at page load that is in RecyclerView.OnScrollListener you will call the PHP again. But this time your last_seen will be 20 and PHP will provide you with results from 21 to 40, and so on.

The concept for Swipe-to-Refresh :

While Implementing Swipe-to-Refresh, it has a method named onRefresh() you will apply the initial PHP call here with last_seen as 0 and it will fetch the initial data from backend.

For Implementation go through following links :

  1. Endless RecyclerView OnScrollListener
  2. http://android-pratap.blogspot.in/2015/01/endless-recyclerview-onscrolllistener.html
  3. Basic Pagination Tutorial (PHP).
Kasten answered 23/11, 2015 at 5:52 Comment(10)
thanks alot for such a clean simple explanation as i mentioned earlier i have understood the concept however when i implement swipe the app crashes with inconsistency error, as shown in logcat and the onload sroll gets invoked when i scroll hard to the bottom not a normal slow scroll, being a novice pls forgive me for asking simple questions but it is imp for my interview project thanxJailer
can you tell me on which line of code, this error is occurring.Kasten
i have no clue and hence my post on SO, i cannot understand why the code is giving error only on fast scrollingJailer
the condition used in onScrolled() method seems wrong to me. I would suggest you to use Endless RecyclerView for EndlessRecyclerOnScrollListener. It is easy implementation of load more.Kasten
public abstract void onLoadMore(int current_page); function in EndlessRecyclerOnScrollListener.java class gives me error where it is defined ?Jailer
have you checked the SampleActivity.java for its proper implementation. And onLoadMore() is an abstract method which need its definition while implementing it in onScrollListener.Kasten
ya i do it now can u help me with Loadmore function. iI mean what to implement over there in my case i want to load more 5 images from sever.Jailer
checkout easy to implement image loading library : Picasso.Kasten
I have noticed that on 2G network the images shuffle while loading in recyclerview before displaying on their correct position while this does not happen on 3G network Why so?Jailer
bro im facing the same problem like you do and im almost solve it but there is some problem. can you share your code? so maybe i can solve my problem thxChinfest
A
2

I also written a new blog post for the requirement you asked here. Check this Android Feed Example.

You have to create a pagination script on your server. I used the following code.

<?php 

//Getting the page number which is to be displayed  
$page = $_GET['page'];  

//Initially we show the data from 1st row that means the 0th row 
$start = 0; 

//Limit is 3 that means we will show 3 items at once
$limit = 3; 

//Importing the database connection 
require_once('dbConnect.php');

//Counting the total item available in the database 
$total = mysqli_num_rows(mysqli_query($con, "SELECT id from feed "));

//We can go atmost to page number total/limit
$page_limit = $total/$limit; 

//If the page number is more than the limit we cannot show anything 
if($page<=$page_limit){

    //Calculating start for every given page number 
    $start = ($page - 1) * $limit; 

    //SQL query to fetch data of a range 
    $sql = "SELECT * from feed limit $start, $limit";

    //Getting result 
    $result = mysqli_query($con,$sql); 

    //Adding results to an array 
    $res = array(); 

    while($row = mysqli_fetch_array($result)){
        array_push($res, array(
            "name"=>$row['name'],
            "publisher"=>$row['publisher'],
            "image"=>$row['image'])
            );
    }
    //Displaying the array in json format 
    echo json_encode($res);
}else{
        echo "over";
}

The rest is same.. additional I added a listener to load more records on the list after reaching the bottom of the list.

Atal answered 15/2, 2016 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.