1

I've been researching this forever and can't find a solution. Everything about my custom list view seems to perform correctly. When I click on the holder.feedUpVoteButton, the text changes +=1 correctly. However, when I scroll down and scroll back up, the text value is reverted to the value it had before it was clicked.

I refuse to use notifyDatasetChanged because I am not adding anything or removing anything from the list.

public class CustomFeedListViewAdapter extends BaseAdapter{

    CustomFeedListViewAdapter customFeedListViewAdapter;
    Date createdAt, currentDate; int num;
    static HashMap<String, String> oneData = new HashMap<String, String>();
    HashMap<String, String> iFeed = new HashMap<>();


    private String likesString;

    String upVoteClicked, downVoteClicked;
    HashMap<String, String> mFeed = new HashMap<>();
    List<ParseObject>  mObjects;

    private ParseObject parseObFeed;

        CustomFeedListViewAdapter(Context context, ArrayList<HashMap<String, String>> data) {

        super();
        this.mContext = context;
        GlobalFeedTab.arrayFeedList = data;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


    @Override
    public int getCount() {
        return GlobalFeedTab.arrayFeedList.size();
    }

    @Override
    public Object getItem(int i) {
        return GlobalFeedTab.arrayFeedList.get(i);
    }

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

         likes = new int[GlobalFeedTab.arrayFeedList.size()];
         countryNames = new int[GlobalFeedTab.arrayFeedList.size()];
        dateNames = new String[GlobalFeedTab.arrayFeedList.size()];

       final ViewHolder holder;

        if (view == null) {

            position = i;

            view = inflater.inflate(R.layout.feed_list_row, viewGroup, false);

            holder = new ViewHolder();


            holder.feedNumOfLikes = (TextView) view.findViewById(R.id.feedNumofLikes);
            holder.feedUpVoteButton = (Button) view.findViewById(R.id.feedUpVoteButton);



        } else {
            position = i;
            holder = (ViewHolder) view.getTag();
        }


        mFeed = GlobalFeedTab.arrayFeedList.get(position);

        holder.feedNumOfLikes.setText(mFeed.get("likes"));

        likesString = mFeed.get("likes");
        likes[position] = Integer.valueOf(likesString);

        holder.feedUpVoteButton.setTag(position);

        ParseQuery<ParseObject> query2 = new ParseQuery<ParseObject>("FeedItem");
        query2.setLimit(250);
        query2.addDescendingOrder("createdAt");

        query2.findInBackground(new FindCallback<ParseObject>() {
            @Override
            public void done(final List<ParseObject> objects, ParseException e) {

                if (e == null) {

                    for ( final ParseObject object : objects) {



                        holder.feedUpVoteButton.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {

                                int pos = (Integer) v.getTag();
                                parseObFeed = objects.get(pos);
                                username = parseObFeed.getString("username");
                                createdAt = parseObFeed.getDate("createdAt");

                                likes[pos] += 1;                           
                                parseObFeed.put("likes", likes[pos]);
                                holder.feedNumOfLikes.setText(String.valueOf(parseObFeed.getInt("likes")));

                                parseObFeed.put(ParseUser.getCurrentUser().getUsername() + "upvoteClicked", true);

                                parseObFeed.saveInBackground();
                            }
                        });                

            }
        });


        return view;
    }


    private class ViewHolder {

        ImageView feedProfilePic;
        TextView feedUsername;
        TextView feedNumOfLikes;
        TextView feedFeedItem;
        TextView feedDate;
        TextView feedNumofReplies;
        Button feedUpVoteButton;
        Button feedDownVoteButton;
        Button feedCommentButton;
        ListView feedListView;

    }

}
K Neeraj Lal
  • 6,768
  • 3
  • 24
  • 33
grantespo
  • 2,233
  • 2
  • 24
  • 62
  • the link gave me guidance, but, I'm still confused about how to implement this with a `BaseAdapter`. can you provide an answer @ArpanSharma – grantespo Nov 28 '16 at 09:33

3 Answers3

2

Actually that is the whole theory.When you increment a value.You have to increment that value in the list from where you are fetching the data.Because when you scroll down,the upper rows will be lost.When you scroll back up,list will be loaded again from the list values.So all you have to do is to increment the value in the list too i.e GlobalFeedTab.arrayFeedList this link is for further detail,for anyone looking for some more detail When you write this line

likes[pos] += 1;

You will also have to increment the value in the arraylist(mFeed) of yours and then write notifyDataSetChanged();

Community
  • 1
  • 1
Arpan Sharma
  • 2,142
  • 11
  • 22
  • Can you provide code in your answer? I'm still confused on how to do this. – grantespo Nov 28 '16 at 19:02
  • How do you increment the value in the array list and where? Can you please give me a complete answer. – grantespo Nov 29 '16 at 05:07
  • Posting code isn't necessary, to fix my issue in the question. I just copy, cut, and pasted the query to be placed in `getView(){ if(e == null){ //here } }` – grantespo Nov 30 '16 at 05:00
1

Every single time getView is called, you re-make

likes = new int[GlobalFeedTab.arrayFeedList.size()];

This is not saved as you scroll and the row you clicked leaves and re-enters the display. The holder could hold this integer value.

private class ViewHolder {
    int likes;
    TextView feedNumOfLikes; 
}

And update it accordingly

holder.feedUpVoteButton
    .setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            holder.feedNumOfLikes.setText(String.valueOf(++holder.likes));

However, you need to also update the parse object.

parseObFeed.put("likes", holder.likes);
parseObFeed.saveInBackground();
// notifyDataSetChanged(); // Up to you. Might not work without

I refuse to use notifyDatasetChanged because I am not adding anything or removing anything from the list.

You are modifying the data, though. Calling it will make your adapter data consistent with what is stored in your Parse Server.


Edit

This gets complicated because you are making each "row" have some "nested" list of objects that are associated with it. I suppose you could store that entire list in the holder.

private class ViewHolder {
    // Java variables to hold onto
    int likes;
    List<ParseObject> feedItems = new ArrayList<ParseObject>();

    // Android views to bind those values to
    TextView feedNumOfLikes; 
}

When inside the adapter, you call Parse to get this secondary list for each item in your adapter, but you only need to store that within the holder. Don't do anything else with it when the query2 returns .

Your real issue here is that is unclear what query2 is even returning. It's some list of data, sure, but are you actually wanting to display a List within a List? Also, each item of the adapter is querying the exact same data from Parse. I think you need a filter... Then, your position variable doesn't correspond between GlobalFeedTab.arrayFeedList and the List<ParseObject>, because those are different lists.

Here's just some comments that I can point out.

    else {
        holder = (ViewHolder) view.getTag();
    }

    // You don't need 'position', it is just the 'i' value...
    mFeed = GlobalFeedTab.arrayFeedList.get(i);

    // This is stored in the Activity, I guess? 
    likesString = mFeed.get("likes");
    holder.feedNumOfLikes.setText(likesString);

    holder.feedUpVoteButton.setTag(i);

    ParseQuery<ParseObject> query2 = new ParseQuery<ParseObject>("FeedItem");

    // This is getting some related "FeedItem" for the current row
    // Just store the info, don't do anything with it yet
    query2.findInBackground(new FindCallback<ParseObject>() {
        @Override
        public void done(final List<ParseObject> objects, ParseException e) {
            if (e == null) {
                holder.feedItems.clear();
                holder.feedItems.addAll(objects);
                notifyDataSetChanged();
            }
        } 
    };

    // Here, you can setup the button for the current row
    holder.feedUpVoteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            int pos = (Integer) v.getTag();

            // This is wrong... 'pos' is the position of the adapter! Not the index of List<ParseObject> results from earlier
            // And this is a very clear ArrayIndexOutOfBounds error...
            // The adapter could be shorter/longer than the holder.feedItems
            parseObFeed = holder.feedItems.get(pos);

            // !! TODO: Figure out which parseObFeed you actually want to use here 

            username = parseObFeed.getString("username");
            createdAt = parseObFeed.getDate("createdAt");

            holder.likes++;
            parseObFeed.put("likes", holder.likes);

            // This is the value that "resets" when you scroll. 
            // parseObFeed has not yet been saved, so when you scroll, the data isn't changed. 
            holder.feedNumOfLikes
                 .setText(String.valueOf(parseObFeed.getInt("likes")));

            // Not really sure what this does - You can store an array of likes rather than one field per username
            parseObFeed.put(ParseUser.getCurrentUser().getUsername() + "upvoteClicked", true);

            parseObFeed.saveInBackground();
            notifyDataSetChanged();
        }
    });    
}

I can't remember if saveInBackground has a callback, but that might be something to look into

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • This looks very detailed. I can't wait to try in a few minutes! @cricket_007 – grantespo Nov 28 '16 at 23:12
  • Warning: It might not work. Just throwing out the first problems I see. – OneCricketeer Nov 28 '16 at 23:13
  • @espa_network Also, I do not think you need the loop over `for ( final ParseObject object : objects)` since you are directly using `objects.get(pos)` within the `onClick`. At no point do you use the one `object` that you are looping on – OneCricketeer Nov 28 '16 at 23:17
  • I tried your suggestion without `notifyDataSetChanged();` and it didn't work. Then I tried it with `notifyDataSetChanged();` and it still didn't work. `notifyDataSetChanged();` caused the button to act as if were disabled. @cricket_007 – grantespo Nov 28 '16 at 23:35
  • I assume I should resort to removing the row, adding a new one, then calling `notifyDataSetChanged();` – grantespo Nov 28 '16 at 23:36
  • Oh, you also wanted to "keep track" of if the button was clicked? That's a separate step. I thought you just cared about the number of likes text – OneCricketeer Nov 28 '16 at 23:36
  • If I refresh the activity, the text updates like its supposed to, It's just when I scroll down and scroll back up, the original value is shown instead. Would "Keeping track of the button being clicked" solve my problem? How would I do that? By using `setTag();` and `getTag();` ? @cricket_007 – grantespo Nov 28 '16 at 23:40
  • Not quite. I'm working on an edit. So, do you want like a like "toggle"? You click to "like", click again to "unlike"? – OneCricketeer Nov 28 '16 at 23:46
  • Yes Exactly, I have 2 Button's, A like button and Unlike Button. The Logic for them works perfectly. It's just when I scroll down then up or up then down, The value before I clicked the button is shown instead. I @cricket_007 – grantespo Nov 28 '16 at 23:48
  • I didn't include all of my code because I didn't want people who weren't familiar with the Parse Sdk to be confused. @cricket_007 – grantespo Nov 28 '16 at 23:51
  • Do you just want the length of `query2` results? Is that a list of users that liked that one particular item? – OneCricketeer Nov 29 '16 at 00:09
  • I tried implementing your code, However, The list view became 100x more slow and the up vote button was acting as if it was disabled. I'm going to figure out a solution myself and I will let you know when I do. Thanks for your time ~e – grantespo Nov 29 '16 at 05:22
  • Yeah, probably. Like I said, each adapter item queries for its own copy of `query2`. That will take a long time by itself. For example, say your limit of 250 items was met, and the adapter only has 4 items, you're requesting 1000 items just right there... – OneCricketeer Nov 29 '16 at 05:44
  • 1
    I fixed it! I was able to 1) get the textView to change it's value after scroll and without using `NotifyDataSetChanged();` and 2) I was able to use a `ParseQuery` which only queries 1 item at a time. Thank You for all of your help. I will post the code tomorrow morning as an answer. @cricket_007 – grantespo Nov 29 '16 at 08:54
  • Posting code isn't necessary, to fix my issue in the question. I just copy, cut, and pasted the query to be placed in `getView(){ if(e == null){ //here } }` – grantespo Nov 30 '16 at 04:59
  • You should answer your own question with the full solution, though. Plus, your code already has `if (e == null) {` – OneCricketeer Nov 30 '16 at 13:53
  • Turns out, I encountered another problem. I'll post when It's perfect with no bugs @cricket_007 – grantespo Nov 30 '16 at 15:27
  • Everything is fixed, check out my answer if your interested! – grantespo Dec 02 '16 at 01:28
1

After continuous trial and error. I finally figured out how to change a textview after it scrolls. My problem was I was getting the wrong ParseObject value. My main activity contains a ParseQuery and I was getting the likesfrom a Hashmap(); However, for some reason I couldn't pass the value of likes directly so I passed the ParseObject itself. Therefore, needing no query in my BaseAdapter Class. Then, I implemented these lines of code in GetView(); to answer my original question:

   holder.upvote[position] =  holder.parseObList[position].getBoolean(ParseUser.getCurrentUser().getUsername() + "upvoteClicked");
    holder.downvote[position] =  holder.parseObList[position].getBoolean(ParseUser.getCurrentUser().getUsername() + "downvoteClicked");

    if(holder.upvote[position]  ){
        holder.feedUpVoteButton.setBackgroundResource(R.drawable.arrowclicked);
        holder.feedNumOfLikes.setText(String.valueOf(holder.likes[position]));

    }
    else if(!holder.upvote[position]){
        holder.feedUpVoteButton.setBackgroundResource(R.drawable.arrowunclicked);
        holder.feedNumOfLikes.setText(String.valueOf(holder.likes[position]));


    }
    if(holder.downvote[position]){
        holder.feedDownVoteButton.setBackgroundResource(R.drawable.arrowclicked);

    }
    else if(!holder.downvote[position]){
        holder.feedDownVoteButton.setBackgroundResource(R.drawable.arrowunclicked);

    }
grantespo
  • 2,233
  • 2
  • 24
  • 62