user5366495
user5366495

Reputation:

Checkbox not updating in my RecyclerView when it's supposed to

I made a video demo of the problem here: https://www.youtube.com/watch?v=u9G4IVemayE

As you se in the video there is a checkbox in my recyclerview list item. This checkbox needs to be checked, when the user choose to save a event to the database. But it set the checkbox to checked in the list item, when then user returns from the activity he enteres when he clicks on a item.

notifyDataSetChanged(); and recyclerView.invalidate() hasn't helped either, I have put them many different places in my attempt. The checkbox is only updated when I restart my app.

EventActivity where the list item is shown and saved from:

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {

        case R.id.menu_saveToDb:
            DbHelper.getInstance(EventActivity.this).saveToDb(eventID, title, description, subtitle, start, end, 1, url, imgURL);

      //1 in the parameters means that it shall have a checked box. 0:unchecked

            MainActivity.databaseList.add(new Event(eventID, title, description, subtitle, start, end, url, imgURL, 1));
            checkBox.setChecked(true);
            //this checkbox is for an activity layout. nevermind it.
            break;
  }
}

This is in MainActivity:

// I have tried to call this method different places.

     public void initRecyclerView(List<Event> list) {

    rvAdapter = new RvAdapter(list, this);
    recyclerView.setAdapter(rvAdapter);
    rvAdapter.notifyDataSetChanged();
  }

**From the RecyclerView Adapter class where i set the checkbox if 1 or 0 **

 @Override
  public void onBindViewHolder(ViewHolder holder, int position) {

    if(list.get(position).getIsDb() == 1){
        holder.checkBox.setChecked(true);
    }else{
        holder.checkBox.setChecked(false);
    }
}

The whole MainActivity class:

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

    recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));


    dbHelper = new DbHelper(this);
    preferences = PreferenceManager.getDefaultSharedPreferences(this);
    showListPref = preferences.getInt("SHOW_LIST", 3);

    databaseList = new ArrayList<>();
    jsonList = new ArrayList<>();

    //Vi loader dataen fra start af, så de er klar til at vises frem når som helst og hurtigt
    loadDatabaseData(this);
    new JsonParser().execute();


    toolbar = (Toolbar) findViewById(R.id.toolbar2);
    toolbar.setTitleTextColor(Color.WHITE);
    toolbar.showOverflowMenu();
    setSupportActionBar(toolbar);
    getSupportActionBar().setIcon(R.drawable.ic_location_city);
    getSupportActionBar().setTitle("  Events in Aarhus");


/*
   Sørger huske den valgte liste efter appen er lukket og vise den valgte igen når appen åbnes igen
   og sætter også titlen på toolbaren efter hvilken database der vises.
    */


    if (showListPref == 2) {
        initRecyclerView(databaseList);
        getSupportActionBar().setTitle("  Events in Aarhus (Database)");

    } else if (showListPref == 3) {
        initRecyclerView(jsonList);
        getSupportActionBar().setTitle("  Events in Aarhus (JSON)");

    }
}


/*
     Her loader vi de data der er gemt i databasen og gemmer dem i List<Event> databaseList.
     databaseList bruges til adapteren for recyclerviewet så dataen fra databasen kan vises i recyclerviwet.

      */
public void loadDatabaseData(Context context) {

    dbHelper = new DbHelper(context);
    sqLiteDatabase = dbHelper.getWritableDatabase();
    Cursor c = sqLiteDatabase.rawQuery("SELECT * FROM " + DbHelper.DATABASE_NAME, null);
    EventCurserWrapper wrapper = new EventCurserWrapper(c);

    checkID = new ArrayList<>();

    if (c.moveToFirst()) {
        while (!c.isAfterLast()) {
            databaseList.add(new Event(wrapper.getEvent().getEventID(), wrapper.getEvent().getTitle(), wrapper.getEvent().subtitle, wrapper.getEvent().getDescrption(), wrapper.getEvent().startTime, wrapper.getEvent().endTime, wrapper.getEvent().url, wrapper.getEvent().getImgURL(), wrapper.getEvent().isDb));
            checkID.add(wrapper.getEvent().eventID);
            c.moveToNext();
        }
    }

    c.close();

}


/*
Denne metode bruger list parameteren til at skifte mellem de forskellige
lister når man fra menuen på toolbaren vælger om man vil se events fra JSON eller Databasen.
 */
public void initRecyclerView(List<Event> list) {

    rvAdapter = new RvAdapter(list, this);
    recyclerView.setAdapter(rvAdapter);
    rvAdapter.notifyDataSetChanged();
}


/*
Async task bruges her til at loade json data og gemme dem i en arraylist
og køre asynkron med appens "mainthread".
 */
private class JsonParser extends AsyncTask<String, Void, String> {

    HttpURLConnection conn;
    URL url = null;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        Toast.makeText(MainActivity.this, "LOADING DATA FROM API", Toast.LENGTH_LONG).show();
    }


    @Override
    protected String doInBackground(String... params) {

        try {

            url = new URL(URL);
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");

            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {

                InputStream inputStream = conn.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                StringBuilder result = new StringBuilder();
                String line;


                while ((line = reader.readLine()) != null) {
                    result.append(line);
                }

                return (result.toString());
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (Exception ee) {
            ee.printStackTrace();
        }

        return "";

    }


    @Override
    protected void onPostExecute(String s) {

        try {

            JSONObject object = new JSONObject(s);
            JSONArray events = object.getJSONArray("events");

            for (int x = 0; x < events.length(); x++) {
                JSONObject object1 = events.getJSONObject(x);

                String eventID = object1.getString(EVENT_ID);
                String title = object1.getString(TITLE);
                String subtitle = object1.getString(SUBTITLE);
                String description = object1.getString(DESCRIPTION);
                String url = object1.getString(EVENT_URL);
                String imgURL = object1.getString(PICTURE_URL);


                JSONArray dateArray = object1.getJSONArray(DATES);
                for (int y = 0; y < dateArray.length(); y++) {
                    JSONObject dateObject = dateArray.getJSONObject(y);
                    long start = dateObject.getLong(START);
                    long end = dateObject.getLong(END);

                    if (!title.isEmpty()) {


                        /*
                        Da datoen vi får fra JSON er i UNIX/Epoch format, så
                        konverter vi denne dato til rigtig "kalender" dato med koden nedunder
                         */
                        Date date = new Date(start * 1000);
                        SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.getDefault());
                        format.setTimeZone(TimeZone.getDefault());
                        String startdate = format.format(date);

                        Date date2 = new Date(end * 1000);
                        SimpleDateFormat format2 = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.getDefault());
                        format2.setTimeZone(TimeZone.getDefault());
                        String enddate = format2.format(date2);


                        /*
                        Her tjekker vi om de events der netop bliver loaded fra JSON om de i forvejen
                        eksister i databasen og hvis de gør, sættes flag til 1 og denne værdi bruges
                        i adapteren til at sætte checkboxen til true : 0 = false;
                         */
                        int flag = 0;
                        if (DbHelper.getInstance(MainActivity.this).getEventIDs(eventID).contains(eventID) == true) {
                            flag = 1;
                        }

                        jsonList.add(new Event(eventID, title, subtitle, description, startdate, enddate, url, imgURL, flag));

                    }
                }
            }


            rvAdapter.notifyDataSetChanged();
            Toast.makeText(MainActivity.this, events.length() + " events loaded", Toast.LENGTH_LONG).show();

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

    }

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);

    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {

        case R.id.listfragmentDemo:
            startActivity(new Intent(MainActivity.this, MainActivityTest.class));
            break;

        case R.id.menu_showDatabase:

            preferences.edit().putInt("SHOW_LIST", 2).apply();
            initRecyclerView(databaseList);

            getSupportActionBar().setTitle("Events in Aarhus (Database)");
            break;

        case R.id.menu_showJson:

            preferences.edit().putInt("SHOW_LIST", 3).apply();
            initRecyclerView(jsonList);
            getSupportActionBar().setTitle("Events in Aarhus (JSON)");
            break;
    }

    return super.onOptionsItemSelected(item);
}


/*
OnResume kaldes i stedet for onRestart, da vi kan være sikker på kaldes i activitens lifecyclus
som du engang fortalte på tavlen (;
 */
@Override
protected void onResume() {
    super.onResume();
    rvAdapter.notifyDataSetChanged();
}

}

The whole adapter class for RecyclerView:

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

private static List<Event> list;
private static Context context;

public RvAdapter(List<Event> list, Context context) {
    this.list = list;
    this.context = context;
}


@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {


    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View v = inflater.inflate(R.layout.list_item, parent, false);
    ViewHolder viewHolder = new ViewHolder(v);

    return viewHolder;
}



@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    holder.title.setText(list.get(position).getTitle());
    holder.eventTime.setText("START:  " + list.get(position).getStartTime() + "      END:  " + list.get(position).getEndTime());


    /*
    Hvis eventet eksisterer i databasen (1) så sæt checkboxen =true
     */
    if(list.get(position).getIsDb() == 1){
        holder.checkBox.setChecked(true);
    }else{
        holder.checkBox.setChecked(false);
    }
}

@Override
public int getItemCount() {
    return list.size();
}


public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

     TextView title, eventTime;
    CheckBox checkBox;

    public ViewHolder(View itemView) {
        super(itemView);

        title = (TextView) itemView.findViewById(R.id.item_event_title);
        eventTime = (TextView) itemView.findViewById(R.id.item_event_time);
        checkBox = (CheckBox)itemView.findViewById(R.id.item_event_checkbox);
        itemView.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {

        Intent intent = new Intent(context, EventActivity.class);

        intent.putExtra("EVENT_ID", list.get(getAdapterPosition()).getEventID());
        intent.putExtra("TITLE", list.get(getAdapterPosition()).getTitle());
        intent.putExtra("START", list.get(getAdapterPosition()).getStartTime());
        intent.putExtra("END", list.get(getAdapterPosition()).getEndTime());
        intent.putExtra("DESCRIPTION", list.get(getAdapterPosition()).getDescrption());
        intent.putExtra("SUBTITLE", list.get(getAdapterPosition()).getSubtitle());
        intent.putExtra("URL", list.get(getAdapterPosition()).getUrl());
        intent.putExtra("IMG_URL", list.get(getAdapterPosition()).getImgURL());
        intent.putExtra("isDatabase", list.get(getAdapterPosition()).getIsDb());
        intent.putExtra("POS", getAdapterPosition());

        context.startActivity(intent);



    }
}

}

Upvotes: 0

Views: 855

Answers (2)

user5366495
user5366495

Reputation:

I found the solution in a single line: MainActivity.jsonList.get(position).setIsDb(1);

I get the position of the clicked item with intentExtras: position = getIntent().getIntExtra("POS", 0); in the EventActivity where an event is shown after a click on a liste-item

and use that position to update the isDB variable of the model data. Then onReturn to MainActivity I just call: notifyDataSetChanged();

the isDB variable is updated to 1 inside here after I save to the database.

 @Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {


        case R.id.menu_saveToDb:


            DbHelper.getInstance(EventActivity.this).saveToDb(eventID, title, description, subtitle, start, end, 1, url, imgURL);

            MainActivity.databaseList.add(new Event(eventID, title, description, subtitle, start, end, url, imgURL, 1));

            //Update isDB of the current model data for the seleceted list_item
            MainActivity.jsonList.get(position).setIsDb(1);

            //This is for another checkbox. nevermind it
            checkBox.setChecked(true);
            break;
 }
}

Upvotes: 1

lubilis
lubilis

Reputation: 4160

You are updating boolean value in db but your model item in Main Activity is not updated since RecyclerView is already instantiated.

There are several methods to solve this:

  • Reload RecyclerView data model in MainActivity onResume method.
  • Notify from EventActivity to MainActivity the change and update item in RecyclerView (class LocalBroadcast manager can help you in this) (BEST APPROACH)
  • Always read from db to get updated bool value (in method -> list.get(position).getIsDb()) (WORST APPROACH)

Upvotes: 0

Related Questions