Search code examples
androidlistviewscrollsavestate

ListView item state not getting saved onScroll in Android


I am having ListView and there is a Like button at the bottom of every List item. I am not able to save the state of the button while Scrolling. The state of the button gets reset while i scroll up or down. I think i need to add a pojo class to get and set the state of button But i have no idea how to do it So can anyone help me with the code?

My Adapter class:

public class FeedListAdapter extends BaseAdapter {
    private Activity activity;

    private int lastPosition = -1;
    private DatabaseHandler db;
    int id = 0;
    String email;
    private List<FeedItem> feedItems;
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();

    public FeedListAdapter(Activity activity, List<FeedItem> feedItems) {
        this.activity = activity;
        this.feedItems = feedItems;
    }

    @Override
    public int getViewTypeCount() {

        if (getCount() != 0)
            return getCount();

        return 1;
    }
    @Override
    public int getCount() {
        return feedItems.size();
    }

    @Override
    public Object getItem(int location) {
        return feedItems.get(location);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {

       final ViewHolder holder;
        final FeedItem item = feedItems.get(position);
        if (convertView == null){

            LayoutInflater inflater = (LayoutInflater) activity
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.feed_item, parent,false);
            holder = new ViewHolder();
            //Getting Views from Layout
            holder.likebutton =
                    (LikeButton) convertView.findViewById(R.id.star_button);
            holder.name = (TextView) convertView.findViewById(R.id.name);
            holder.timestamp = (TextView) convertView
                    .findViewById(R.id.timestamp);
            holder.statusMsg = (TextView) convertView
                    .findViewById(R.id.txtStatusMsg);
            holder.url = (TextView) convertView.findViewById(R.id.txtUrl);
            holder.like = (TextView) convertView.findViewById(R.id.like_box_no);
            holder.share = (TextView) convertView.findViewById(R.id.share_no);
            holder.comment = (TextView) convertView.findViewById(R.id.comment_no);
            holder.profilePic = (NetworkImageView) convertView
                    .findViewById(R.id.profilePic);
            holder.feedImageView = (FeedImageView) convertView
                    .findViewById(R.id.feedImage1);
            //End Getting Views from Layout

            convertView.setTag(holder);
        }

        else{

            holder = (ViewHolder)convertView.getTag();
        }

        if (imageLoader == null)
            imageLoader = AppController.getInstance().getImageLoader();

         //get User Email
        db = new DatabaseHandler(activity.getApplication());
        HashMap<String, String> user = db.getUserDetails();
        email = user.get("email").toString();
        // End get User Email ID for sending it to db


        holder.name.setText(item.getName());

        // Converting timestamp into x ago format
        CharSequence timeAgo = DateUtils.getRelativeTimeSpanString(
                Long.parseLong(item.getTimeStamp()),
                System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS);
        holder.timestamp.setText(timeAgo);
        if (item.getFav().equals("1")) {

                holder.likebutton.setLiked(true);


        } else {
            // status is empty, remove from view

            holder.likebutton.setLiked(false);
        }
        // Check for empty status message
        if (!TextUtils.isEmpty(item.getStatus())) {
            holder.statusMsg.setText(item.getStatus());
            holder.statusMsg.setVisibility(View.VISIBLE);
        } else {
            // status is empty, remove from view
            holder.statusMsg.setVisibility(View.GONE);
        }
        // Chcek for empty Like
        if (!TextUtils.isEmpty(item.getLike())) {
            holder.like.setText(item.getLike());
            holder.like.setVisibility(View.VISIBLE);
        } else {
            // status is empty, remove from view
            holder.like.setText("0");
        }
        // Chcek for empty Comment
        if (!TextUtils.isEmpty(item.getComment())) {
            holder.comment.setText(item.getComment());
            holder.comment.setVisibility(View.VISIBLE);
        } else {
            // status is empty, remove from view
            holder.comment.setText("0");
        }
        // Check for empty Share
        if (!TextUtils.isEmpty(item.getShare())) {
            holder.share.setText(item.getShare());
            holder.share.setVisibility(View.VISIBLE);
        } else {
            // status is empty, remove from view
            holder.share.setText("0");
        }


        // Checking for null feed url
        if (item.getUrl() != null) {
            holder.url.setText(Html.fromHtml("<a href=\"" + item.getUrl() + "\">"
                    + item.getUrl() + "</a> "));

            // Making url clickable
            holder.url.setMovementMethod(LinkMovementMethod.getInstance());
            holder.url.setVisibility(View.VISIBLE);
        } else {
            // url is null, remove from the view
            holder.url.setVisibility(View.GONE);
        }

        // user profile pic
        holder.profilePic.setImageUrl(item.getProfilePic(), imageLoader);
        //Setting preloading Image to profile pic
        imageLoader.get(item.getProfilePic(), ImageLoader.getImageListener(holder.profilePic, R.drawable._businessman, R.drawable._businessman));

        // Feed image
        if (item.getImge() != null) {
            holder.feedImageView.setImageUrl(item.getImge(), imageLoader);
            holder.feedImageView.setVisibility(View.VISIBLE);
            holder.feedImageView
                    .setResponseObserver(new FeedImageView.ResponseObserver() {
                        @Override
                        public void onError() {
                        }

                        @Override
                        public void onSuccess() {
                        }
                    });
        } else {
            holder.feedImageView.setVisibility(View.GONE);
        }

        //Animating the List View
        Animation animation = AnimationUtils.loadAnimation(activity.getApplication(), (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
        convertView.startAnimation(animation);
        lastPosition = position;
        //End Animating the List View

         //onClick Like Button



                //Toast.makeText(activity.getApplication(), "Fav Changed : " + item.getId(), Toast.LENGTH_SHORT).show();
                //if Favourite Clicked Do this
                holder.likebutton.setOnLikeListener(new OnLikeListener() {

                    @Override
                    public void liked(LikeButton likeButton) {

                        id = item.getId();
                        Log.d("inFavChngeListner", "Clickd" + item.getId());
                        new send_json().execute();
                        likeButton.setLiked(true);

                    }

                    @Override
                    public void unLiked(LikeButton likeButton) {
                        new send_json_unlike().execute();
                        likeButton.setLiked(false);

                    }
                });


        return convertView;


    }

     public static class ViewHolder {
         public LikeButton likebutton;
         public TextView name;
         public TextView timestamp;
         public TextView statusMsg;
         public TextView like;
         public TextView share;
         public TextView comment;
         public TextView url;
         public NetworkImageView profilePic;
         public FeedImageView feedImageView;

    }

    //Sending Likes with email id and feed id to Remote Mysql Db
    public class send_json extends AsyncTask<String, String, JSONObject> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();

        }

        @Override
        protected JSONObject doInBackground(String... params) {
            UserFunctions userFunction = new UserFunctions();

            JSONObject json = userFunction.like_func(email, String.valueOf(id));
            Log.d("BG Like, Email:" + email + "Id: " + String.valueOf(id), json.toString());

            return json;
        }

        @Override
        protected void onPostExecute(JSONObject jsonObject) {
            super.onPostExecute(jsonObject);

        }
    }
    //Sending UnLike Request with email id and feed id to Remote Mysql Db
    public class send_json_unlike extends AsyncTask<String, String, JSONObject> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();

        }

        @Override
        protected JSONObject doInBackground(String... params) {
            UserFunctions userFunction = new UserFunctions();

            JSONObject json = userFunction.unlike_func(email, String.valueOf(id));
            Log.d("BG UnLike, Email:" + email + "Id: " + String.valueOf(id), json.toString());
            return json;
        }



}
}

My Fragment:

    public class MainFragment extends Fragment implements  SwipeRefreshLayout.OnRefreshListener{

    private static final String TAG = MainFragment.class.getSimpleName();
    private ListView listView;
    private FeedListAdapter listAdapter;
    private List<FeedItem> feedItems;
    View  view;
    private CircleRefreshLayout mRefreshLayout;
    private boolean count=false;
    JSONObject feedObj;
    FeedItem item;

    public MainFragment() {
    }

    public static MainFragment newInstance(String text) {
        MainFragment fragment = new MainFragment();
        Bundle bundle = new Bundle();
        fragment.setArguments(bundle);
        return fragment;
    }


    @Override
    public void registerForContextMenu(View view) {
        super.registerForContextMenu(view);
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.activity_main, container, false);
        mRefreshLayout = (CircleRefreshLayout) view.findViewById(R.id.refresh_layout);
        listView = (ListView) view.findViewById(R.id.list);
        feedItems = new ArrayList<FeedItem>();
        listAdapter = new FeedListAdapter(getActivity(), feedItems);

        view.setFocusableInTouchMode(true);
        view.requestFocus();

//Listeneing to Back Button
        view.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        getActivity().finish();
                        Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show();
                        return true;
                    }
                }
                return false;
            }
        });

        //Starting start Loader Animation Thread and fetching the feed
mRefreshLayout.post(new Runnable() {
    @Override
    public void run() {
        startAnim();
        count=true;
        fetch();
    }
});

        mRefreshLayout.setOnRefreshListener(
                new CircleRefreshLayout.OnCircleRefreshListener() {
                    @Override
                    public void refreshing() {
                        // do something when refresh starts
                        count = true;
                        fetch();


                    }

                    @Override
                    public void completeRefresh() {
                        // do something when refresh complete

                    }
                });
        listView.setAdapter(listAdapter);


        return view;

    }


    private void fetch()
     {

                 // making fresh volley request and getting json
            JsonObjectRequest jsonReq = new JsonObjectRequest(Request.Method.GET,
                    URL_FEED, null, new Response.Listener<JSONObject>() {

                @Override
                public void onResponse(JSONObject response) {
                    VolleyLog.d(TAG, "Response: " + response.toString());
                    if (response != null) {
                        feedItems.clear();
                        parseJsonFeed(response);

                    }
                    if (count){
                        stopAnim();
                        mRefreshLayout.finishRefreshing();
                        count=false;
                    }

                }
            }, new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());


                }
            });

            // Adding request to volley request queue
            AppController.getInstance().addToRequestQueue(jsonReq);
        }


    /**
     * Parsing json reponse and passing the data to feed view list adapter
     * */
    private void parseJsonFeed(JSONObject response) {
        try {

            JSONArray feedArray = response.getJSONArray("feed");
            for (int i = 0; i < feedArray.length(); i++) {

                feedObj = (JSONObject) feedArray.get(i);
                item = new FeedItem();
                item.setId(feedObj.getInt("id"));
                item.setName(feedObj.getString("name"));

                // Image might be null sometimes
                String image = feedObj.isNull("image") ? null : feedObj
                        .getString("image");
                item.setImge(image);
                item.setStatus(feedObj.getString("status"));
                item.setProfilePic(feedObj.getString("profilePic"));
                item.setTimeStamp(feedObj.getString("timeStamp"));
                // url might be null sometimes
                String feedUrl = feedObj.isNull("url") ? null : feedObj
                        .getString("url");
                item.setUrl(feedUrl);
                item.setLike(feedObj.getString("like"));
                item.setComment(feedObj.getString("comment"));
                item.setShare(feedObj.getString("share"));
                item.setFav(feedObj.getString("fav"));
                feedItems.add(item);

            }



            // notify data changes to list adapater
            listAdapter.notifyDataSetChanged();
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }


    @Override
    public void onRefresh() {

    }

    //start Animation on Start
    void startAnim(){

        view.findViewById(R.id.avloadingIndicatorView).setVisibility(View.VISIBLE);
    }

    //stop Animation on start
    void stopAnim(){
        view.findViewById(R.id.avloadingIndicatorView).setVisibility(View.GONE);
    }

}

Solution

  • well you fetch your data once only and add the items in 'feedItems'. then on 'userFunction.like_func' you like or dislike using the 'userFunction.unlike_func' which we do not know what they do but probably they do not update 'feedItems' collection and precisely do not call 'setFav' on the clicked item. This is why the likes are not updated. You can:

    1) update(call setFav) required fields in the async tasks or even better create volley request for those.

    2) in 'holder.likebutton.setOnLikeListener(new OnLikeListener() {'

    add:

    item.setFav("1") or item.setFav("0")

    holder.likebutton.setOnLikeListener(new OnLikeListener() {
    
                        @Override
                        public void liked(LikeButton likeButton) {
    
                            id = item.getId();
                            Log.d("inFavChngeListner", "Clickd" + item.getId());
                            new send_json().execute();
                            likeButton.setLiked(true);
                            item.setFav("1")
    
                        }
    
                        @Override
                        public void unLiked(LikeButton likeButton) {
                            new send_json_unlike().execute();
                            likeButton.setLiked(false);
                            item.setFav("0")
                        }
                    });
    

    this however does not guarantee synchronization with remote data as the request may fail and this is why 1) should be done also