Android Room Database Issues - android

I'm trying to complete this small in class exercise where we have to save data using Room Database and then display that information in a ListView.
This app is about a player. The player has 4 fields (Id, Name, Position, Number of Goals scored).
The ID of the very first player saved should be #1, #2 for the second player and so on.
The user must enter the name of the player through the EditText field.
There are then three radio buttons of which, the user has to select one in order to choose their position (Goalie, Defence, Forward). Finally, the last field the user must enter the number of goals this player has scored through the use of the EditText field.
Finally, the last field the user must enter the number of goals this player has scored through the use of the EditText field.
Once the user clicks the "SAVE" button, all the fields previously selected will be cleared and their will be a quick toast message displaying the ID# of the player.
In order to view all the saved data the user must click the "VIEW ALL" button which will take them to the second activity and display the Name of the Player, Position, Number of Goals scored in the next activity.
I'm not too familiar with room so whenever I press the "Save"Save button my app crashes. Any help would be greatly appreciated, Thanks!
MainActivity.Java
public class MainActivity extends AppCompatActivity {
private EditText playerEdt, goalsEdt;
private int id = 0;
private RadioGroup groupRad;
private RadioButton radioButton;
private MyDatabase myDb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playerEdt = findViewById(R.id.edtPlayer);
goalsEdt = findViewById(R.id.edtGoals);
myDb = MyDatabase.getInstance(MainActivity.this);
}
public void saveData(View view) {
id++;
String name = playerEdt.getText().toString();
groupRad = findViewById(R.id.radGroup);
int selectedId = groupRad.getCheckedRadioButtonId();
radioButton = findViewById(selectedId);
String position = radioButton.getText().toString();
String goalsString = goalsEdt.getText().toString();
int goals = Integer.valueOf(goalsString);
Player player = new Player(id, name, position, goals);
myDb.playerDao().insert(player);
playerEdt.setText("");
goalsEdt.setText("");
groupRad.clearCheck();
}
public void viewData(View view) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
}
SecondActivity.Java
public class SecondActivity extends AppCompatActivity {
ListView listView;
MyDatabase database;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
listView = findViewById(R.id.listView);
database = MyDatabase.getInstance(SecondActivity.this);
List<Player> players = database.playerDao().getAll();
ArrayAdapter adapter = new ArrayAdapter(SecondActivity.this, android.R.layout.simple_list_item_1, players);
listView.setAdapter(adapter);
}
}
Player.Java
#Entity
public class Player {
#PrimaryKey
private int id;
#ColumnInfo(name = "player_name")
private String name;
#ColumnInfo(name = "player_position")
private String position;
#ColumnInfo(name = "player_goals")
private int goals;
public Player(int id, String name, String position, int goals) {
this.id = id;
this.name = name;
this.position = position;
this.goals = goals;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getGoals() {
return goals;
}
public void setGoals(int goals) {
this.goals = goals;
}
}
PlayerDao.java
#Dao
public abstract class PlayerDao {
#Insert
public abstract void insert (Player player);
#Delete
public abstract void delete (Player player);
#Update
public abstract void update (Player player);
#Query("select * from Player")
public abstract List<Player> getAll();
}
MyDatabase.Java
#Database(entities = Player.class, version = 1)
public abstract class MyDatabase extends RoomDatabase {
public abstract PlayerDao playerDao();
private static MyDatabase instance;
public static MyDatabase getInstance(Context context){
if( instance == null){
instance = Room.databaseBuilder(context, MyDatabase.class, "PlayerDb")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
activity_main.xml: https://www.codepile.net/pile/d5rq8mx2
activity_second.xml: https://www.codepile.net/pile/2vbYzXq3

It would appear that you have a number of issues:-
You need to have getters for all the members of the Player class.
These were added (and setters) :-
public int getGoals() {
return goals;
}
public void setGoals(int goals) {
this.goals = goals;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
The core issue is that the attemps to find the views in the saveData is returning nulls and hence you are getting a null pointer exception (NPE) because the views don't exist within the SAVE button.
The solution is to find the views (i.e use findViewById in the onCreate method).
There is also a lack of data verification which will causes issue. The following version of MainActivity.java handles the issues :-
public class MainActivity extends AppCompatActivity {
private EditText playerEdt, goalsEdt;
private int id = 0;
private RadioGroup groupRad;
private RadioButton radioButton;
private MyDatabase myDb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playerEdt = findViewById(R.id.edtPlayer);
goalsEdt = findViewById(R.id.edtGoals);
groupRad = findViewById(R.id.radGroup);
myDb = MyDatabase.getInstance(MainActivity.this);
}
public void saveData(View view) {
id++;
String name = playerEdt.getText().toString();
if (name.length() < 1) {
Toast.makeText(view.getContext(),"Player Name is blank. Try again.",Toast.LENGTH_SHORT).show();
playerEdt.requestFocus();
}
radioButton = findViewById(groupRad.getCheckedRadioButtonId());
if (radioButton == null) {
Toast.makeText(view.getContext(),"You must select Goalie Defence or Forward. Try again",Toast.LENGTH_SHORT).show();
return;
}
String position = radioButton.getText().toString();
String goalsString = goalsEdt.getText().toString();
int goals = 0;
try {
goals = Integer.valueOf(goalsString);
} catch (Exception e) {
Toast.makeText(view.getContext(),"You must give the number of Goals. try again.",Toast.LENGTH_SHORT).show();
goalsEdt.requestFocus();
}
Player player = new Player(id, name, position, goals);
if (myDb.playerDao().insert(player) < 1) {
Toast.makeText(view.getContext(),"Player not Added (duplicate)",Toast.LENGTH_SHORT).show();
return;
}
playerEdt.setText("");
goalsEdt.setText("");
groupRad.clearCheck();
Toast.makeText(view.getContext(),"Player Added. Name is " + name + " Position is " + position + " Goals = " + goals,Toast.LENGTH_SHORT).show();
}
public void viewData(View view) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
}
Additionally PlayerDao.java has been changed to not fail if the player name is duplicated and to also return useful values when inserting, deleting or updating. it is :-
#Dao
public abstract class PlayerDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract long insert (Player player);
#Delete
public abstract int delete (Player player);
#Update
public abstract int update (Player player);
#Query("select * from Player")
public abstract List<Player> getAll();
}

First of all, it would be better if you use autoGenerate = true in your Player.java class for id:
#Entity
public class Player {
#PrimaryKey(autoGenerate = true)
private int id;
By doing this you don't have to give your players ids and Room does the job for you.
And for the app crash when saving, you must check the log and see what is causing the app to crash. Update your question with that so users can help you.

Related

How to add & update data in SqliteDatabase by using RecyclerView Adapter

I am just trying to make a list by using RecyclerView where each of the list Item will contain a checkbox button. So when user will click on checkbox, it will replace the value of a Table column such as column country get value "A", on the other hand, uncheck will replace the country column value from "A" to "B" or anything.
Can anyone please help me with this? any suggestion regarding this and other similar ways to add data in SQLite database by using Recyclable will be highly a lot helpful.
Below I have added my code for your reference and Thanks in advance.
My DatabaseHelper Class
package com.hfad.ressql;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.hfad.ressql.DatabaseContractor.*;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "workers.db";
private static final int DATABASE_VERSION =7;
SQLiteDatabase db;
public DatabaseHelper( Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
this.db=db;
final String SQL_CREATE_TBALE="CREATE TABLE " + EmployeeDetails.TABLE_NAME + "(" + EmployeeDetails._ID +" INTEGER PRIMARY KEY AUTOINCREMENT, "+
EmployeeDetails.COLUMN_FIRSTNAME+" TEXT, "+EmployeeDetails.COLUMN_LASTNAME+" TEXT, "+EmployeeDetails.COLUMN_COUNTRY+" TEXT)";
db.execSQL(SQL_CREATE_TBALE);
fillquestion();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + EmployeeDetails.TABLE_NAME);
onCreate(db);
}
public void fillquestion(){
DataModel o4 = new DataModel("Earth","Soil","B");
IntertData(o4);
DataModel o5 = new DataModel("Sun","Light","B");
IntertData(o5);
DataModel o6 = new DataModel("Moon","Rock","B");
IntertData(o6);
}
public void IntertData (DataModel data){
ContentValues contentValues = new ContentValues();
contentValues.put(EmployeeDetails.COLUMN_FIRSTNAME, data.getFirstName());
contentValues.put(EmployeeDetails.COLUMN_LASTNAME, data.getLastName());
contentValues.put(EmployeeDetails.COLUMN_COUNTRY, data.country);
db.insert(EmployeeDetails.TABLE_NAME,null,contentValues);
}
public List<DataModel> object1() {
ArrayList<DataModel> details = new ArrayList<DataModel>();
db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + EmployeeDetails.TABLE_NAME, null );
if (cursor.moveToFirst()) {
do {
DataModel object2 = new DataModel();
object2.setFirstName(cursor.getString(cursor.getColumnIndex(EmployeeDetails.COLUMN_FIRSTNAME)));
object2.setLastName(cursor.getString(cursor.getColumnIndex(EmployeeDetails.COLUMN_LASTNAME)));
object2.setCountry(cursor.getString(cursor.getColumnIndex(EmployeeDetails.COLUMN_COUNTRY)));
details.add(object2);
} while (cursor.moveToNext());
}
cursor.close();
return details;
}
}
Here is my DataModel Class
package com.hfad.ressql;
public class DataModel {
public String FirstName;
public String LastName;
public String country;
public DataModel() {
}
public DataModel(String firstName, String lastName, String country) {
this.FirstName = firstName;
this.LastName = lastName;
this.country = country;
}
public String getFirstName() {
return FirstName;
}
public void setFirstName(String firstName) {
FirstName = firstName;
}
public String getLastName() {
return LastName;
}
public void setLastName(String lastName) {
LastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
Database Constructor
public final class DatabaseContractor {
private DatabaseContractor (){}
public static class EmployeeDetails implements BaseColumns {
public static final String TABLE_NAME="employy";
public static final String COLUMN_FIRSTNAME="First_Name";
public static final String COLUMN_LASTNAME="Last_Name";
public static final String COLUMN_COUNTRY="Country";
public static final String COLUMN_FAVO="mfav";
}
}
Recycler Adapter
Here I am struggling hard to sort it out. All I need is, if I click on check box, one pre-given value will be updated in the database, at the same time check box will be checked until user uncheck it. And when user will uncheck it, database will replace previous value with a new value. Actually, I have just trying to have values in a table column, so that I can use it as a favorite or bookmark list.
public class RecycAdapter extends
RecyclerView.Adapter<RecycAdapter.ViewHolder> {
List<DataModel> dotamodeldataArraylist;
Context context;
SQLiteDatabase db;
DatabaseHelper helper;
ContentValues contentValues;
Cursor cursor;
public RecycAdapter(List<DataModel> dotamodeldataArraylist,Context context) {
this.dotamodeldataArraylist=dotamodeldataArraylist;
this.context=context;
}
#Override
public ViewHolder onCreateViewHolder( ViewGroup parent, int ViewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemlist,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final RecycAdapter.ViewHolder holder, final int position) {
DataModel obj3= dotamodeldataArraylist.get(position);
holder.Fnam.setText(obj3.getFirstName());
holder.Lname.setText(obj3.getLastName());
holder.Country.setText(obj3.getCountry());
holder.fav.();
holder.fav.setChecked(fav);
final int currentPosition = position;
final boolean fav = 0==0;
holder.fav.setChecked(fav);
final int currentPosition = position;
holder.fav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(holder.fav.isChecked()){
try {
contentValues = new ContentValues();
contentValues.put(DatabaseContractor.EmployeeDetails.COLUMN_COUNTRY, "B");
db.update("DRINK", contentValues, "id_=?", new String[]{Integer.toString(currentPosition)});
} catch (SQLException e){
Toast.makeText(context,"error" + position , Toast.LENGTH_LONG).show();
}
Toast.makeText(context,"checked " + position , Toast.LENGTH_LONG).show();
} if(!holder.fav.isChecked()){
Toast.makeText(context,"not checked" + position , Toast.LENGTH_LONG).show();
}
}
});
}
#Override
public int getItemCount() {
return dotamodeldataArraylist.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView Fnam,Lname,Country;
CheckBox fav;
RelativeLayout relativeLayout;
public ViewHolder(View itemView) {
super(itemView);
Fnam = itemView.findViewById(R.id.name1);
Lname = itemView.findViewById(R.id.city1);
Country = itemView.findViewById(R.id.country1);
fav=itemView.findViewById(R.id.chk);
relativeLayout = (RelativeLayout) itemView.findViewById(R.id.layout);
}
}
}
View Class
view all class
public class Viewall extends AppCompatActivity {
RecyclerView recyclerView;
DatabaseHelper databaseHelper;
RecycAdapter recycAdapter;
List<DataModel> dotamodeldataArraylist;
Context context;
Button show;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewall);
show = findViewById(R.id.view);
recyclerView=findViewById(R.id.recycle);
databaseHelper =new DatabaseHelper(this);
dotamodeldataArraylist = new ArrayList<DataModel>();
dotamodeldataArraylist=databaseHelper.object1();
recycAdapter =new RecycAdapter(dotamodeldataArraylist,this);
RecyclerView.LayoutManager reLayoutManager =new
LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(reLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(recycAdapter);
I believe the following will do as you wish.
The were quite a few issues with code. One of the major issues is that you expected the position to correlate with the id (aka your _id column).
The position of the first item in the list is 0, unless you force/specifically set the value of 0, an alias of the rowid column (your _id column is an alias of the rowid column), the first value assigned will be 1, then likely 2, then likely 3 ...........
So at best position will be 1 less than the id.
If a row is deleted, other than the last row then position will be one less except up until the deleted row is passed and then position will be 2 less than the rowid. More deletions and an even more complex correlation between position and id. I guess somebody could come up with a fool proof conversion BUT the simpe way is to ensure that the DataModel has the vale of the respective _id column.
As such DataModel.java should be changed to include a member/variable for the id therefore the following was used :-
public class DataModel {
public String FirstName;
public String LastName;
public String country;
public long id; //<<<<<<<<<< ADDED also added gettter and setter
public DataModel() {
}
public DataModel(String firstName, String lastName, String country) {
this(firstName,lastName,country,-1);
}
//<<<<<<<<<< ADDED so ID can be set
public DataModel(String firstName, String lastName, String country, long id) {
this.FirstName = firstName;
this.LastName = lastName;
this.country = country;
this.id = id;
}
public String getFirstName() {
return FirstName;
}
public void setFirstName(String firstName) {
FirstName = firstName;
}
public String getLastName() {
return LastName;
}
public void setLastName(String lastName) {
LastName = lastName;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
}
see comments for changes
As you need to extract the id from the database, the object1 method was changed in DatabaseHelper.java (a few other changes have also been made) the following was used :-
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "workers.db";
private static final int DATABASE_VERSION =7;
SQLiteDatabase db;
public DatabaseHelper( Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
db = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
this.db=db; //<<<<<<< WRONG PLACE as onCreate only ever runs when there is no database
final String SQL_CREATE_TBALE="CREATE TABLE " + DatabaseContractor.EmployeeDetails.TABLE_NAME + "(" + DatabaseContractor.EmployeeDetails._ID +" INTEGER PRIMARY KEY AUTOINCREMENT, "+
DatabaseContractor.EmployeeDetails.COLUMN_FIRSTNAME+" TEXT, "+ DatabaseContractor.EmployeeDetails.COLUMN_LASTNAME+" TEXT, "+ DatabaseContractor.EmployeeDetails.COLUMN_COUNTRY+" TEXT)";
db.execSQL(SQL_CREATE_TBALE);
fillquestion();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + DatabaseContractor.EmployeeDetails.TABLE_NAME);
onCreate(db);
}
public void fillquestion(){
IntertData(new DataModel("Earth","Soil","B"));
IntertData(new DataModel("Sun","Light","B"));
IntertData(new DataModel("Moon","Rock","B"));
}
public void IntertData (DataModel data){
ContentValues contentValues = new ContentValues();
contentValues.put(DatabaseContractor.EmployeeDetails.COLUMN_FIRSTNAME,data.getFirstName());
contentValues.put(DatabaseContractor.EmployeeDetails.COLUMN_LASTNAME,data.getLastName());
contentValues.put(DatabaseContractor.EmployeeDetails.COLUMN_COUNTRY,data.country);
db.insert(DatabaseContractor.EmployeeDetails.TABLE_NAME,null,contentValues);
}
public List<DataModel> object1() {
ArrayList<DataModel> details = new ArrayList<>();
//db = getReadableDatabase(); db has already been set when database was instantiated/constructed
Cursor cursor = db.rawQuery("SELECT * FROM " + DatabaseContractor.EmployeeDetails.TABLE_NAME, null );
while (cursor.moveToNext()) {
details.add(new DataModel(
cursor.getString(cursor.getColumnIndex(DatabaseContractor.EmployeeDetails.COLUMN_FIRSTNAME)),
cursor.getString(cursor.getColumnIndex(DatabaseContractor.EmployeeDetails.COLUMN_LASTNAME)),
cursor.getString(cursor.getColumnIndex(DatabaseContractor.EmployeeDetails.COLUMN_COUNTRY)),
cursor.getLong(cursor.getColumnIndex(DatabaseContractor.EmployeeDetails._ID)) //<<<<<<<<< Added so id is available
));
}
cursor.close();
return details;
}
}
Pretty extensive changes were made to RecycAdapter.java, the following was used :-
public class RecycAdapter extends RecyclerView.Adapter<RecycAdapter.ViewHolder> {
List<DataModel> dotamodeldataArraylist;
Context context;
SQLiteDatabase db;
DatabaseHelper helper;
ContentValues contentValues;
public RecycAdapter(List<DataModel> dotamodeldataArraylist,Context context) {
this.dotamodeldataArraylist=dotamodeldataArraylist;
this.context=context;
helper = new DatabaseHelper(context);
db = helper.getWritableDatabase();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int ViewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemlist,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final RecycAdapter.ViewHolder holder, final int position) {
//DataModel obj3= dotamodeldataArraylist.get(position); //<<<<<<<<<< NOT NEEDED
holder.Fnam.setText(dotamodeldataArraylist.get(position).getFirstName());
holder.Lname.setText(dotamodeldataArraylist.get(position).getLastName());
holder.Country.setText(dotamodeldataArraylist.get(position).getCountry());
holder.fav.setChecked(false); //<<<<<<<<< not stored so initially set to false
holder.fav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String newcountry = "B";
if(holder.fav.isChecked()){
if (dotamodeldataArraylist.get(position).getCountry().equals("B")) {
newcountry = "A";
}
contentValues = new ContentValues();
contentValues.put(DatabaseContractor.EmployeeDetails.COLUMN_COUNTRY, newcountry);
if (db.update(
DatabaseContractor.EmployeeDetails.TABLE_NAME,
contentValues,
DatabaseContractor.EmployeeDetails._ID +"=?",
new String[]{String.valueOf(dotamodeldataArraylist.get(position).getId())}
) > 0) {
dotamodeldataArraylist.get(position).setCountry(newcountry);
notifyItemChanged(position);
Toast.makeText(context,
"checked and updated " +
position+ dotamodeldataArraylist.get(position).getFirstName() +
" ID is " + String.valueOf(dotamodeldataArraylist.get(position).getId()),
Toast.LENGTH_LONG
).show();
} else {
Toast.makeText(context,"error" + position , Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(context,"not checked" + position , Toast.LENGTH_LONG).show();
}
}
});
}
#Override
public int getItemCount() {
return dotamodeldataArraylist.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView Fnam,Lname,Country;
CheckBox fav;
public ViewHolder(View itemView) {
super(itemView);
Fnam = itemView.findViewById(R.id.name1);
Lname = itemView.findViewById(R.id.city1);
Country = itemView.findViewById(R.id.country1);
fav = itemView.findViewById(R.id.chk);
}
}
}
lastly a few minor changes were made to Viewall.java, the following was used :-
public class Viewall extends AppCompatActivity {
RecyclerView recyclerView;
DatabaseHelper databaseHelper;
RecycAdapter recycAdapter;
List<DataModel> dotamodeldataArraylist;
Button show;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewall);
show = findViewById(R.id.view);
recyclerView = findViewById(R.id.recycle);
databaseHelper = new DatabaseHelper(this);
dotamodeldataArraylist = databaseHelper.object1();
recycAdapter = new RecycAdapter(dotamodeldataArraylist, this);
RecyclerView.LayoutManager reLayoutManager = new
LinearLayoutManager(this);
recyclerView.setLayoutManager(reLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(recycAdapter);
}
}
Result
Note the layout(s) may be different, but your's should probably work and alter the presentation accordingly
When first run :-
After clicking the checkbox for Sun
Click again and back to Country B and so on.
Note the check box isn't flipped, that's a bit of an issue as to correctly display the changed data (country) notifyItemChanged is used, which will reprocess the list and thus set the checkbox to false. You'd need to store the checkbox value somewhere (in short you should really use checkboxes in this way).
Closing the app and restarting maintains the changes made, thus confirming that the changes to the database have been made.
On top of #MiKe's code, i have just made some changes in my RecyclerAdapter inside onClickListener to save checkbox Status and added isChecked as a boolean value inside dataModel class. now its working perfectly.
holder.chkbox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Values = new ContentValues();
Values.put(DatabaseContractor.EmployeeDetails.COLUMN_FAVORITE,holder.chkbox.isChecked());
try{ db.update(DatabaseContractor.EmployeeDetails.TABLE_NAME,
Values,
DatabaseContractor.EmployeeDetails._ID + "=?",
new String[]{String.valueOf(dotamodeldataArraylist.get(position).getId())});
} catch (SQLException e){
Toast.makeText(context,"Error"+position,Toast.LENGTH_LONG).show();
}
}
});
Thanks again mike for your awesome guideline. Now, i can directly save checkbox status in sqlite Database.

Populate Room Database with Arraylist

I would like to build a Deckbuilder that allows you to save created decks locally on the device.
The Decklist are stored in Arraylists, called TransferDeck. Which I would like to store in room database. My issue is, that I do not know how to populate my database correctly, with the data comming out of the Arraylist.
I am used to working with Arraylist and below you see my try for storing the data:
So this is what I tried but what sadly does not work:
private void populateDB(final List<TransferDeck> mTransferDeck) {
new Thread(new Runnable() {
#Override
public void run() {
List<SaveDeck> mSaveDeck = new ArrayList<>();
for(int i = 0; i<mTransferDeck.size(); i++){
mSaveDeck.add(new SaveDeck(i, "FirstSavedDeck", mTransferDeck.get(i).getCardImage() ,mTransferDeck.get(i).getTypeImage(), mTransferDeck.get(i).getCost(), mTransferDeck.get(i).getName(), mTransferDeck.get(i).getNumber()));
}
mSavedDecksDB.deckBuilderDao().insertCards(mSaveDeck);
}
}).start();
}
Below you can find the rest of my code, but the above one should be enough to make clear what I want to do...
I created the class SaveDeck which should be able to Save a Deck with a given Deckname:
:-
#Entity
public class SaveDeck implements Serializable {
#PrimaryKey(autoGenerate = true)
private int _id;
public SaveDeck(int _id, String deckName, int cardImage, int typeImage, Integer cardCost, String cardName, Integer cardNumber) {
this._id = _id;
DeckName = deckName;
CardImage = cardImage;
TypeImage = typeImage;
CardCost = cardCost;
CardName = cardName;
CardNumber = cardNumber;
}
#ColumnInfo(name = "DeckName")
private String DeckName;
#ColumnInfo(name = "CardImage")
private int CardImage;
#ColumnInfo(name = "TypeImage")
private int TypeImage;
#ColumnInfo(name = "CardCost")
private Integer CardCost;
#ColumnInfo(name = "CardName")
private String CardName;
#ColumnInfo(name = "CardNumber")
private Integer CardNumber;
}
I created the Dao Class as follows:
:-
#Dao
public interface DeckBuilderDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
public long[] insertCards(SaveDeck... saveDecks);
#Insert(onConflict = OnConflictStrategy.IGNORE)
public long insertCard(SaveDeck saveDecks);
#Update
public int updateCardBaseEntries(SaveDeck... saveDecks);
#Update
public int updateCardBaseEntry(SaveDeck saveDecks);
#Delete
public int deleteCardBaseEntried(SaveDeck... saveDecks);
#Delete
public int deleteCardBaseEntry(SaveDeck saveDecks);
#Query("SELECT * FROM SaveDeck")
public SaveDeck[] getAllDecks();
//probably I do not need the getAllDecks Query. Right now I only need the following one:
#Query("SELECT * FROM SaveDeck WHERE DeckName = :NameOfDeck ORDER BY DeckName, CardName")
public SaveDeck getOneDeck(String NameOfDeck);
}
Furthermore created the DataBase Class:
#Database(entities = {SaveDeck.class}, version = 1)
public abstract class SaveDecksDataBase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
The last class is a fragment, where I try to populate my database, and in the populateDB() class is the issue
public class review_fragment extends Fragment {
private List<TransferDeck> mTransferDeck = DataHolder.getInstance().savedDecklistTransfer;
SaveDecksDataBase mSavedDecksDB;
Cursor mCursor;
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//return super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.review_fragment, container, false);
/*Introduce Cards Recycler*/
RecyclerView rvCards = view.findViewById(R.id.rv_review_cardlist);
rvCards.setLayoutManager(new GridLayoutManager(getActivity(), 5));
review_RViewAdapter_Cards adapterCards = new review_RViewAdapter_Cards(getContext(), mTransferDeck);
rvCards.setAdapter(adapterCards);
/*Init Room database*/
mSavedDecksDB = Room.databaseBuilder(getActivity(),SaveDecksDataBase.class,"SavedDecksDB.db").build();
populateDB(mTransferDeck);
return view;
}
private void populateDB(final List<TransferDeck> mTransferDeck) {
new Thread(new Runnable() {
#Override
public void run() {
List<SaveDeck> mSaveDeck = new ArrayList<>();
for(int i = 0; i<mTransferDeck.size(); i++){
mSaveDeck.add(new SaveDeck(i, "FirstSavedDeck", mTransferDeck.get(i).getCardImage() ,mTransferDeck.get(i).getTypeImage(), mTransferDeck.get(i).getCost(), mTransferDeck.get(i).getName(), mTransferDeck.get(i).getNumber()));
}
mSavedDecksDB.deckBuilderDao().insertCards(mSaveDeck);
}
}).start();
}
}
I like to mention that this should be a comment rather than an answer.
First, either use AysncTask or use more robust Executors.newSingleThreadExecutor(). If you prefer the second one then it's best if you create a helper class (example). Example:
private void populateDB(final List<TransferDeck> mTransferDeck) {
AppExecutors.diskIO().execute(() -> {
for(int i = 0; i<mTransferDeck.size(); i++){
mSavedDecksDB.deckBuilderDao().insertCards(new SaveDeck(...);
}
});
}
(1) Create a blank constructor.
(4) Room Database should not be initialized there and it's best if it's singleton. So the your database class (3) can be like:
public abstract class SaveDecksDataBase extends RoomDatabase {
private static SaveDecksDataBase sINSTANCE;
private static final Object LOCK = new Object();
public static SaveDecksDataBase getDatabase(final Context context) {
if (sINSTANCE == null) {
synchronized (LOCK) {
if (sINSTANCE == null) {
sINSTANCE = Room.databaseBuilder(context.getApplicationContext(),
SaveDecksDataBase.class, "SavedDecksDB.db")
.build();
}
}
}
return sINSTANCE;
}
public abstract DeckBuilderDao deckBuilderDao();
}
Lastly, to get SaveDeck object you also has to use Executors or AsyncTask to do the work in background, and then populate the RecyclerView.
Android Room Database
Practice set

Unable to retrieve firestore database using recyclerView

I am using recyclerView and Adapter to fetch the data in profileActivity
here is my
public class studentDetailsRecyclerActivity extends AppCompatActivity {
//recyclerview to set the details for UI in the student profile activity
private RecyclerView mRecyclerView;
private storeDetailsAdapter mStoreDetailsAdapter;
private List<storeStudentDetails> studentDetailsList;
private FirebaseFirestore dbReference;
private ProgressBar mProgressBar;
private String TAG = studentDetailsRecyclerActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
dbReference = FirebaseFirestore.getInstance();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_details);
mProgressBar = findViewById(R.id.progressbar);
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView_products);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
studentDetailsList = new ArrayList<>();
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
//to get the "details" this is our collection from firestore so we must fetch them
//by calling the addOnSuccessListener
dbReference.collection("details").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) { //we must have to hide the progress bar when the data gets loaded
//here queryDocumentsSnapshot will hold all the "details" which is your collection in firestore
if(!queryDocumentSnapshots.isEmpty()){
//we must have to create empty list so that to store all
//details from DocumentsSnapshots
List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
//enhanced for loop because we have to give every index documentSnapShot
for(DocumentSnapshot d: list){
storeStudentDetails sd = d.toObject(storeStudentDetails.class);
studentDetailsList.add(sd);
Log.d(TAG, "onSuccess: " + sd.toString());
}
//to refresh and sync we must have to use notifyDataSetChanged
mStoreDetailsAdapter.notifyDataSetChanged();
}
}
}) .addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}
}
and here is my storeDetailsAdapter
import java.util.List;
public class storeDetailsAdapter extends RecyclerView.Adapter<storeDetailsAdapter.StudentViewHolder>{
private Context context;
private List<storeStudentDetails> studentDetailsList;
public storeDetailsAdapter(Context context, List<storeStudentDetails> studentDetailsList) {
this.context = context;
this.studentDetailsList = studentDetailsList;
}
#NonNull
#Override
public StudentViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new StudentViewHolder(
LayoutInflater.from(context).inflate(R.layout.profile_activity, parent, false)
);
}
#Override
public void onBindViewHolder(#NonNull StudentViewHolder holder, int position) {
storeStudentDetails mStoreDetails = studentDetailsList.get(position);
holder.studName.setText(mStoreDetails.getStudentName());
holder.rollNum.setText(mStoreDetails.getRollNo());
holder.bookName.setText( mStoreDetails.getBook());
holder.fine.setText("Fine:" + mStoreDetails.getFine());
holder.dept.setText(mStoreDetails.getDept());
}
#Override
public int getItemCount() {
return studentDetailsList.size();
}
class StudentViewHolder extends RecyclerView.ViewHolder {
TextView studName,rollNum,bookName,dept,fine;
public StudentViewHolder(View itemView) {
super(itemView);
studName=itemView.findViewById(R.id.studentName_prof);
rollNum = itemView.findViewById(R.id.rollNumber_prof);
bookName = itemView.findViewById(R.id.bookName_prof);
fine = itemView.findViewById(R.id.fineAmt_prof);
dept = itemView.findViewById(R.id.department_prof);
}
}
}
and here is my StoreStudentDetails class:
public class storeStudentDetails implements Serializable {
private String studentName;
private String rollNo;
private String book;
private Double fine;
private String dept;
#Exclude private String id;
public storeStudentDetails() {
}
public storeStudentDetails(String studentName, String rollNo,String book, double fine ,String dept) {
this.studentName = studentName;
this.rollNo = rollNo;
this.book = book;
this.fine = fine;
this.dept = dept;
}
public void setId(String id) {
this.id = id;
}
public String getStudentName() {
return studentName;
}
public String getRollNo() {
return rollNo;
}
public String getBook() {
return book;
}
public Double getFine() {
return fine;
}
public String getDept() {
return dept;
}
public String getId() {
return id;
}
}
To solve this, please move the following lines of code:
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
Right before the following line of code:
mStoreDetailsAdapter.notifyDataSetChanged();
And this is because onSuccess() method has an asynchronous behavior and by the time you are setting the adapter outside the callback your list is empty.
As you can see, the easiest solution for this problem is to move those lines of code inside the callback. but if you want to use the value of your studentDetailsList outside the onSuccess() method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Pass an object in android mvp?

recently I been reading about the mvp pattern on Internet and to be honest, it has been a little bit confusing.
I'm trying to re-write an app using the mvp pattern but I reached a point using AsyncTask where I can't figure out how to pass the Object in the Interactor back to the View.
Here'e the code:
View:
public class DetailsActivity extends BaseActivity {
private DetailsPresenter presenter;
private Pet pet;
private TextView name, description, breed, lostAt, age;
private int idList;
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.details_layout);
DetailsPresenter presenter = new DetailsPresenter();
Typeface font0 = Typeface.createFromAsset(getAssets(), "fonts/CaviarDreams.ttf");
Typeface font1 = Typeface.createFromAsset(getAssets(), "fonts/CaviarDreams_Bold.ttf");
TextView txtDescription = findViewById(R.id.textView8);
TextView txt1 = findViewById(R.id.textView6); //Age, Type
TextView txt2 = findViewById(R.id.textView9); //LostAt
Button btnContact = findViewById(R.id.button6);
description = findViewById(R.id.details);
menuRes = R.menu.details_menu;
name = findViewById(R.id.textView10);
breed = findViewById(R.id.breed);
lostAt = findViewById(R.id.lostAt);
age = findViewById(R.id.age);
name.setTypeface(font1);
breed.setTypeface(font0);
description.setTypeface(font0);
lostAt.setTypeface(font0);
txtDescription.setTypeface(font1);
txt2.setTypeface(font1);
txt1.setTypeface(font1);
btnContact.setTypeface(font0);
idList = getIntent().getExtras().getInt("TAG_LIST");
id = getIntent().getExtras().getInt("TAG_ID");
if(idList == 0){
txt1.setText(R.string.type);
txt2.setText(R.string.txt2);
} else{
txt1.setText(R.string.txt0);
txt2.setText(null);
}
btnContact.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), ChatFragment.class);
startActivity(intent);
}
});
// Get pet details from database on background
pet = presenter.getPet(idList, id);
}
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
if(idList == 0){
name.setText(pet.getName());
breed.setText("(" + pet.getBreed() + ")");
description.setText(pet.getDescription());
lostAt.setText(pet.getLocation());
}else{
name.setText(pet.getBreed());
description.setText(pet.getDescription());
lostAt.setText(pet.getGender());
age.setText(pet.getAge());
}
}
}
Presenter:
public class DetailsPresenter {
private DetailsInteractor interactor;
public Pet getPet(int idList, int id) {
interactor = new DetailsInteractor(idList, id);
interactor.execute();
return interactor.pet;
}
}
Interactor:
public class DetailsInteractor extends AsyncTask<String, String, Pet> {
public Pet pet;
private int idList;
private int id;
private DBAction database;
public DetailsInteractor (int idList, int id) {
this.idList = idList;
this.id = id;
database = new DBAction();
}
#Override
protected Pet doInBackground(String... strings) {
pet = database.requestItem(idList, id);
return pet;
}
I need that after getting the data from the database, it updates the View, using object Pet.
Any answers and suggestions will be welcomed, Thanks!
When You create/initialize a presenter in Activity(This is your view) you should pass the view to the presenter. Something like this.
DetailsPresenter presenter = new DetailsPresenter(View view);
View can be any object with which you can update the UI or can call the methods in the activity.
Moreover, you have to go through few more good site to learn about MVP.
http://valokafor.com/learn-android-mvp-pattern-example/ this is really nice one.

How can I restore parcelable list in android

I have this app that in an activity creates a list of players and displays them in a listview. When I create a player I use the populatePlayerList method and it works fine. But when I save the list in a parcel and try to restore it, it doesn't populate the listview. Can't figure out why this happens. Below is some code:
public class PlayerManager extends AppCompatActivity {
EditText playerName, playerNumber, playerPosition;
ArrayList<Player> players = new ArrayList<Player>();
ListView playerListView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player_manager);
playerName = (EditText) findViewById(R.id.etPlayerName);
playerNumber = (EditText) findViewById(R.id.etPlayerNumber);
playerPosition = (EditText) findViewById(R.id.etPlayerPosition);
playerListView = (ListView) findViewById(R.id.playerList);
if(savedInstanceState!=null){
players = savedInstanceState.getParcelableArrayList("savedList");
populatePlayerList();
}
final Button addPlayerBtn = (Button) findViewById(R.id.bAddPlayer);
addPlayerBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addPlayer(playerName.getText().toString(), playerNumber.getText().toString(), playerPosition.getText().toString());
populatePlayerList();
Toast.makeText(getApplicationContext(), playerName.getText().toString() + " added!", Toast.LENGTH_SHORT).show();
}
});
private void populatePlayerList(){
ArrayAdapter<Player> adapter = new PlayerListAdapter();
playerListView.setAdapter(adapter);
}
private void addPlayer(String name, String number, String position){
players.add(new Player(name, number, position));
}
private class PlayerListAdapter extends ArrayAdapter<Player> {
public PlayerListAdapter(){
super(PlayerManager.this, R.layout.player_list, players);
}
#Override
public View getView(int position, View view, ViewGroup parent){
if (view == null)
view = getLayoutInflater().inflate(R.layout.player_list, parent, false);
Player currentPlayer = players.get(position);
TextView name = (TextView) view.findViewById(R.id.playerName);
name.setText(currentPlayer.get_name());
TextView number = (TextView) view.findViewById(R.id.playerNumber);
number.setText(currentPlayer.get_number());
TextView playerPosition = (TextView) view.findViewById(R.id.playerPosition);
playerPosition.setText(currentPlayer.get_position());
return view;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("savedList", players);
}
Here is my Player Class:
public class Player implements Parcelable{
private String name, position,number;
public Player (String name, String number, String position) {
this.name = name;
this.number = number;
this.position = position;
}
protected Player(Parcel in) {
name = in.readString();
position = in.readString();
number = in.readString();
}
public static final Parcelable.Creator<Player> CREATOR = new Parcelable.Creator<Player>() {
#Override
public Player createFromParcel(Parcel in) {
return new Player(in);
}
#Override
public Player[] newArray(int size) {
return new Player[size];
}
};
public String get_name() {
return name;
}
public String get_position() {
return position;
}
public String get_number(){
return number;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(position);
dest.writeString(number);
}
}
I checked the players list when i try to restore it and its empty. So i guess its never saved in outState. Any ideas why?
I fixed my problem by adding android:launchMode="singleInstance" in the PlayerManager activity in Android Manifest.xml file. And I override the onBackPressed to sent me back to a previous activity so the onDestroy method isn't called. That way I get to keep the same instance in the PlayerManager activity.

Categories

Resources