My project simplify as below:
First, I use application method Data.java to save data.
It contain the data:
private ArrayList<String> data = new ArrayList<String>();
public int getsize() {
return this.data.size();
}
public String getdata(int i) {
return this.data.get(i);
}
public void adddata(String s) {
return this.data.add(s);
}
My AActivity class onCreate as below:
Data d = (Data)this.getApplication();
String test = new String[d.getsize()];
for(i = 0; i < d.getsize(); i++) {
test[i] = d.getdata(i);
}
//to show in list
DataAdapter = new DataAdapter (this, test);
setListAdapter(DataAdapter);
And when button is click, startActivity the BActivity class.
In BActivity class, the code as below:
Data d = (Data)this.getApplication();
d.adddata("newdata");
finish();
And AActivity class onResume() as below:
#Override
public void onResume(){
super.onResume();
this.DataAdapter.notifyDataSetChanged();
}
But why the list is not update?
I confirm the data has be save.
My DataAdapter:
public DataAdapter(Context ctxt, String[] d) {
this.data = new String[d.length];
myInflater = LayoutInflater.from(ctxt);
int i;
for(i = 0; i < d.length; i++) {
data[i] = d[i];
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewTag viewTag;
if(convertView == null) {
convertView = myInflater.inflate(R.layout.row_bookmark_list, null);
viewTag = new ViewTag((TextView)convertView.findViewById(R.id.tv));
convertView.setTag(viewTag);
}
else {
viewTag = (ViewTag) convertView.getTag();
}
viewTag.tv.setText(data[position]);
}
class ViewTag {
TextView tv;
public ViewTag(TextView t) {
this.tv = t;
}
}
Add your new data directly to the adapter not to 'd'. The adapter keeps its own internal data which means that whatever changes you apply to your 'd' has no impact on the adapter.
For example:
List<String> itemsList = new ArrayList<String>();
ArrayAdapter aa = new ArrayAdapter(..., itemsList);
...
itemsList.add("new item"); --> wrong!
aa.notifyDataSetChanged(); --> nothing changes, you wrongly added the item to itemsList
you have to deal directly with the adapter:
aa.add("new item"); --> correct
aa.notifyDataSetChanged(); --> the adapter will reflect the change
You can't access the notifyDataSetChanged as a static method
( thats what you are doing in your example ).
If you have a ListActivity: you have access to the method getListAdapter().
Thats the right reference to your dataset.
So in short:
getListAdapter().notifyDataSetChanged();
will do the trick. If you don't have a ListActivity then you will have to find your listview thru View.findViewById([id of listview]) and get the Adapter there.
Hope this helps a bit :-)
I think your problem is that your DataAdapter is referenced to the array test, but test never changes. Referencing data to the DataAdapter instead of test should work.
OK, after looking at the Adapter code it will not work. Why are you copying your data? The adapter will never notice a change in the data element, because it is only working with a copy of that element at construction time. If copying the data is necessary, you should make sure the adapter updates its content, too.
Related
I saw all the questions which is similar to my question ( in this , this , this and this link )
I had myAdapter.notifyDataSetChanged() in my Activity but it doesn't work
I have 3 classes,
DBHelper - For storing and getting Database contents ( NO ISSUES HERE )
SimpleRecyclerAdapter - Adapter for RecyclerList
ThirdActivity
What i did in ThirdActivity :
I have TextBox to get data and store it in Database and a Button. In
the Onclicklistener of Button, i specified code to
get text from textbox
add it into table using DBHelper
retrive data as ArrayList from DBHelper
myAdapter.notifyDataSetChanged()
When i click the Button, I got Data in LogCat which i specified inside OnclickListener but it is not reflected to the listview.
Here is my code,
ThirdActivity:
public class ThirdActivity extends AppCompatActivity{
private DrawerLayout mDrawerLayout;
DbHelper dbHelper;
EditText et;
Button addButton;
RecyclerView rv;
ArrayList<String> myNotesList;
SimpleRecycler3Adapter adapter3;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.thirdactivity);
myNotesList = new ArrayList<>();
et=(EditText) findViewById(R.id.et);
addButton=(Button)findViewById(R.id.addButton);
rv = (RecyclerView) findViewById(R.id.dbListrv);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getBaseContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
adapter3 = new SimpleRecycler3Adapter(myNotesList);
rv.setAdapter(adapter3);
dbHelper = new DbHelper(this, null, null, 1);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("DB", "Constructor");
String note=et.getText().toString();
dbHelper.addNote(note);
printData();
}
});
}
public void printData(){
Log.d("DB","Constructor");
myNotesList=dbHelper.databasetostring();
Log.d("DB","Data came"+myNotesList.get(myNotesList.size()-1));
// adapter3 = new SimpleRecycler3Adapter(myNotesList);
// rv.setAdapter(adapter3);
adapter3.notifyDataSetChanged();
}
}
SimpleRecyclerViewAdapter :
public class SimpleRecycler3Adapter extends RecyclerView.Adapter<SimpleRecycler3Adapter.NotesHolder> {
private ArrayList<String> myNotesList=new ArrayList<String>();
String TAG="ThirdAdapter kbt";
RecyclerView rv;
public SimpleRecycler3Adapter(ArrayList<String> myList) {
Log.d(TAG,"Constructor");
Log.d(TAG,"Not null");
int i = 0;
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
Log.d(TAG,"finish");
}
#Override
public NotesHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Log.d(TAG,"On create started");
View view2 = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerlist3_item, viewGroup, false);
Log.d(TAG,"ADAP STAR ONCR second switch 2nd line");
NotesHolder viewHolder2 = new NotesHolder(view2);
Log.d(TAG,"ADAP STAR ONCR second switch 3nd line");
return viewHolder2;
}
#Override
public void onBindViewHolder(NotesHolder notesHolder, int i) {
Log.d(TAG, "ONBIND SECOND i value is " + i);
// notesHolder.thumbnail.setImageResource(R.drawable.placeholder);
notesHolder.dblistitem.setText(myNotesList.get(i));
Log.d(TAG,"ONBIND second title issssss");
}
#Override
public int getItemCount() {
return myNotesList.size();
}
class NotesHolder extends RecyclerView.ViewHolder {
protected ImageView thumbnail;
protected TextView dblistitem;
public NotesHolder(View itemView) {
super(itemView);
Log.d(TAG, "JSON Inside HOLDER");
rv=(RecyclerView)itemView.findViewById(R.id.dbListrv);
// thumbnail = (ImageView) itemView.findViewById(R.id.thumbnail);
dblistitem = (TextView) itemView.findViewById(R.id.dblistitem);
}
}
}
You're not updating the myNotesList that is in adapter class but in activity class. But the adapter uses it's local myNotesList.
So on button click, update myNotesList of adapter with latest data available and notify the adapter.
EDIT
Pass the latest data to adapter. Have this method in adapter class and call this before notifyDataSetChanged();
public void updateNotes(ArrayList<String> notesList) {
myNotesList = notesList;
}
1.you are intializing your dbhelper after setting adapter to listview so it couldn't contain any data initially
2.for updating recycler view data list do as follows
myNotesList.clear();
myNotesList.addAll(dbHelper.databasetostring());
adapter3.notifyDataSetChanged();
You have a problem in your SimpleRecyclerViewAdapter, just change this:
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
For this:
myNotesList = myList;
And in your activity's printData() change:
myNotesList=dbHelper.databasetostring();
for this:
myNotesList.clear();
myNotesList.addAll(dbHelper.databasetostring());
adapter3.notifyDataSetChanged();
Explanation:
First you initialize myNotesList variable:
myNotesList = new ArrayList<>();
Then you initialize adapter3
adapter3 = new SimpleRecycler3Adapter(myNotesList);
But your adapter is not saving the reference, instead you're copying its data into another variable:
while (i < myNotesList.size()) {
myNotesList.add(myList.get(i).toString());
}
Doing that, if you change myNotesList variable in your activity will not modify your adapter's dataset.
In your method printData() you change myNotesList variable. Which will not touch your adapter or its data
public void printData(){
Log.d("DB","Constructor");
myNotesList=dbHelper.databasetostring();
Log.d("DB","Data came"+myNotesList.get(myNotesList.size()-1));
// adapter3 = new SimpleRecycler3Adapter(myNotesList);
// rv.setAdapter(adapter3);
adapter3.notifyDataSetChanged();
}
You can't change myNotesList by changing myList.
public SimpleRecycler3Adapter(ArrayList<String> myList) {
Log.d(TAG,"Constructor");
Log.d(TAG,"Not null");
// int i = 0;
// while (i < myNotesList.size()) {
// myNotesList.add(myList.get(i).toString());
// }
this.myNotesList = myList;
Log.d(TAG,"finish");
}
Not a good idea to call notifyDataSetChanged() when you know exactly what changed in your data collection.
See this implementation here.
They have even documented to use notifyDataSetChanged() as a last resort in this doc.
You get nice animations for free if you use methods like notifyItemInserted() and the rest.
Also do not go on replacing the collection object entirely, see the implmentation link that has been attached.
So I have 2 activities.
The first (ActivityOne) displays a listview with data from SQLite cursor, and a button.
On click of that button, I want to add an item to the listview, so I display the second activity (ActivityTwo), that contains a number of editTexts and a save Button, that does the saving in the Database.
But what I want is:
after saving the new item to the DB, the ActivityTwo should close and the ActivityOne should be displayed with the refreshed content from the DB
.
This seems a reasonable workflow. How do I achieve it?
Code for ActivityOne:
public class ActivityOne extends Activity {
private ArrayList<String> idclient = new ArrayList<String>();
private ArrayList<String> numeclient = new ArrayList<String>();
private ArrayList<String> tipclient = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ListView mylist = (ListView) findViewById(R.id.lv_clienti);
LoadList();
Button btnex = (Button) findViewById(R.id.btnNewCli);
btnex.setOnClickListener(
new View.OnClickListener()
{
public void onClick(View aView)
{
Toast.makeText(getApplicationContext(), "Add new client... " , Toast.LENGTH_SHORT).show();
Intent toAnotherActivity = new Intent(aView.getContext(), NewClientActivity.class);
startActivity(toAnotherActivity);
}
}
);
}
public void LoadList(){
SQLiteDatabase db = new myDbHelper(getApplicationContext()).getWritableDatabase();
Cursor mCursor = db.rawQuery("select idclient,nameclient,typeclient from clienti order by numeclient" , null);
idclient.clear();
numeclient.clear();
tipclient.clear();
if (mCursor.moveToFirst()) {
do {
idclient.add(Integer.toString(mCursor.getInt(0)));
nameclient.add(mCursor.getString(1));
typeclient.add(mCursor.getString(2));
} while (mCursor.moveToNext());
}
DisplayClientiAdapter disadpt = new DisplayClientiAdapter(ClientiActivity.this,idclient,nameclient, typeclient);
ListView lv = (ListView) findViewById(R.id.lv_clienti);
lv.setAdapter(disadpt);
mCursor.close();
db.close();
}
}
And in the ActivityTwo, I have in a button click:
db.execSQL("insert into clients (idclient, nameclient,typeclient,...");
DisplayClientiAdapter da = new DisplayClientiAdapter(getApplicationContext());
da.notifyDataSetChanged();
finish();
Also the displayAdapter is something like:
public class DisplayClientiAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String> idclient;
private ArrayList<String> numeclient;
private ArrayList<String> tipclient;
public DisplayClientiAdapter(Context c){
this.mContext = c;
}
public DisplayClientiAdapter(Context c, ArrayList<String> idclient, ArrayList<String> numeclient, ArrayList<String> tipclient) {
this.mContext = c;
this.idclient = idclient;
this.numeclient = numeclient;
this.tipclient = tipclient;
}
public int getCount() {
return idclient.size();
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int pos, View child, ViewGroup parent) {
Holder mHolder;
LayoutInflater layoutInflater;
if (child == null) {
layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
child = layoutInflater.inflate(R.layout.clienti_item, null);
mHolder = new Holder();
mHolder.txt_idclient = (TextView) child.findViewById(R.id.tv_cl_id);
mHolder.txt_numeclient = (TextView) child.findViewById(R.id.tv_cl_nume);
mHolder.txt_tipclient = (TextView) child.findViewById(R.id.tv_cl_tip);
child.setTag(mHolder);
} else {
mHolder = (Holder) child.getTag();
}
mHolder.txt_idclient.setText(idclient.get(pos));
mHolder.txt_numeclient.setText(numeclient.get(pos));
mHolder.txt_tipclient.setText(tipclient.get(pos));
return child;
}
public class Holder {
TextView txt_idclient;
TextView txt_numeclient;
TextView txt_tipclient;
}
Of course it does not work like this. The list is not refreshed... I assume it has to do with the displayAdapter !?!?!
I cannot call the LoadList method since it is static or something like that...
Please help.
Thank you
Its not a problem with your adapter. You have to call Loadlist() in onresume method instead of oncreate method in ActivityOne. It will work then.
First of all, have a look at this two articles:
http://www.doubleencore.com/2013/05/layout-inflation-as-intended/
http://www.doubleencore.com/2013/06/context/
You shouldn't inflate your views with null in your inflate method if you have parent view available.
Also, using application context for inflating may cause strange behaviour, as it may not use correct theme you may've set in app manifest for your Activity.
On the other hand - why don't you use CursorAdapter instead of BaseAdapter?
The problem with your adapter is, that you don't set the data in it! :)
///EDIT:
I checked the wrong activity - why do you create second adapter in there?
The easiest solution would be to move the LoadList() to onStart.
If you want to do it right, you should use ContentObserver and (probably) CursorAdapter.
When I repopulate my ListView, I call a specific method from my Adapter.
Problem:
When I call updateReceiptsList from my Adapter, the data is refreshed, but my ListView doesn't reflect the change.
Question:
Why doesn't my ListView show the new data when I call notifyDataSetChanged?
Adapter:
public class ReceiptListAdapter extends BaseAdapter {
public List<Receipt> receiptlist;
private Context context;
private LayoutInflater inflater;
private DateHelpers dateH;
public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
context = mcontext;
receiptlist = rl;
Collections.reverse(receiptlist);
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
dateH = new DateHelpers();
}
#Override
public int getCount() {
try {
int size = receiptlist.size();
return size;
} catch(NullPointerException ex) {
return 0;
}
}
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
#Override
public Receipt getItem(int i) {
return receiptlist.get(i);
}
#Override
public long getItemId(int i) {
return receiptlist.get(i).getReceiptId() ;
}
private String getPuntenString(Receipt r) {
if(r.getPoints().equals("1")) {
return "1 punt";
}
return r.getPoints()+" punten";
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final Receipt receipt = receiptlist.get(position);
ReceiptViewHolder receiptviewholder;
Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");
Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");
if (vi == null) { //convertview==null
receiptviewholder = new ReceiptViewHolder();
vi = inflater.inflate(R.layout.view_listitem_receipt, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
receiptviewholder.shop.setTypeface(tf_hn_bold);
receiptviewholder.price.setTypeface(tf_hn_bold);
vi.setTag(receiptviewholder);
}else{//convertview is not null
receiptviewholder = (ReceiptViewHolder)vi.getTag();
}
receiptviewholder.shop.setText(receipt.getShop());
receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
receiptviewholder.price.setText("€ "+receipt.getPrice());
receiptviewholder.points.setText(getPuntenString(receipt));
vi.setClickable(false);
return vi;
}
public static class ReceiptViewHolder {
public TextView shop;
public TextView date;
public TextView price;
public TextView points;
}
public Object getFilter() {
// XXX Auto-generated method stub
return null;
}
}
--EDIT:
found Workaround
Just to have some functional code i do now:
listview.setAdapter( new ReceiptListAdapter(activity,mcontext, -new dataset-);
Works, but not how it is supposed to work.
Change your method from
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
To
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist.clear();
receiptlist.addAll(newlist);
this.notifyDataSetChanged();
}
So you keep the same object as your DataSet in your Adapter.
I have the same problem, and i realize that. When we create adapter and set it to listview, listview will point to object somewhere in memory which adapter hold, data in this object will show in listview.
adapter = new CustomAdapter(data);
listview.setadapter(adapter);
if we create an object for adapter with another data again and notifydatasetchanged():
adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();
this will do not affect to data in listview because the list is pointing to different object, this object does not know anything about new object in adapter, and notifyDataSetChanged() affect nothing.
So we should change data in object and avoid to create a new object again for adapter
As I have already explained the reasons behind this issue and also how to handle it in a different answer thread Here. Still i am sharing the solution summary here.
One of the main reasons notifyDataSetChanged() won't work for you - is,
Your adapter loses reference to your list.
When creating and adding a new list to the Adapter. Always follow these guidelines:
Initialise the arrayList while declaring it globally.
Add the List to the adapter directly with out checking for null and empty
values . Set the adapter to the list directly (don't check for any
condition). Adapter guarantees you that wherever you make
changes to the data of the arrayList it will take care of it, but never loose the
reference.
Always modify the data in the arrayList itself (if your data is completely new
than you can call adapter.clear() and arrayList.clear() before
actually adding data to the list) but don't set the adapter i.e If
the new data is populated in the arrayList than just
adapter.notifyDataSetChanged()
Hope this helps.
Maybe try to refresh your ListView:
receiptsListView.invalidate().
EDIT: Another thought came into my mind. Just for the record, try to disable list view cache:
<ListView
...
android:scrollingCache="false"
android:cacheColorHint="#android:color/transparent"
... />
I had the same problem using ListAdapter
I let Android Studio implement methods for me and this is what I got:
public class CustomAdapter implements ListAdapter {
...
#Override
public void registerDataSetObserver(DataSetObserver observer) {
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
}
...
}
The problem is that these methods do not call super implementations so notifyDataSetChange is never called.
Either remove these overrides manually or add super calls and it should work again.
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
super.unregisterDataSetObserver(observer);
}
If adapter is set to AutoCompleteTextView then notifyDataSetChanged() doesn't work.
Need this to update adapter:
myAutoCompleteAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, myList);
myAutoComplete.setAdapter(myAutoCompleteAdapter);
Refer: http://android-er.blogspot.in/2012/10/autocompletetextview-with-dynamic.html
class StudentAdapter extends BaseAdapter {
ArrayList<LichHocDTO> studentList;
private void capNhatDuLieu(ArrayList<LichHocDTO> list){
this.studentList.clear();
this.studentList.addAll(list);
this.notifyDataSetChanged();
}
}
You can try. It work for me
If by any chance you landed on this thread and wondering why adapter.invaidate() or adapter.clear() methods are not present in your case then maybe because you might be using RecyclerView.Adapter instead of BaseAdapter which is used by the asker of this question. If clearing the list or arraylist not resolving your problem then it may happen that you are making two or more instances of the adapter for ex.:
MainActivity
...
adapter = new CustomAdapter(list);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
...
and
SomeFragment
...
adapter = new CustomAdapter(newList);
adapter.notifyDataSetChanged();
...
If in the second case you are expecting a change in the list of inflated views in recycler view then it is not gonna happen as in the second time a new instance of the adapter is created which is not attached to the recycler view. Setting notifyDataSetChanged in the second adapter is not gonna change the content of recycer view. For that make a new instance of the recycler view in SomeFragment and attach it to the new instance of the adapter.
SomeFragment
...
recyclerView = new RecyclerView();
adapter = new CustomAdapter();
recyclerView.setAdapter(adapter);
...
Although, I don't recommend making multiple instances of the same adapter and recycler view.
In my case I simply forget to add in my fragment mRecyclerView.setAdapter(adapter)
Add this code
runOnUiThread(new Runnable() { public void run() {
adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();
}
});
I made a very noob mistake that I was setting the adapter of RecyclerView before initialzing the adapter itself like this.
// Assuume oneOffJobTasksListRVAdapter is declared already
recyclerView.setAdapter(oneOffJobTasksListRVAdapter);
oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
Switching the lines fixed my issue.
oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
recyclerView.setAdapter(oneOffJobTasksListRVAdapter);
If you're using a custom adapter you have to add
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
to your custom adapter methods, then you only need to call notifyDataSetChanged() after you change your data, like replace, remove or add a new item
ArrayList <String> items;
int position=1;
items.set(position,"Changed Item");
items.remove(position);
items.add("New item");
notifyDataSetChanged();
I have the same problem
but I just finished it!!
you should change to
public class ReceiptListAdapter extends BaseAdapter {
public List<Receipt> receiptlist;
private Context context;
private LayoutInflater inflater;
private DateHelpers dateH;
private List<ReceiptViewHolder> receiptviewlist;
public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
context = mcontext;
receiptlist = rl;
receiptviewlist = new ArrayList<>();
receiptviewlist.clear();
for(int i = 0; i < receiptlist.size(); i++){
ReceiptViewHolder receiptviewholder = new ReceiptViewHolder();
receiptviewlist.add(receiptviewholder);
}
Collections.reverse(receiptlist);
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
dateH = new DateHelpers();
}
#Override
public int getCount() {
try {
int size = receiptlist.size();
return size;
} catch(NullPointerException ex) {
return 0;
}
}
public void updateReceiptsList(List<Receipt> newlist) {
receiptlist = newlist;
this.notifyDataSetChanged();
}
#Override
public Receipt getItem(int i) {
return receiptlist.get(i);
}
#Override
public long getItemId(int i) {
return receiptlist.get(i).getReceiptId() ;
}
private String getPuntenString(Receipt r) {
if(r.getPoints().equals("1")) {
return "1 punt";
}
return r.getPoints()+" punten";
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final Receipt receipt = receiptlist.get(position);
ReceiptViewHolder receiptviewholder;
Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");
Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");
if (vi == null) { //convertview==null
ReceiptViewHolder receiptviewholder = receiptviewlist.get(position);
vi = inflater.inflate(R.layout.view_listitem_receipt, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
receiptviewholder.shop.setTypeface(tf_hn_bold);
receiptviewholder.price.setTypeface(tf_hn_bold);
vi.setTag(receiptviewholder);
}else{//convertview is not null
receiptviewholder = (ReceiptViewHolder)vi.getTag();
}
receiptviewholder.shop.setText(receipt.getShop());
receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
receiptviewholder.price.setText("€ "+receipt.getPrice());
receiptviewholder.points.setText(getPuntenString(receipt));
vi.setClickable(false);
return vi;
}
public static class ReceiptViewHolder {
public TextView shop;
public TextView date;
public TextView price;
public TextView points;
}
public Object getFilter() {
// XXX Auto-generated method stub
return null;
}
}
My case was different but it might be the same case for others
for those who still couldn't find a solution and tried everything above, if you're using the adapter inside fragment then the reason it's not working fragment could be recreating so the adapter is recreating everytime the fragment recreate
you should verify if the adapter and objects list are null before initializing
if(adapter == null){
adapter = new CustomListAdapter(...);
}
...
if(objects == null){
objects = new ArrayList<>();
}
I have a peculiar problem. I am parsing a restaurant's menu card. They have it in english and in german. I have a class FoodItem as :
public class FoodItem {
private int foodClass;
private String foodType;
private String foodName;
private String foodCost;
private String hauptBeilage;
private String salat;
}
Now, I have an arraylist of fooditems downloaded using Jsoup. I separate the german and english menu using the String foodType.
I want to list german menu at the start. But, I get the english menu appended to the list as well. How should I tackle this?
My downloadThread (Jsoup) is :
public void run()
{
Log.i("downloadThread", "Inside run() - Starting getFoodItems");
getDailyGerman();
getDailyEnglish();
//Sending a message through handler here
}
In my activity, I have:
handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
foodItemAdapter.notifyDataSetChanged();
}
};
If I send a message through handler after getDailyGerman(); then i get a illegalstateexception saying the content of the adapter has changed, but the listview is not updated.
My Adapter code :
public FoodItemAdapter(Context context, int textViewResourceId, ArrayList<FoodItem> FoodItemArg) {
super(context, textViewResourceId, FoodItemArg);
FoodItemAdapter.foodItems = FoodItemArg;
this.setNotifyOnChange(false);
// if(FoodItemAdapter.foodItems == null)
// Log.i("Adapter", "Problem Inside Adapter Constructor");
}
//=========================public methods============================
public static ArrayList<FoodItem> getDailyEnglishFoodItems()
{
ArrayList<FoodItem> returnList = new ArrayList<FoodItem>();
for(FoodItem eachItem : FoodItemAdapter.foodItems)
{
if(eachItem.getFoodClass() == 1)
{
Log.i("Adapter" , "Adding English Daily Food : " + eachItem.getFoodName());
returnList.add(eachItem);
}
}
return returnList;
}
public static ArrayList<FoodItem> getDailyGermanFoodItems()
{
ArrayList<FoodItem> returnList = new ArrayList<FoodItem>();
for(FoodItem eachItem : FoodItemAdapter.foodItems)
{
if(eachItem.getFoodClass() == 2)
{
Log.i("Adapter" , "Adding German Daily Food : " + eachItem.getFoodName());
returnList.add(eachItem);
}
}
return returnList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
/*
* Describes each view in the list view.
* Get the question and find the question text, timestamp and the votes.
* Show them in the textview which is a part of the listview.
*/
View v = convertView;
FoodItem foodItem =(FoodItem) FoodItemAdapter.foodItems.get(position);
if(foodItem == null)
{
Log.i("Adapter", "Null Food Item");
}
int colorPos = 0;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.fooditem_row, null);
colorPos = position % colors.length;
}
Please help as I am stuck at this point for 3 days. Thanks.
I had the same issue once I added the items and called
notifyDataSetChanged() in the UI
thread issue solved
From What I understand of your question, you want to have the English items at the top of the list then the German Items. you can do that using Collection.sort method and Using a specific comparator for the task in hand.
For example:
final List<FoodItem> combinedList = getDailyGermanFoodItems();
combinedList.addAll(getDailyEnglishFoodItems());
Collections.sort(compinedList, new FoodItemComparator());
//then you call the handler to update the adapter and the listView
handler.post(new Runnable(){
public void run(){
FoodItemAdapter adapter = new FoodItemAdapter(activity.this, layout, combinedList);
listView.setAdapter(adapter);
}});
where FoodItemComparator:
public class FoodItemComparatorimplements Comparator<FoodItem>{
public int compare(FoodItem item1, item2) {
String foodType1 = item1.getFoodType();
String foodType2 = item2.getFoodType();
if (foodType1.equals(foodType2))
return 0;
if (foodType1.equals("English"))
return 1;
if (foodType2.equals("English))
return -1;
return foodType1.compareTo(foodType2);
}
}
Assuming foodType Value is guaranteed to be German/English only.
Also you will have to have a getter funcion inside your FoodItem Class so the comparator can access it:
Class FoodItem
.......
public String getFoodType(){
return foodType;
}
EDIT
If you want to display each one alone , then store the two lists inside your activity object, then when user select a language (english / german):
FoodItemAdapter adapter = new FoodItemAdapter(activity.this, layout, germanList);
listView.setAdapter(adapter);
From this answer in stack overflow and the sample project referred there, i got the Idea of RotationAsync, where a progress bar work fine with device rotation.
But my problem is, i have a listview with each row there is progress bar. And is there any way to retain the progress while rotation for reach row.
Me creating onclicklistener object for the button click listener in getview function of my adapter class. Where its onClick function call the AsyncTask class
Since each getview (row) is calling different instant of my AsyncTask, i cannot make it static of single ton class.
Any Idea on this.
Thanks.
So you have a ListView which I assume you have some adapter which in it's get view hosts the progress bars. However that progress must be backed by something right? So just save that data. Like I am assuming an adapter like so:
public class MyProgressBarAdapter extends BaseAdapter {
private ArrayList<Integer> mProgessValues;
private SparseArray<AsyncTask<?,?,?>> mTasks;
// No stored reference to a Context
private MyProgressBarAdapter() {
}
public void saveState(Bundle bundle) {
bundle.putIntegerArrayList(getClass().getName() + ".progressValues", mProgressValues);
}
public Object exportLiveState() {
return mTasks;
}
public static MyProgressBarAdapter restore(Bundle bundle, Object rawState) {
MyProgressBarAdapter adapter = new MyProgressBarAdapter();
Class<MyProgressBarAdapter> c = adapter.getClass();
ArrayList<Integer> progresses = null;
if (bundle != null) {
progresses = bundle.getIntegerArrayList(c.getName() + ".progressValues");
}
if (progresses == null) {
progresses = new ArrayList<Integer>();
}
adapter.mProgressValues = progresses;
if (rawState != null && rawState instanceof SparseArray) {
adapter.mTasks = (SparseArray<AsyncTask<?,?,?>>) rawState;
}
return adapter;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = getViewWithHolder(convertView, parent);
ViewHolder holder = convertView.getTag();
// set the appropriate things on the view elements.
holder.position = position;
holder.taskContainer = mTasks;
holder.progressBar.setProgress(mProgressValues.get(position));
convertView.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
ViewHolder holder = view.getTag();
int pos = holder.position;
SparseArray<AsyncTask> tasks = holder.taskContainer;
AsyncTask task = tasks.get(pos);
if (task == null) {
// Create your task
task = new AsyncTask<?, ?, ?> (...);
tasks.put(pos, task);
task.execute();
}
}
return convertView;
}
/// You can write the rest of the adapter I believe.
...
}
and then you don't really need onConfigurationChanged. Just read and save your data accordingly.
public class MyActivity extends Activity {
ListView mListView;
MyProgressBarAdapter mAdapter;
#Override
public void onCreate(Bundle savedState) {
super.onCreate();
Object[] liveState = getLastNonConfigurationInstance();
setContentView(R.layout.mylistview_with_progressbars);
mListView = findViewById(R.id.listview);
// Be consistent with the index
MyProgressBarAdapter adapter = MyProgressBarAdapter.restore(savedState, liveState[0]);
mListView.setAdapter(adapter);
mAdapter = adapter;
...
}
#Override
public void onSaveInstanceState(Bundle bundle) {
mAdapter.save(bundle);
}
#Override
public Object[] onRetainNonConfigurationInstance () {
// size to be whatever live state you need to store other than the adapter
Object[] objects = new Object[1];
// This reference will be retained between onCreate() and onDestroy() calls.
objects[0] = mAdapter.exportLiveState();
// Any other things that can't be serialized
return objects;
}
#Override
public Object[] getLastNonConfigurationInstance() {
Object[] live = (Object[]) super.getLastNonConfigurationInstance();
if (live == null) {
live = new Object[1];
}
return live;
}
// The rest of your activity
...
}
That will make it so that when you flip the orientation, the adapter will be recreated but it will be reinitialized to the same state it was in before. I made some assumptions about the way you store your progress and the nature of your asyncTasks but I hope you can adjust as needed.
You could even, if you don't store a reference to any context, you might be able to get away with just storing the entire adapter itself inside the onRetainNonConfigurationInstance() and using that in the getLastRetainedNonConfigurationInstance()
You can set android:configChanges="orientation" in manifest file to make your activity does not restart when rotating
One solution that i used
If we have only one layout for both landscape and portrait mode, then we can we can solve this by
1. Set the activity asandroid:configChanges="orientation" in manifest file
2. Override the onConfigurationChanged like this
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
But the problem is still the if i need to use different layout for both landscape and portrait mode, each have a listview with progress bar in each row. there i need to retain the progress while rotate which use same AsyncTask class.
How can you set percentage value for each row item? Why don't you update that value to the data item. You can have some thing like below. Since you have the data item you can store whatever you want :) Ps: I wonder that I can format text in comment to not add new answer.
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView,
ViewGroup parent)
public Object getChild(int groupPosition, int childPosition)