'Android listview content won't refresh upon call back

I got some problem when trying to refresh the list view content on call back. Inside my onCreate():

private ListAdapter mAdapter;
private ListView listview;

mAdapter = new ListAdapter(getActivity());
listview.setAdapter(mAdapter);

Then, inside my callback which is working as I managed to print out the data:

public void callback(ArrayList<Transaction> list)
{
    expenseslist.clear();

    Log.d("SIZE", String.valueOf(list.size()));
    for(int i = 0; i < list.size(); i++){
        expenseslist.add(list.get(i));
    }

    mAdapter.notifyDataSetChanged();
    listview.invalidateViews();
}


private class ListAdapter extends BaseAdapter {
    LayoutInflater inflater;
    ViewHolder viewHolder;

    public ListAdapter(Context context) {
        inflater = LayoutInflater.from(context);
    }

    public int getCount() {
        return expenseslist.size();
    }

    public Object getItem(int position) {
        return position;
    }

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

    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {

            convertView = inflater.inflate(R.layout.transactioncat_listview_row,
                    null);
            viewHolder = new ViewHolder();

            viewHolder.txt_dcatTitle = (TextView) convertView
                    .findViewById(R.id.txtCatTitle);
            viewHolder.txt_dcatTotal = (TextView) convertView
                    .findViewById(R.id.txtCatTotal);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txt_dcatTitle.setText(expenseslist.get(position)
                .getCategory().getCategoryName().trim());
        viewHolder.txt_dcatTotal.setText(Extra.formatCurrency(expenseslist.get(position)
                .getTransactionAmt()));

        return convertView;
    }
}

I called the notifyDataSetChanged() and invalidateViews() but it is not refreshing. I am not adding new rows to the list view but rather, updating the content in each row.

How can I do this correctly?

public class ExpenseActivity extends Fragment implements TransactionListCallBack{

    private ArrayList<Transaction> transactionlist = new ArrayList<Transaction>();
    private ArrayList<Transaction> expenseslist = new ArrayList<Transaction>();

    private ListAdapter mAdapter;
    private ListView listview;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.activity_expense,container,false);
        
        expenseslist.clear();

        transactionlist = (ArrayList<Transaction>)getArguments().getSerializable("transactionlist");
        for(int j = 0; j < transactionlist.size(); j++){
            expenseslist.add(transactionlist.get(j));
        }

        listview = (ListView) v.findViewById(R.id.transactionCatListview);

        // Binding the list into list view
        mAdapter = new ListAdapter(getActivity());
        listview.setAdapter(mAdapter);
        
        return v;
    }

    @Override
    public void callback(ArrayList<Transaction> list)
    {
        total = 0;
        expenseslist.clear();

        for(int i = 0; i < list.size(); i++){
            expenseslist.add(list.get(i));
            }
        }

        mAdapter.notifyDataSetChanged();
        listview.invalidateViews();
    }

    private class ListAdapter extends BaseAdapter {
        LayoutInflater inflater;
        ViewHolder viewHolder;

        public ListAdapter(Context context) {
            inflater = LayoutInflater.from(context);
        }

        public int getCount() {
            return expenseslist.size();
        }

        public Object getItem(int position) {
            return expenseslist.get(position);
        }

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

        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {

                convertView = inflater.inflate(R.layout.transactioncat_listview_row,
                        null);
                viewHolder = new ViewHolder();

                viewHolder.txt_dcatTitle = (TextView) convertView
                        .findViewById(R.id.txtCatTitle);
                viewHolder.txt_dcatTotal = (TextView) convertView
                        .findViewById(R.id.txtCatTotal);

                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.txt_dcatTitle.setText(expenseslist.get(position)
                    .getCategory().getCategoryName().trim());

            viewHolder.txt_dcatTotal.setText(Extra.formatCurrency(expenseslist.get(position)
                    .getTransactionAmt()));
                    
            return convertView;
        }
    }

    private class ViewHolder {
        CircularProgressBar progressBar;
        ImageView background_catview;
        ImageView icon_catview;
        TextView txt_dcatTitle;
        TextView txt_dcatTotal;
    }

    // override menu item to add new transaction
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.clear();
        inflater.inflate(R.menu.menu_main_addtransaction, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_openNewTransaction) {
            new GetAllCategoriesAsyncTask(
                    new GetAllCategoriesAsyncTask.OnRoutineFinished() {
                        public void onFinish() {
                            AddTransactionActivity addFragment = new AddTransactionActivity();
                            FragmentTransaction tt = getFragmentManager().beginTransaction();
  
                            addFragment.setTransactionListCallBack(ExpenseActivity.this);

                            tt.replace(R.id.frame,addFragment);
                            tt.addToBackStack("tag");
                            tt.commit();
                        }
                    }).execute();
        }
        return super.onOptionsItemSelected(item);
    }
}


Solution 1:[1]

Make this change

public Object getItem(int position) {
    return expenseslist.get(position);
}

Solution 2:[2]

You need to keep a list of your data in adapter for notifyDataSetChanged to work:

Change

mAdapter = new ListAdapter(getActivity(), expenseslist);

And inside your adapter:

list<Transaction> expenseslist;

public ListAdapter(Context context, list<Transaction> expenseslist) {
    inflater = LayoutInflater.from(context);
    this.expenseslist = expenseslist;
}

EDIT:

Also change these methods:

    public Transaction getItem(int position) {
        return expenseslist.get(position);
    }

    public long getItemId(int position) {
        return expenseslist.get(position).getId();
    }

Solution 3:[3]

Hi Denise in your adapter create a new function called updateTransactions(). This is the code for function where expenseslist is the list your passing to adapter.

public void updateTransactions(List<Transaction> list) {
    expenseslist.clear();
    expenseslist.addAll(list);
    notifyDataSetChanged();

    Log.d("SIZE", String.valueOf(expensesList.size()));
}

Then in your callback function call updateTransactions() using instance of adapter like this

public void callback(ArrayList<Transaction> list)
{
    mAdapter.updateTransactions(list);
}

Edit: Demo run code

package ********;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Collections;

/**
 * Created by Rishabh Bhatia on 18/5/17.
 */

public class TestActivity extends Activity {

    private ListView list;
    private ListAdapter adapter;
    private ArrayList<String> dataList;

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

        list = (ListView) findViewById(R.id.list);
        dataList = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            String string = "abc";
            dataList.add(string);
        }

        adapter = new ListAdapter(this, dataList);
        list.setAdapter(adapter);

        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.d("rishabh", "something is selected");
                String transaction = dataList.get(0);
                transaction = "xyz";
                dataList.remove(0);
                dataList.add(0, transaction);
                adapter.updateExpenses(dataList);
            }
        });
    }

    private class ListAdapter extends BaseAdapter {

        private ArrayList<String> expenseslist;

        LayoutInflater inflater;
        ViewHolder viewHolder;

        public ListAdapter(Context context, ArrayList<String> list) {
            inflater = LayoutInflater.from(context);
            this.expenseslist = (ArrayList<String>) list.clone();
        }

        public int getCount() {
            return expenseslist.size();
        }

        public Object getItem(int position) {
            return expenseslist.get(position);
        }

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

        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {

                convertView = inflater.inflate(R.layout.row,
                        null);
                viewHolder = new ViewHolder();

                viewHolder.text = (TextView) convertView
                        .findViewById(R.id.tvRow);

                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.text.setText(expenseslist.get(position));

            return convertView;
        }

        public void updateExpenses(ArrayList<String> list) {
            Log.d("rishabh", "updating the expenses "+list.size());
            expenseslist.clear();
            for(int i =0; i<list.size(); i++) {
                Log.d("rishabh", "inside for loop");
                expenseslist.add(list.get(i));
                Log.d("rishabh", "updating item to list "+list.get(i));
            }
            notifyDataSetChanged();
            Log.d("rishabh", "updating the expenses "+expenseslist.size());
        }
    }

    static class ViewHolder {
        private TextView text;
    }

}

Hopefully it helps.

Solution 4:[4]

Are you are coding within a fragment? Could i ask where/when you call this method

public void callback(ArrayList list);

As I guess you call this method from an activity on a lifecycle method right after you add the fragment. Or maybe you call this method not in a UI thread with a try - catch block.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dishonered
Solution 2
Solution 3
Solution 4 SangLe