#java #android #android-recyclerview #android-button #android-database
#java #Android #android-recyclerview #android-кнопка #android-база данных
Вопрос:
Я пытаюсь сохранить состояние кнопки в определенном элементе RecyclerView всякий раз, когда пользователь нажимает эту кнопку. В момент нажатия на него его видимость исчезнет, и будет видна другая кнопка. Как я могу сохранить состояние кнопки, чтобы всякий раз, когда приложение полностью закрывается, когда я снова его открываю, состояние кнопки все еще сохранялось?
Я попытался создать базу данных для состояния видимости кнопки, но я не мог понять, куда поместить правильный код для добавления данных и их сохранения.
onBindViewHolder() внутри класса RecyclerView, вот куда я помещаю свой прослушиватель нажатия кнопки.
@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());
}
});
}
Это мой импорт, специфичный для этого метода (onBindViewHolder):
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.facebook.FacebookSdk.getApplicationContext;
Это моя база данных
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 amp;amp; 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;
}
}
Комментарии:
1. Используйте
SharedPreference
вместо базы данных. Все, что вам нужно, это добавить некоторую переменную likebtnVisibility = true
и использовать ее вonCreate
методе вашей деятельности, чтобы определить, видна кнопка или нет
Ответ №1:
Ниже приведен рабочий пример, основанный на вашем коде, хотя адаптер, вероятно, сильно отличается. Были сделаны некоторые предположения / догадки. Таким образом, код очень похож на код в принципе, и его необходимо будет адаптировать.
Вышесказанное добавляет mac (при условии, что mac является уникальным) при создании экземпляра адаптера конкретный mac будет добавлен только один раз.
В методе onBindView статус / метка извлекается из базы данных, чтобы определить, какая кнопка отображается.
При нажатии кнопки отображаемая кнопка переключается, и соответствующий mac (тег кнопки используется для сохранения mac) используется для обновления соответствующей строки в соответствии с 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 amp;amp; 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;
*/
}
}
- Обратите внимание на комментарии, были некоторые проблемы с вашим кодом.
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 resu<
}
}
- Вероятно, это совсем другое дело, и был использован только самый простой из макетов.
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
Действие вызова, используемое для тестирования :-
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);
}
}
Результаты
При первом запуске :-
После нажатия M2 и M3 (кнопки изменены таким образом, чтобы при нажатии они были безопасными) приложение останавливается, а затем запускается :-
Комментарии:
1. Спасибо за этот прототип, концепции вашего кода здесь решили мою проблему.
Ответ №2:
база данных — ваш лучший вариант, вы должны поместить код в onclicklistiner.
если вы хотите «переключить», сохраните логическое значение в базе данных, чтобы, если оно true, пользователь нажал на элемент, и если оно false, пользователь не нажал на элемент.
чем в вашем onclicklistiner вы выполняете оператор if для логического значения.