everybody! Today I was trying to solve the next problem: I've created room database for List of languages, prefill it with five ready object for different one's and then I was trying to transfer them into spinner adapter something like that:
Entity and DAO code for the Language object:
#Entity
public class Language {
#PrimaryKey
private long id;
#ColumnInfo(name = "language")
private String language;
public Language(String language) {
this.language = language;
}
public static Language[] populateData() {
return new Language[]{new Language("English"), new Language("French"), new Language(
"Spanish"), new Language("Russian"), new Language("Italian")};
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Dao
public interface LanguageDao {
#Query("SELECT * FROM language")
List<Language> getAll();
#Insert
void insertAll(Language... languages);
}
Further I created database object with Singleton in the AppDatabase class like that:
#Database(entities = {Language.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract LanguageDao languageDao();
public synchronized static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = buildDatabase(context);
}
return INSTANCE;
}
private static AppDatabase buildDatabase(final Context context) {
return Room.databaseBuilder(context, AppDatabase.class, "my-database")
.addCallback(new Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Executors.newSingleThreadScheduledExecutor().execute(new Runnable() {
#Override
public void run() {
getInstance(context).languageDao()
.insertAll(Language.populateData());
}
});
}
})
.allowMainThreadQueries()
.build();
}
}
As you can see I've inserted prefill data of language objects into the instance of Database. I know that's allowMainThreadQueries() method is not recommended here (just use it to simplify current training).
Further, I've created the following method which returns spinner object and put it into activity code:
private Spinner createLanguageSpinner(){
Spinner spinner = findViewById(R.id.language_spinner);
List<Language> languages = AppDatabase.getInstance(this).languageDao().getAll();
List<String>languageStrings = new LinkedList<>();
for(int i = 0; i < languages.size(); i++){
languageStrings.add(languages.get(i).getLanguage());
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
languageStrings);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
return spinner;
}
My problem is here:
List<Language> languages = AppDatabase.getInstance(this).languageDao().getAll();
I can't fill current List with predefined objects, which is resulted into empty spinner without options to choose. Could you tell where i'm getting wrong? I also would like to hear opinions about how can I simplify the creating of adapter.
I'm a bit late to the party, but in case if someone has this question too.
The problem is that you're trying to insert new objects without the PrimaryKey id value. As the result you're getting an empty table in the db.
You either should to set id value manually, e.g.:
#Entity
public class Language {
...
public Language(long id, String language) {
this.id = id; // or create method to generate a unique id as a PrimaryKey value must be unique
this.language = language;
}
public static Language[] populateData() {
return new Language[]{
new Language(1, "English"),
new Language(2, "French"),
new Language(3, "Spanish"),
new Language(4, "Russian"),
new Language(5, "Italian")
};
}
}
Or use the autoGenerate property of the PrimaryKey to let SQLite generate the unique id:
#PrimaryKey(autoGenerate = true)
private long id;
See the reference for more information.
No other changes in your code are required.
For the second question:
I also would like to hear opinions about how can I simplify the
creating of adapter
You can create a custom adapter for your spinner and pass a List<Language> to it directly:
public class MyAdapter extends BaseAdapter implements SpinnerAdapter {
private LayoutInflater mInflater;
private List<Language> mItems;
public MyAdapter(Context context, List<Language> items) {
mInflater = LayoutInflater.from(context);
mItems = items;
}
#Override
public int getCount() {
return mItems.size();
}
#Override
public Object getItem(int position) {
return mItems.get(position);
}
#Override
public long getItemId(int position) {
return mItems.get(position).getId();
}
// This is for the default ("idle") state of the spinner.
// You can use a custom layout or use the default one.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = mInflater.inflate(R.layout.spinner_item, parent, false);
}
Language item = (Language) getItem(position);
TextView textView = view.findViewById(R.id.text);
textView.setText(item.getTitle());
return view;
}
// Drop down item view as stated in the method name.
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = mInflater.inflate(R.layout.spinner_dropdown_item, parent, false);
}
Language item = (Language) getItem(position);
TextView textView = view.findViewById(R.id.text);
textView.setText(item.getTitle());
return view;
}
}
In your Activity:
List<Language> languages = AppDatabase.getInstance(this).languageDao().getAll();
Spinner spinner = findViewById(R.id.spinner);
MyAdapter myAdapter = new MyAdapter(this, languages);
spinner.setAdapter(myAdapter);
See BaseAdapter and SpinnerAdapter reference.
Or you can use ArrayAdapter and simply override toString method of your object to determine what text will be displayed for the item in the list (reference):
#Entity
public class Language {
...
#NonNull
#Override
public String toString() {
// A value you want to be displayed in the spinner item.
return language;
}
}
and in your Activity:
List<Language> languages = AppDatabase.getInstance(this).languageDao().getAll();
Spinner spinner = findViewById(R.id.spinner);
// Pass your list as the third parameter. No need to convert it to List<String>
ArrayAdapter<Language> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, languages);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
Related
Can't get where is my mistake - it seems data is inserted (I checked the database file through Device File Explorer) but it doesn't returns.
I wonder whether it's in Adapter or ViewHolder or anywhere else.
Any help is granted!
This the activity where I perform my queries
public class ShowDatabaseActivity extends AppCompatActivity {
private List <Contact> contactsList = new ArrayList<>()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_database);
setupToolbar();
initRecyclerView();
Intent intent = getIntent();
unpack(intent);
}
private void setupToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(v -> onBackPressed());
}
}
private void initRecyclerView() {
RecyclerView recyclerView = findViewById(R.id.recycler_view);
final ContactsListAdapter adapter = new ContactsListAdapter(contactsList);
adapter.notifyDataSetChanged();
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
}
private void unpack(Intent intent) {
final Handler handler = new Handler();
Thread backgroundThread = new Thread(() -> {
Bundle extras = intent.getExtras();
String lastName = extras.getString(Constants.LAST_NAME_KEY);
String firstName = extras.getString(Constants.FIRST_NAME_KEY);
String middleName = extras.getString(Constants.MIDDLE_NAME_KEY);
int age = extras.getInt(Constants.AGE_KEY);
Contact contact = new Contact(lastName, firstName, middleName, age);
AppDatabase.getINSTANCE(ShowDatabaseActivity.this).contactDao().insert(contact);
AppDatabase.getINSTANCE(ShowDatabaseActivity.this).contactDao().getAll();
handler.post(() -> {
});
});
backgroundThread.start();
}
}
If I debug this line shows data sucessfully -
(ShowDatabaseActivity.this).contactDao().insert(contact);
My adapter
public class ContactsListAdapter extends RecyclerView.Adapter<ContactsListAdapter.ContactViewHolder> {
private Context context;
private List<Contact> contacts;
public ContactsListAdapter(#NonNull List<Contact> contacts) {
this.contacts = contacts;
}
#Override
public ContactViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.recycler_view, parent, false);
return new ContactViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ContactViewHolder holder, int position) {
Contact currentContact = contacts.get(position);
if (currentContact!=null) {
holder.contactLastNameView.setText(currentContact.getLastName());
holder.contactFirstNameView.setText(currentContact.getFirstName());
holder.contactMiddleNameView.setText(currentContact.getMiddleName());
holder.contactAgeView.setText(Integer.toString(currentContact.getAge()));
}
else {
holder.contactLastNameView.setText("No information");
holder.contactFirstNameView.setText("No information");
holder.contactMiddleNameView.setText("No information");
holder.contactAgeView.setText("No information");
}
}
#Override
public int getItemCount() {
return contacts.size();
}
class ContactViewHolder extends RecyclerView.ViewHolder {
private final TextView contactLastNameView;
private final TextView contactFirstNameView;
private final TextView contactMiddleNameView;
private final TextView contactAgeView;
private ContactViewHolder(View itemView) {
super(itemView);
contactLastNameView = itemView.findViewById(R.id.last_name_text_view);
contactFirstNameView = itemView.findViewById(R.id.first_name_text_view);
contactMiddleNameView = itemView.findViewById(R.id.middle_name_text_view);
contactAgeView = itemView.findViewById(R.id.age_text_view);
}
}
#Override
public int getItemViewType(final int position) {
return R.layout.recycler_view;
}
}
My DataBase
#Database(entities = {Contact.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract ContactDao contactDao();
private List<Contact> allContacts;
List<Contact> getAllContacts() {
return allContacts;
}
private static AppDatabase INSTANCE;
public synchronized static AppDatabase getINSTANCE(Context context) {
INSTANCE = getDatabase(context);
return INSTANCE;
}
private static AppDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "table_contacts")
.build();
Log.d("LOG", "Getting the database instance");
}
}
return INSTANCE;
}
}
There is no doubt in my Entity and Dao classes as it worked perfectly with other variant of database so I don't attach them.
Will be very grateful for any help!
Your code is incomplete
You are setting only empty Array inside your adapter, to rectify this make your Adapter's object global
Your handler.post have nothing inside it
you need to create a function inside you adapter like below
public void addItem(List<Contacts> list) {
mList.add(list)
notifyItemInserted(mList.size - 1)
}
Now you need to call addItem inside your handler.post by using
adapter.addItem(contact)
This will add the content inside your adapter's list and notify the chnages also
AppDatabase.getINSTANCE(ShowDatabaseActivity.this).contactDao().getAll();
This code does not assign data to anything/ variable to hold your data. Assign data to a reference variable and pass that variable to the adapter so it can show data from it.
I have 2 view models observing 2 tables in room each emitting live data, they should update my recycler view when a value changes. My adapter is equipped to handle more than one model and view holder, but I'm not sure how to update the recycler views adapter with new data without overwriting the current data or duplicating any data any ideas?
So my adapter takes a list of Visitable (Visitable pattern)
I have 2 objects that implement this interface, the interface has a type so I can tell what view holder it wants and I update the recycler view using diff utils, it look like this
public class CardAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private final List<Visitable> elements;
private final TypeFactory typeFactory;
private final ItemTouchListener onItemTouchListener;
private final Context context;
private String cardType;
private final String layoutIdentifier;
private static final String TAG = "Adptr-Card";
private String CARD_CLICK_UPDATE = "card_click_update";
private final String[] imageFilePathNames;
private RequestManager glide;
public CardAdapter(List<Visitable> elements, TypeFactory typeFactory, ItemTouchListener onItemTouchListener,
Context context,
String cardType, String layoutIdentifier, RequestManager glide) {
this.glide = glide;
this.elements = elements;
this.typeFactory = typeFactory;
this.onItemTouchListener = onItemTouchListener;
this.context = context;
this.cardType = cardType;
this.layoutIdentifier = layoutIdentifier;
this.imageFilePathNames = context.getResources().getStringArray(R.array.image_set_names);
}
#NonNull
#Override
public BaseViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View contactView = LayoutInflater.from(context).inflate(viewType, parent, false);
return typeFactory.createViewHolder(contactView, viewType, onItemTouchListener, glide, cardType);
}
#Override
public void onBindViewHolder(#NonNull BaseViewHolder holder, int position) {
holder.bind(elements.get(position), position);
}
#Override
public int getItemViewType(int position) {
return elements.get(position).type(typeFactory);
}
public void setCardType(String cardType) {
this.cardType = cardType;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return elements.size();
}
public List<Visitable> getList() {
return elements;
}
public List<Sentence> getSentencesList() {
ArrayList<Sentence> sentences = new ArrayList<>();
for (Visitable visitable : elements) {
if (visitable.type(typeFactory) == CardViewHolder.LAYOUT) {
sentences.add((Sentence) visitable);
}
}
return sentences;
}
public Visitable getItem(int position) {
if (position > 0 && position < elements.size()) {
return elements.get(position);
}
return elements.get(0);
}
class CalculateDiffUtils extends AsyncTask<Void, Void, DiffResult> {
private final List<Visitable> oldCardList;
private final List<Visitable> newCardList;
CalculateDiffUtils(List<Visitable> oldCardList, List<Visitable> newCardList) {
this.oldCardList = oldCardList;
this.newCardList = newCardList;
}
#Override
protected DiffUtil.DiffResult doInBackground(Void... params) {
return DiffUtil.calculateDiff(new VisitableDiffUtils(oldCardList, newCardList, typeFactory));
}
#Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
super.onPostExecute(diffResult);
dispatchUpdates(diffResult, newCardList);
}
}
private void dispatchUpdates(DiffUtil.DiffResult diffResult, List<Visitable> newCardList) {
this.elements.clear();
this.elements.addAll(newCardList);
diffResult.dispatchUpdatesTo(this);
}
public void refreshDiffUtilsList(List<Visitable> sentences) {
new CalculateDiffUtils(elements, sentences).execute();
}
public void removeItem(int position) {
elements.remove(position);
notifyItemRemoved(position);
}
public void addCard(Sentence sentence) {
elements.add(getItemCount(), sentence);
notifyItemInserted(getItemCount());
}
public void addGroup(GroupsWithSentences sentence) {
elements.add(getItemCount(), sentence);
notifyItemInserted(getItemCount());
}
public void updateCardClick(int position) {
notifyItemChanged(position, CARD_CLICK_UPDATE);
}
public void refreshList(List<Visitable> newElements) {
ArrayList<Visitable> elementArrayList = new ArrayList<>(newElements);
elements.clear();
elements.addAll(elementArrayList);
notifyDataSetChanged();
}
}
My 2 view models sit in a fragment, they observe some data from my Room database and are updated when changes happen, but this means I will only ever have the data from one of the view models, I guess I want a way to combine these view models maybe using some kind of mediator live data, here are my 2 view models (I've removed stuff for brevity, they are both initiated using factories)
GROUP VIEW MODEL
public class GroupViewModel extends ViewModel {
private final GroupRepository groupRepository;
private final LiveData<List<GroupsWithSentences>> groups;
public GroupViewModel(#NonNull Application application, String[] cardArgs) {
groupRepository = new GroupRepository(application);
groups = groupRepository.getGroupsByWordDescriptionAndWordType(cardArgs[0],cardArgs[1]);
}
public LiveData<List<GroupsWithSentences>> getGroups() {
return groups;
}
}
SENTENCE VIEW MODEL
public class CardViewModel extends ViewModel {
private final SentenceRepository sentenceRepository;
private final LiveData<List<Sentence>> cards;
private static final String TAG = "view_model";
public CardViewModel(#NonNull Application application , int clicks){
sentenceRepository = new SentenceRepository(application);
search = new MutableLiveData<>();
cardArgs = new MutableLiveData<>();
cards = Transformations.switchMap(search, mySearch -> sentenceRepository.searchLiveCardListByWordTypeAndWordDescriptionAndSearchWord(getCardArgs()[0],getCardArgs()[1],mySearch));
}
public LiveData<List<Sentence>> getLiveCardList(){
return cards;
}
}
CALLING ADAPTER IN MY FRAGMENT
private void setUpCardViewModelObserver(String[] args) {
cardViewModel.getLiveCardList().observe(getViewLifecycleOwner(), sentenceList -> {
if (sentenceList != null) {
ArrayList<Visitable> list = new ArrayList<>(sentenceList);
cardAdapter.refreshDiffUtilsList(list);
checkResults(list.size());
}
});
}
private void setUpGroupViewModelObserver() {
groupViewModel.getGroups().observe(getViewLifecycleOwner(), groupsWithSentencesList -> {
if (groupsWithSentencesList != null) {
ArrayList<Visitable> list = new ArrayList<>(groupsWithSentencesList);
cardAdapter.refreshDiffUtilsList(list);
checkResults(groupsWithSentencesList.size());
}
});
}
Any help is welcome, many thanks.
So the answer was to use Mediator Live data, i set the new mediator live data to respond to changes to my existing live data objects and then mediate those changes so i now only have one stream of data so my card view model now looks like this
public CardViewModel(#NonNull Application application , int clicks, String[] cardArgs){
sentenceRepository = new SentenceRepository(application);
search = new MutableLiveData<>();
cards = Transformations.switchMap(search, mySearch -> sentenceRepository.searchLiveCardListByWordTypeAndWordDescriptionAndSearchWord(cardArgs[0],cardArgs[1],mySearch));
groupRepository = new GroupRepository(application);
groups = groupRepository.getGroupsByWordDescriptionAndWordType(cardArgs[0],cardArgs[1]);
sentencesAndGroups = new MediatorLiveData<>();
sentencesAndGroups.addSource(cards, sentences -> {
sentencesAndGroups.setValue(combineLatest(sentences, groups.getValue()));
});
sentencesAndGroups.addSource(groups, groupsWithSentences -> {
sentencesAndGroups.setValue(combineLatest(cards.getValue(), groupsWithSentences));
});
}
and my new combine latest method looks like this
private List<Visitable> combineLatest(List<Sentence> sentenceList, List<GroupsWithSentences> groupsWithSentences) {
List<Visitable> visitableList = new ArrayList<>();
if (sentenceList != null){
visitableList.addAll(sentenceList);
}
if (groupsWithSentences != null){
visitableList.addAll(groupsWithSentences);
}
return visitableList;
}
I have created an input class like 3 inputs Map: String, List, List,
and then use it in a RecyclerView.
In my constructor, I input strings in the list then clear it, so I have to get my data from the mainExampleClass
How can I access it? For instance, in the List which contains the Examples List
List<Example> exampleList;
exampleList.get(0);
how could I access the data after get(i)?
The RecylerView Data object:
public class Example {
private static MainExampleObject exampleObject;
private static String StepName;
private static List<String> TemporaryCode = new ArrayList<>(), TemporaryExplanation = new ArrayList<>();
public Example(MainExampleObject exampleObject) {
this.exampleObject = exampleObject;
}
public static void addCode(String code) {
TemporaryCode.add(code);
}
public static void addExplanation(String explanation) {
TemporaryExplanation.add(explanation);
}
public static void setStepName(String stepName) {
StepName = stepName;
}
public static MainExampleObject getExampleObject() {
return exampleObject;
}
static List<String> getTemporaryCode() {
return TemporaryCode;
}
static List<String> getTemporaryExplanation() {
return TemporaryExplanation;
}
static String getStepName() {
return StepName;
}
public static void addExample(){
exampleObject = new MainExampleObject(StepName, TemporaryCode, TemporaryExplanation);
TemporaryCode.clear();
TemporaryExplanation.clear();
}
}
The example object class:
class MainExampleObject {
private static String StepName;
private static List<String> Code, Explanation;
MainExampleObject(String stepHeader, List<String> code, List<String> explanation) {
StepName = stepHeader;
Code = code;
Explanation = explanation;
}
public static String getStepNamex() {
return StepName;
}
}
More Details
The method by which I add the data to the list
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Example.setStepName(String.valueOf(dataSnapshot.getKey()));
for (DataSnapshot childSnapshot : dataSnapshot.child("Code").getChildren()) {
Example.addCode(String.valueOf(childSnapshot.getValue()));
}
for (DataSnapshot childSnapshot : dataSnapshot.child("Explaination").getChildren()) {
Example.addExplanation(String.valueOf(childSnapshot.getValue()));
}
addExample();
exampleList.add(new Example(getExampleObject()));
adapter.notifyDataSetChanged();
}
The Adapter
List<Example> exampleList;
ViewLesson viewLesson;
public interface OnItemSelectedListenerCustom {
void onItemClicked(int selectedPosition);
}
public class ExampleHolder extends RecyclerView.ViewHolder { // here is where you define what text have value
CardView cv;
LinearLayout ll;
public ExampleHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.CV);
ll = (LinearLayout) itemView.findViewById(R.id.CV_LL);
}
}
public ExampleAdapter(ViewLesson viewLesson, List<Example> exampleList) {
this.viewLesson = viewLesson;
this.exampleList = exampleList;
}
#Override
public ExampleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_view, parent, false);
return new ExampleHolder(itemView);
}
#Override
public void onBindViewHolder(final ExampleHolder holder, int position) {
TextView tv = new TextView(holder.cv.getContext());
if (Example.getStepName() != null) {
tv.setText(Example.getStepName());
holder.ll.addView(tv);
}
if (Example.getTemporaryCode() != null && Example.getTemporaryExplanation() != null) {
int i = 0;
for (String code : Example.getTemporaryCode()) {
tv = new TextView(holder.cv.getContext());
tv.setText(code);
holder.ll.addView(tv);
tv = new TextView(holder.cv.getContext());
tv.setText(Example.getTemporaryCode().get(i));
holder.ll.addView(tv);
i++;
}
tv = new TextView(holder.cv.getContext());
tv.setText(String.valueOf(exampleList.get(0).getClass().toString()));
holder.ll.addView(tv);
tv = new TextView(holder.cv.getContext());
tv.setText(String.valueOf(exampleList.get(1).getClass().toString()));
holder.ll.addView(tv);
}
}
#Override
public int getItemCount() {
return exampleList.size();
}
}
This is the exactly line that I want to get this example of data in separately
StepName = 2 Adding b, TemporaryCode = [1aaaa, 2baaa, 3caaa], TemporaryExplanation = [1sttt, 2nddd, 3rddd]
where
the string is 2 Adding b
the 1st List is [1aaaa, 2baaa, 3caaa]
the 2nd List is [1sttt, 2nddd, 3rddd]
The Line
tv.setText(String.valueOf(exampleList.get(0).getClass().toString()));
Based on this line seems you want a String representation of all the variables in your class.
exampleList.get(0).getClass().toString()
Well, getClass() returns you a Java Class variable, and toString on a Class tells nothing about its fields.
Please see How to override toString() properly in Java? and apply it to your class after you fix whatever you did to think you needed static everywhere
If done correctly, this would work.
setText(String.valueOf(exampleList.get(0)))
newbie to Android here!
I've been learning how to implement SQLite in my app, and to sum it up, I have an Accountant class which has access to the SQLite database. The class pulls up the items from the database and puts them in an ArrayList. This ArrayList is what is used for my adapter for the recyclerView.
Whenever I add a new item in the app, the the item's data is stored in the database and the Accountant class's ArrayListgets updated with this info.
Then, the adapter calls its notifyDataSetChanged() method to update the View. This is where the problem occurs; the RecyclerView DOES display all items, but only upon app startup, NOT when a new item is added.
I've done all I can, it just LOOKS like it's supposed to work, but it doesn't and it's driving me nuts.
Here's the code
ItemAdapter Class
private class ItemAdapter extends RecyclerView.Adapter<ItemHolder> {
private List<Item> mItemList;
public ItemAdapter(List<Item> itemList) {
mItemList = itemList;
}
public ItemHolder onCreateViewHolder(ViewGroup parent, int ViewType) {
View view = getLayoutInflater().inflate(R.layout.list_item_item, parent, false);
return new ItemHolder(view);
}
public void onBindViewHolder(ItemHolder holder, int position) {
Item item = mItemList.get(position);
holder.bindItem(item);
}
public int getItemCount() {
return mItemList.size();
}
}
Accountant Class
public class Accountant {
private static Accountant sAccountant;
private double mTotalMoney;
private Context mContext;
private SQLiteDatabase mDatabase;
private List<Item> mItemList;
public static Accountant get(Context context) {
sAccountant = sAccountant == null ? new Accountant(context) : sAccountant;
return sAccountant;
}
private Accountant(Context context) {
mTotalMoney = 0;
mContext = context.getApplicationContext();
mDatabase = new ItemBaseHelper(mContext).getWritableDatabase();
mItemList = getListFromSQL();
}
private static ContentValues getContentValues(Item i) {
ContentValues values = new ContentValues();
values.put(ItemTable.cols.NAME, i.getName());
values.put(ItemTable.cols.PRICE, i.getPrice());
values.put(ItemTable.cols.COUNT, i.getCount());
return values;
}
public void addItem(Item item) {
ContentValues cv = getContentValues(item);
mDatabase.insert(ItemTable.NAME, null, cv);
mItemList = getListFromSQL();
}
public void removeItem(int i) {
}
public void addMoney(double money, boolean isSet) {
mTotalMoney += isSet ? money - mTotalMoney : money;
}
public String getTotalMoney() {
return MoneyUtils.prep(mTotalMoney);
}
public String getChange() {
double cost = 0;
for (Item item : getItemList())
cost += item.getPrice() * item.getCount();
return MoneyUtils.prep(mTotalMoney - cost);
}
public List<Item> getItemList() {
return mItemList;
}
private List<Item> getListFromSQL() {
List<Item> itemList = new ArrayList<>();
ItemCursorWrapper cursor = queryItems(null, null);
try {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
itemList.add(cursor.getItem());
cursor.moveToNext();
}
} finally {
cursor.close();
}
return itemList;
}
public ItemCursorWrapper queryItems(String whereClause, String[] whereArgs) {
Cursor cursor = mDatabase.query(ItemTable.NAME, null, whereClause, whereArgs, null, null, null);
return new ItemCursorWrapper(cursor);
}
public String individualPriceOf(Item i) {
return MoneyUtils.prep(i.getPrice());
}
public String totalPriceOf(Item i) {
return MoneyUtils.prep(i.getCount() * i.getPrice());
}
public String countOf(Item i) {
return String.valueOf(i.getCount());
}
public void clearList() {
mDatabase.delete(ItemTable.NAME, null, null);
}
}
Item adding logic
public void addItem(Item item) {
mAccountant.addItem(item);
mAdapter.notifyItemInserted(mAccountant.getListFromSQL().size() - 1);
mAdapter.notifyDataSetChanged();
mChangeButton.setText(mAccountant.getChange());
}
Well there is fundamental problem not even related to RecyclerView.
First let's see how to fix your issue then explanation of what's wrong.
change this
private List<Item> mItemList;
to this
private final List<Item> mItemList;
then instead of any assignment like mItemList = getListFromSQL(); write this
mItemList.clear();
mItemList.addAll(getListFromSQL());
Now explanation why your code is not working. The thing is that when you assign your dataSource (i.e. mItemList) to some new value you are changing reference to it (that's a java fundamental thing) so that your RecyclerView doesn't know anything about it and it's own dataSource which you assign only once in constructor remains the same old one which is not changed therefore your notifyDataSetChanged call does nothing.
General advice whenever using RecyclerView or a ListView make sure you define your dataSource as final.
This is happening because you do not add the item into your Adpater's list. Make a method inside your adapter and call this method from your Accountant class.
private class ItemAdapter extends RecyclerView.Adapter<ItemHolder> {
public void addItem(Item item) {
mItemList.add(item); ///Add the item to your arrayList and then notify
notifyItemInserted(mItemList.size());
}
When you add single item in Adapter dont call notifyDataSetChanged() method because it will notify the whole list. Instead only use notifyItemInserted() method.
Another think is make sure when you notify the adapter it must be from UI thread.
When you add your item then just call this adapter addItem() method from your Accountant class.
public void addItem(Item item) { ///This method is from Accountant Class
mAccountant.addItem(item);
mAdapter.addItem(item); // Call the addItem() from Adapter class
mChangeButton.setText(mAccountant.getChange());
}
Hope it will work...
In my application i am trying to populate a wheel adapter using setter and getter method as shown in my Post Class.
class Post {
private String imageList;
private String country_name;
private String country_code;
public void setImageList ( String imageList){
this.imageList = imageList;
}
public String getImageList (){
return imageList;
}
public void setCountryName ( String country_name){
this.country_name = country_name;
}
public String getCountryName (){
return country_name;
}
...
}
My wheelAdapter class is as follows:
public class SecondWheelAdapter extends AbstractWheelTextAdapter {
ArrayList<convertor_pst> PostList = new ArrayList<convertor_pst>();
public ImageLoader imageLoader;
// Countries names
private String countries[] =
new String[] {"EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD"};
// Countries flags
private int flags[] = new int[] {R.drawable.eur, R.drawable.usd,R.drawable.eur, R.drawable.usd,R.drawable.eur, R.drawable.usd,R.drawable.eur, R.drawable.usd,R.drawable.eur, R.drawable.usd,R.drawable.eur, R.drawable.usd};
/**
* Constructor
*/
Convertor main;
public SecondWheelAdapter(Context context) {
super(context, R.layout.country_layout, NO_RESOURCE);
setItemTextResource(R.id.country_name);
}
#Override
public View getItem(int index, View cachedView, ViewGroup parent) {
View view = super.getItem(index, cachedView, parent);
ImageView img = (ImageView) view.findViewById(R.id.flag);
img.setImageResource(flags[index]);
return view;
}
#Override
public int getItemsCount() {
return countries.length;
}
#Override
protected CharSequence getItemText(int index) {
return countries[index];
}
I am trying to replace this array
// Countries names
private String countries[] =
new String[] {"EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD","EUR", "USD"};
with values stored in ArrayList<> but i don't know how to modify countries[] so that it accepts data from my PostList.get(id).getCountryName(); and next i need to set It in setItemTextResource(R.id.country_name);
Please give me a hint or a tutorial to follow. I get this wheel adapter from github it comes with a library but i am having dificulty.
Optimized solution:
Instead of managing different String Arrays or ArrayList, I would suggest you to create one ArrayList, it would be easy to manage single ArrayList.
I mean to say remove countries[] and flags[] and create Single ArrayList<Post> type.