0

This is how my ListFragment looks

public class TransactionListFragment extends ListFragment {
    private List<Transaction> mTransactions;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActivity().setTitle(R.string.transactions);
        mTransactions = Transactions.get(getActivity()).getTransactionsFromServer();

        ArrayAdapter<Transaction> adapter = new ArrayAdapter<>(getActivity(),
                android.R.layout.simple_list_item_1, mTransactions);
        setListAdapter(adapter);
    }
}

and Transactions.get(getActivity()).getTransactionsFromServer(); looks like

private void getTransactionsFromServer() {
        final String url = "myURL";
        RequestQueue queue = Volley.newRequestQueue(mContext);

        JsonObjectRequest request = new JsonObjectRequest(url, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.d("GET /Transactions:", response.toString());
                        generateTransactionCollectionFromResponse(response);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // handle error
            }
        }) {

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<>();
                headers.put("BEARER", "55b885274e7912280095ef80ac1cb937:d8922b44-75af-4810-a87e");
                return headers;
            }
        };

        queue.add(request);
    }

    private void generateTransactionCollectionFromResponse(JSONObject response) {
        JSONArray transactionsJson = null;
        try {
            transactionsJson = response.getJSONArray("transactions");
            Log.d("TransactionsJson:", transactionsJson.toString());

            for (int i = 0; i < transactionsJson.length(); i++) {
                JSONObject transactionJson = transactionsJson.getJSONObject(i);
                Transaction transaction = new Transaction(transactionJson.getString("id"), transactionJson.getString("name"));
                mTransactions.add(transaction);
            }

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

Given that the task will happen asynchronously, the ListView would be empty initially.

Question

How do I indicate from my Transactions that reload the listView once I am done getting the results?

UPDATE
This is how Transaction is constructed

public class Transactions {
    private List<Transaction> mTransactions;
    private static Transactions sTransactions;
    private Context mContext;

    private Transactions(Context appContext) {
        mContext = appContext;
        mTransactions = new ArrayList<>();
    }

Question
How can I get Adapter or ListView?

daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • `((BaseAdapter)getListView().getAdapter()).notifyDataSetChanged()`? – Blackbelt Feb 23 '15 at 20:21
  • 1
    As you can see from my code `Transaction` has no reference to `ListView` or `Adapter`. @Blackbelt, in `Transaction` class, `getListView()` is not found – daydreamer Feb 23 '15 at 20:26
  • The problem here is, If I'm not mistaken, to get a reference to the `Adapter` from an asynchronous method call, @Blackbelt What OP need to do is implement a callback method in `ListFragment`, e.g. an Interface? – Marcus Feb 23 '15 at 20:28
  • @Selvin, I think it keeps a reference, but I could be wrong – Blackbelt Feb 23 '15 at 20:28
  • I updated my question with what `Transaction` looks like. It does not have reference to `ListView` or `Adapter` – daydreamer Feb 23 '15 at 20:30
  • @daydreamer, I did the wrong assumption that your method was declared inside `TransactionListFragment`. One solution could be the delegate pattern through an interface, as suggested by @Marcus – Blackbelt Feb 23 '15 at 20:31
  • or since your Transactions object as a context you could broadcast the an event, e.g "Data ready", and use a `BroadcastReceiver` to refresh the adapter's dataset – Blackbelt Feb 23 '15 at 20:34
  • @Blackbelt, any example? – daydreamer Feb 23 '15 at 20:35
  • @Blackbelt that's two valid options. Someone should start typing an answer... And I'm too tierd :P – Marcus Feb 23 '15 at 20:35
  • @Nickolaus also mentioned AsyncTask, which comes with an own callback method and runs on the UI thread, where an `ListView` update is possible. So: Delegation pattern, LocalBroadcast and AsyncTask is on the table. – Marcus Feb 23 '15 at 20:37
  • 1
    for quick and dirty, I have passed `ListView` to `Transaction` and used `((BaseAdapter)getListView().getAdapter()).notifyDataSetChanged()` to update view and that works well, Thanks @Blackbelt – daydreamer Feb 23 '15 at 20:52
  • Check this post [Update ArrayList of adapter dynamically](http://stackoverflow.com/questions/28539666/recyclerview-adapter-and-viewholder-update-dynamically/28540382#28540382) – Xcihnegn Feb 23 '15 at 21:31

3 Answers3

2

Quick easy/dirty solution could be to provide the ListView or the Adapter instance to the Transactions' constructor, and when you finish to parse the data, you can just call:

((BaseAdapter)mListView.getAdapter()).notifyDataSetChanged();

be aware that it has to run on the UI Thread.

Or,

Solution using LocalBroadcast

In your Fragment declare a BroadcastReceiver:

public class MyReceiver extends BroadcastReceiver {    
    @Override
    public void onReceive(Context context, Intent intent) {
    }
} 

and an action for the IntentFilter

public static final String DATA_READY_ACTION = "DATA_READY_ACTION";
public MyBrodacastReceiver mReceiver;

you need to register and unregister it. You could use onResume to register it, onPause to unregister it:

LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(DATA_READY_ACTION));

to unregister

LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mMessageReceiver);

in your Transactions class, when the new data is available, you can BroadCast the event "Data Ready"

  Intent intent = new Intent(TransactionFragment.DATA_READY_ACTION);
  LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

When onReceive is fired you can update/refresh your ListView.

Solution using an Interface

Let the Activity implement an interface,

public interface DataListener {
    public void onDataReceived(List<Transaction> t);
}

and pass a reference of the class that implements this interface, e.g. your ListFragment, to your Transactions class. After you finish to fill up your data, you can notify through this object, the event. E.g

private final DataListener mListener;
private Transactions(Context appContext, DataListener listener) {
    mContext = appContext;
    mListener = listener;
    mTransactions = new ArrayList<>();           
}

//.. fetch data code
if (mListener != null) {
    mListener.onDataReceived(mTransactions);
} 

Please check for typo

Blackbelt
  • 156,034
  • 29
  • 297
  • 305
0

you should use adapter.notifyDataSetChanged(),

more info: http://developer.android.com/reference/android/widget/BaseAdapter.html#notifyDataSetChanged()

Matias Elorriaga
  • 8,880
  • 5
  • 40
  • 58
0

You will need to create an AsyncTask which you can call in your on create method, then you can:

  • run Transactions.get(getActivity()).getTransactionsFromServer(); in the doInBackground method
    and
  • create/update your ListAdapter in the onPostExecute method
  • Nickolaus
    • 4,785
    • 4
    • 38
    • 60