0

I need to execute a query to my DB to get some IDs and then use those IDs to execute another query to Realm DB and return the result to the Adapter which is used to create UI. My problem is that the adapter is created in onCreatView (main thread) and i need to let that wait queries result. Do you know how can i do? Really thanks :)

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    Realm realm = Realm.getDefaultInstance();
    cardList = new ArrayList(realm.where(Card.class).findAll().sort("rarity"));
    View rootView = inflater.inflate(R.layout.inventory_fragment_layout, null);
    RecyclerView recyclerView = rootView.findViewById(R.id.inventory_recycler);
    recyclerView.setLayoutManager(new GridLayoutManager(this.getContext(), 2));
    adapter = new inventoryFragmentRecyclerAdapter(this.getContext(), getCards()); <----- getCards() is used here.
    adapter.setClickListener(this);
    recyclerView.setAdapter(adapter);
    TextView topText = rootView.findViewById(R.id.inv_topText);
    topText.setTypeface(Typeface.createFromAsset(getActivity().getAssets(), "fonts/Square.ttf"));

    return rootView;
}



.
.
.
.
.
.



public List<Card> getCards() { //It is used when adapter is created, and it need to contain all cards to display

    //Realm realm = Realm.getDefaultInstance();

    String urlToGet = "myurl";
    String user_name = settings.getString("username", null);

    OkHttpClient client = new OkHttpClient();

    RequestBody formBody = new FormBody.Builder()
            .add("user_name", user_name)
            .build();

    Request request = new Request.Builder()
            .url(urlToGet)
            .post(formBody)
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }

        @Override
        public void onResponse(Call call, final Response response) throws IOException {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }

            responseCardInInventory = response.body().string();

            handler.post(new Runnable() {
                @Override
                public void run() {

                    List<CardInInv> cardListInInv = new ArrayList<>();

                    Gson gson = new Gson();
                    CardInInventory civ = gson.fromJson(responseCardInInventory, CardInInventory.class);
                    cardListInInv = getCards(civ);

                    //HERE I NEED TO USE REALM TO EXECUTE A QUERY USING cardListInInv


                }
            });
        }
    });

    //HERE I NEED TO RETURN THE RESULT OF THE QUERY EXECUTED BEFORE
    return cardsIninv;

}

EDIT 1: Hre is my adapter code

public class inventoryFragmentRecyclerAdapter extends RecyclerView.Adapter<inventoryFragmentRecyclerAdapter.ViewHolder> {

private List<Card> dataCard = new ArrayList<>();
private LayoutInflater mInflater;
private ItemClickListener mClickListener;


// data is passed into the constructor
public inventoryFragmentRecyclerAdapter(Context context, List<Card> data) {
    this.mInflater = LayoutInflater.from(context);
    this.dataCard = data;
}

// inflates the cell layout from xml when needed
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = mInflater.inflate(R.layout.inventory_rw, parent, false);
    return new ViewHolder(view);
}

// binds the data to the in each cell
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.cardImage.setImageBitmap(BitmapFactory.decodeByteArray(dataCard.get(position).getCardImage(), 0, dataCard.get(position).getCardImage().length));
}

// total number of cells
@Override
public int getItemCount() {
    return dataCard.size();
}


// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    ImageView cardImage;

    //instanzio le componenti della pagina

    ViewHolder(View itemView) {
        super(itemView);

        cardImage = itemView.findViewById(R.id.inv_rw_cardimage);
        itemView.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
    }
}

// convenience method for getting data at click position
Card getItem(int id) {
    return dataCard.get(id);
}

// allows clicks events to be caught
public void setClickListener(ItemClickListener itemClickListener) {
    this.mClickListener = itemClickListener;
}

// parent activity will implement this method to respond to click events
public interface ItemClickListener {
    void onItemClick(View view, int position);
}
}
Fyruz
  • 75
  • 1
  • 20

3 Answers3

1

Move your query to your adapter and start your adapter with an empty list. Make your query call at the end of your constructor. When you've successfully finished your query, notify your adapter that it has changed and let it handle the changes with your new data at the end of your queries finishing successfully. So after doing that your adapter would look something like this.

public inventoryFragmentRecyclerAdapter(Context context, List<Card> data) {
    this.mInflater = LayoutInflater.from(context);
    getCards();
}

public void getCards() { //It is used when adapter is created, and it need to contain all cards to display
//Realm realm = Realm.getDefaultInstance();

String urlToGet = "myurl";
String user_name = settings.getString("username", null);

OkHttpClient client = new OkHttpClient();

RequestBody formBody = new FormBody.Builder()
        .add("user_name", user_name)
        .build();

Request request = new Request.Builder()
        .url(urlToGet)
        .post(formBody)
        .build();

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
    }

    @Override
    public void onResponse(Call call, final Response response) throws IOException {
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }

        responseCardInInventory = response.body().string();

        handler.post(new Runnable() {
            @Override
            public void run() {

                List<CardInInv> cardListInInv = new ArrayList<>();

                Gson gson = new Gson();
                CardInInventory civ = gson.fromJson(responseCardInInventory, CardInInventory.class);
                cardListInInv = getCards(civ);

                //use realm to do all your card inventory stuff to get your resulting object
                dataCard = yourObjectListAfterUsingRealm;
                notifyDataSetChanged();
            }
        });
    }
});
}
Bryan Dormaier
  • 800
  • 6
  • 10
  • Can you make a quick example please, im a bit new – Fyruz Jan 17 '18 at 22:44
  • Post your adapter code and I'll see if I can help you iron it out. – Bryan Dormaier Jan 17 '18 at 22:45
  • I've added it. Thank you for your patience. – Fyruz Jan 17 '18 at 22:54
  • I edited the answer with an example of how you can move the query to the adapter and then notify it when you have your resulting objects. – Bryan Dormaier Jan 17 '18 at 23:03
  • this will not work. because `getCards(civ);is executed asynchronously.so `adapter.notifyDataSetChanged()` will be called before geting the list – Bishoy Kamel Jan 17 '18 at 23:06
  • No, you'll have already set the data within the adapter. notifyDataSet just tells the adapter to check for changes based on the data it's binding against. – Bryan Dormaier Jan 17 '18 at 23:16
  • Although looking now, it looks like there is a second `getCards()` method that was not listed. In that case, the notifyDataSet() and setting the adapter data would happen in the successful callback on the second asynchronous call. – Bryan Dormaier Jan 17 '18 at 23:18
  • You use `dataCard` before `notifyDataSetChanged();`, so is thta the same `dataCard` i use in `onBindViewHolder` ? – Fyruz Feb 04 '18 at 13:12
  • Yeah, `notifyDataSetChanged()` tells the adapter that the data your adapter is bound to, so the adapter already knows what it's checking against. You're just notifying it that the collection has changed and it may need to re-bind your views. – Bryan Dormaier Feb 05 '18 at 22:09
1

move This line adapter = new inventoryFragmentRecyclerAdapter(context, yourList); inside onResponse() and pass the list directly without the getCards();

Edit:

initialize your adapter when your data is ready.where ? this is based on your needs.

or initialize it with empty list and call adapter.notifyDataSetChanged() when something changed (added or removed from the list or a brand new list is initialized)

Bishoy Kamel
  • 2,327
  • 2
  • 17
  • 29
  • I need to execute 2 queries, one to a db, one to realm – Fyruz Jan 17 '18 at 22:51
  • so if i use `adapter.notifyDataSetChanged()` when i finished obtaining data it will refresh with new data? – Fyruz Jan 17 '18 at 22:58
  • exactly. from the documentation `notifyDataSetChanged()` -> Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself. – Bishoy Kamel Jan 17 '18 at 23:01
0

Create your adapter with empty list
adapter = new inventoryFragmentRecyclerAdapter(this.getContext(), new ArrayList<Card>());

and then in your adapter create a method like this

public void updateCardList(List<Card> cardList) { this.mCards = cardList; notifyDataSetChanged(); }

and call this function in the
@Override public void onResponse(Call call, final Response response)

insa_c
  • 2,851
  • 1
  • 16
  • 11