1

I am trying to save the button's state in a certain item of RecyclerView whenever a user clicks that button. At the time it was clicked, it's visibility will be gone and another button will be visible. How can I save the button's state so that whenever the app is entirely closed, when I open it again the state of the button is still there?

I tried making a database for the button's visibility state but I couldn't figure out where to put the right code to add the data and save it.

onBindViewHolder() inside RecyclerView class, this is where I put my button click listener.

@Override
    public void onBindViewHolder(final MyViewHolder holder, int position){
        MakerAdapter h = makerList.get(position);
        final String macString = h.getHMac();

        holder.rIcon.setImageResource(h.getHIcon());
        holder.rDevice.setText(h.getHDevice());
        holder.rBrand.setText(h.getHBrand());
        holder.rIp.setText(h.getHIp());
        holder.rMac.setText(h.getHMac());
        holder.rDate.setText(h.getHDate());
        holder.rWifi.setText(h.getHWifi());

        holder.rMark.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSafeDB = new SafeDB(getApplicationContext(), null,null,1);
                holder.rMark.setVisibility(GONE);
                holder.rUnsafe.setVisibility(VISIBLE);
                mSafeDB.addSafeMaker(macString, holder.rMark.getVisibility());
            }
        });

        holder.rUnsafe.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSafeDB = new SafeDB(getApplicationContext(), null,null,1);
                holder.rUnsafe.setVisibility(GONE);
                holder.rMark.setVisibility(VISIBLE);
                mSafeDB.addSafeMaker(macString, holder.rUnsafe.getVisibility());
            }
        });
    }

These are my imports specific to this(onBindViewHolder) method:

import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.facebook.FacebookSdk.getApplicationContext;

This is my database

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SafeDB extends SQLiteOpenHelper {

    private static final int DB_VERSION = 1;
    private static final String DB_NAME = "safedb.db";
    private static final String TABLE_NAME = "marked_safe";
    private static final String COL_ID = "id";
    private static final String COL_MAC = "mac";
    private static final String COL_MARK = "mark";

    //////////Housekeeping START
    public SafeDB(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
        super(context, DB_NAME, factory, DB_VERSION);
    }
    public SafeDB(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        // this.context = context;
    }


    @Override
    public void onCreate(SQLiteDatabase db){
        String query = "CREATE TABLE " + TABLE_NAME + "(" +
                COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COL_MAC + " TEXT, " +
                COL_MARK + " INTEGER " +
                ");";
        db.execSQL(query);
    }

    public void open() throws SQLException {
        close();
        this.getWritableDatabase();
    }

    public void closeDB() {
        SQLiteDatabase db = this.getReadableDatabase();
        if (db != null && db.isOpen())
            db.close();
    }



    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1){
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
//////////Housekeeping END

    public void deleteTable(){
        SQLiteDatabase db = getWritableDatabase();
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
    }

    //IF FIRST TIME. THIS WILL BE TRIGGERED
    public void addSafeMaker(String mac, int mark){
        ContentValues values = new ContentValues();
        values.put(COL_MAC, mac);
        values.put(COL_MARK, mark);
        SQLiteDatabase db = getWritableDatabase();
        db.insert(TABLE_NAME, null, values);
    }


    //UPDATE THE Arp
    public void updateMaker(String mac, int mark){
        ContentValues values = new ContentValues();
        values.put(COL_MAC, mac);
        values.put(COL_MARK, mark);
        SQLiteDatabase db = getWritableDatabase();
        db.update(TABLE_NAME, values, "id = 1", null);
    }


    //GET THE MAC
    public String getMac(String x) {
        SQLiteDatabase db = getWritableDatabase();
        String query = "SELECT * FROM "+ TABLE_NAME+" WHERE "+ COL_ID+" = '" + x+"'" + " LIMIT 1;" ;
        Cursor c = db.rawQuery(query, null);
        c.moveToFirst();

        String mac = c.getString(c.getColumnIndex("mac"));
        return mac;
    }

    //GET THE BUTTON VISIBILITY VALUE
    public String getSafeValue(String x) {
        SQLiteDatabase db = getWritableDatabase();
        String query = "SELECT * FROM "+ TABLE_NAME+" WHERE "+ COL_ID+" = '" + x+"'" + " LIMIT 1;" ;
        Cursor c = db.rawQuery(query, null);
        c.moveToFirst();

        String mark = c.getString(c.getColumnIndex("mark"));
        return mark;
    }

    //CHECK IF EMPTY
    public boolean isEmpty() {
        boolean e = true;
        SQLiteDatabase db = getWritableDatabase();
        String count = "SELECT count(*) FROM " + TABLE_NAME;
        Cursor c = db.rawQuery(count, null);
        c.moveToFirst();
        int icount = c.getInt(0);
        e = icount <= 0;
        return e;
    }

    public int getCount(){
        int count = 0;
        SQLiteDatabase db = getWritableDatabase();
        String c = "SELECT count(*) FROM " + TABLE_NAME;
        Cursor x = db.rawQuery(c, null);
        x.moveToFirst();
        count = x.getInt(0);
        return count;
    }
}

  • Use `SharedPreference` instead of database. All you need is to add some variable like `btnVisibility = true` and use it in `onCreate` method of your activity to determine does the button visible or not – Asset Bekbossynov Apr 23 '19 at 04:15

2 Answers2

0

database is your best option, you should put the code inside onclicklistiner.

if you want to "toggle" so save a boolean in the database that if it true than the user have been clicked on the item, and if its false, the user didn't clicked the item.

than in your onclicklistiner you do an if statement on the boolean.

DVIR
  • 51
  • 3
0

The following is a working example based upon your code, albeit that the Adapter is probably quite different. Some assumptions/guesswork were made. So the code is very much in-principle code and would need to be adapted.

The above adds a mac (assuming a mac is unique) when the adapter is instantiated, a specific mac will only be added once.

In the onBindView method the status/mark is retrieved from the db to determine which button is displayed.

When a button is clicked the displayed button is toggled and the respective mac (the button's tag is used to store the mac) is used to update the respective row according to the mac.

SafeDB.java

public class SafeDB extends SQLiteOpenHelper {

    private static final int DB_VERSION = 1;
    private static final String DB_NAME = "safedb.db";
    private static final String TABLE_NAME = "marked_safe";
    private static final String COL_ID = "id";
    private static final String COL_MAC = "mac";
    private static final String COL_MARK = "mark";

    //////////Housekeeping START
    public SafeDB(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
        super(context, DB_NAME, factory, DB_VERSION);
    }
    public SafeDB(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase db){
        String query = "CREATE TABLE " + TABLE_NAME + "(" +
                COL_ID + " INTEGER PRIMARY KEY, " + // AUTOINCREMENT REMOVED NOT NECESSARY
                COL_MAC + " TEXT UNIQUE, " + // Probably should be unique
                COL_MARK + " INTEGER " +
                ");";
        db.execSQL(query);
    }

    public void open() throws SQLException {
        close();
        this.getWritableDatabase();
    }

    public void closeDB() {
        SQLiteDatabase db = this.getReadableDatabase();
        if (db != null && db.isOpen())
            db.close();
    }



    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1){
        deleteTable(); // might as well use the deleteTable method as it exists
        onCreate(db);
    }
//////////Housekeeping END

    public void deleteTable(){
        SQLiteDatabase db = getWritableDatabase();
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
    }

    //IF FIRST TIME. THIS WILL BE TRIGGERED
    // wont hurt to return the id of the inserted row (-1 if no row inserted)
    public long addSafeMaker(String mac, int mark){
        ContentValues values = new ContentValues();
        values.put(COL_MAC, mac);
        values.put(COL_MARK, mark);
        SQLiteDatabase db = getWritableDatabase();
        return db.insert(TABLE_NAME, null, values);
    }


    //UPDATE THE Arp
    public int updateMaker(String mac, int mark){
        ContentValues values = new ContentValues();
        values.put(COL_MARK, mark);
        //values.put(COL_MARK, mark); // guess this wont change rather that it will be used to determine the row to be updated
        SQLiteDatabase db = getWritableDatabase();
        //db.update(TABLE_NAME, values, "id = 1", null); // Will only ever update 1 specific row
        String whereclause = COL_MAC + "=?";
        String[] whereargs = new String[]{mac};
        return db.update(TABLE_NAME,values,whereclause,whereargs);
    }


    //GET THE MAC
    public String getMac(String x) {
        SQLiteDatabase db = getWritableDatabase();
        String query = "SELECT * FROM "+ TABLE_NAME+" WHERE "+ COL_ID+" = '" + x+"'" + " LIMIT 1;" ;
        Cursor c = db.rawQuery(query, null);
        c.moveToFirst();

        String mac = c.getString(c.getColumnIndex("mac"));
        return mac;
    }

    public boolean getSafeValue(String mac) {
        int mark = 0; // assume marked safe if row not found
        SQLiteDatabase db = this.getWritableDatabase();
        String whereclause = COL_MAC + "=?";
        String[] whereargs = new String[]{mac};
        Cursor c = db.query(TABLE_NAME,null,whereclause,whereargs,null,null,null);
        if (c.moveToFirst()) {
            mark = c.getInt(c.getColumnIndex(COL_MARK));
        }
        return mark < 1;
    }

    //GET THE BUTTON VISIBILITY VALUE
    /*
    public String getSafeValue(String x) {
        SQLiteDatabase db = getWritableDatabase();
        String query = "SELECT * FROM "+ TABLE_NAME+" WHERE "+ COL_ID+" = '" + x+"'" + " LIMIT 1;" ;
        Cursor c = db.rawQuery(query, null);
        c.moveToFirst();

        String mark = c.getString(c.getColumnIndex("mark"));
        return mark;
    }
    */

    //CHECK IF EMPTY
    /*
    public boolean isEmpty() {
        boolean e = true;
        SQLiteDatabase db = getWritableDatabase();
        String count = "SELECT count(*) FROM " + TABLE_NAME;
        Cursor c = db.rawQuery(count, null);
        c.moveToFirst(); // WARINING if no rows then next line will crash INDEX OUT OF BOUNDS
        int icount = c.getInt(0);
        e = icount <= 0;
        return e;
    }
    */

    public long getCount(){
        int count = 0;
        SQLiteDatabase db = getWritableDatabase();
        return DatabaseUtils.queryNumEntries(this.getWritableDatabase(),TABLE_NAME);
        /*
        quick form used as above
        String c = "SELECT count(*) FROM " + TABLE_NAME;
        Cursor x = db.rawQuery(c, null);
        x.moveToFirst();
        count = x.getInt(0);
        return count;
        */
    }
}
  • You should note the comments, there were some issues with your code.

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    ArrayList<String> mMacList;
    SafeDB mDB;
    Context mContext;


    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView mMac;
        public Button mSafe;
        public Button mUnsafe;

        public MyViewHolder(View view) {
            super(view);
            mMac = view.findViewById(R.id.name);
            mSafe = view.findViewById(R.id.marksafe);
            mUnsafe = view.findViewById(R.id.markunsafe);
        }
    }

    public MyAdapter(Context context,ArrayList<String> maclist) {
        mMacList = maclist;
        mContext = context;
        mDB = new SafeDB(context);

        // Could add the mac's to the DB here (note DB changed so mac is unqiue so same mac won't be added)
        for (String mac: maclist) {
            mDB.addSafeMaker(mac,0);
        }
    }

    @NonNull
    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.mylist_item,viewGroup, false);
        MyViewHolder vh = new MyViewHolder(v);
        mContext = viewGroup.getContext();
        return vh;
    }

    @Override
    public void onBindViewHolder(@NonNull final MyViewHolder viewHolder, int i) {
        viewHolder.mMac.setText(mMacList.get(i));
        if (mDB == null) {
           mDB = new SafeDB(viewHolder.mMac.getContext());
        }
        //Set the Tag for the buttons with the mac so it can be retrieved
        viewHolder.mSafe.setTag(mMacList.get(i));
        viewHolder.mUnsafe.setTag(mMacList.get(i));

        // Display the buttons according to the database
        if (mDB.getSafeValue(mMacList.get(i))) {
            viewHolder.mSafe.setVisibility(View.GONE);
            viewHolder.mUnsafe.setVisibility(View.VISIBLE);
        } else {
            viewHolder.mSafe.setVisibility(View.VISIBLE);
            viewHolder.mUnsafe.setVisibility(View.GONE);
        }
        // Add the onCLickListeners
        viewHolder.mSafe.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewHolder.mSafe.setVisibility(View.GONE);
                viewHolder.mUnsafe.setVisibility(View.VISIBLE);
                String mac = (String) ((Button) viewHolder.mSafe).getTag();
                changeSafeMark((String)viewHolder.mSafe.getTag(),0);
            }
        });
        viewHolder.mUnsafe.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewHolder.mSafe.setVisibility(View.VISIBLE);
                viewHolder.mUnsafe.setVisibility(View.GONE);
                changeSafeMark((String)viewHolder.mUnsafe.getTag(),1);
            }
        });
    }

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

    public int changeSafeMark(String mac, int mark) {
        int result = mDB.updateMaker(mac,mark);
        return result;
    }
}
  • This is probably quite different and only the simplest of layouts was used.

mylistitem.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/marksafe"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="Make Safe"
        />
    <Button
        android:id="@+id/markunsafe"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:text="Make Unsafe"
        />
</LinearLayout>

MainActivity.java

The invoking activity used to test :-

public class MainActivity extends AppCompatActivity {

    RecyclerView mList;
    RecyclerView.LayoutManager mLayoutManager;
    MyAdapter mMyAdapter;

    // The underlying data (just a list of strings for the macs)
    ArrayList<String> mymacliist = new ArrayList<>(Arrays.asList("M1","M2","M3"));

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

        mList = this.findViewById(R.id.mylist);
        mLayoutManager = new LinearLayoutManager(this);
        mList.setLayoutManager(mLayoutManager);
        mMyAdapter = new MyAdapter(this,mymacliist);
        mList.setAdapter(mMyAdapter);
    }
}

Results

When First run :-

enter image description here

After M2 and M3 are clicked (buttons are changed to Make Safe when clicked), then the App is stopped and then started :-

enter image description here

MikeT
  • 51,415
  • 16
  • 49
  • 68