ArrayAdapter values not changing - android

public class UnitListAdapter extends ArrayAdapter<Unit> {
private Context context;
private ArrayList<Unit> units;
private MeasurementType type;
private static HashMap<String, Double> unitValues;
public void setMeasurementType(MeasurementType measurementType) {
type = measurementType;
}
public void setUnitValues(HashMap<String, Double> newValues) {
unitValues.clear();
unitValues = newValues;
}
public void setUnits(ArrayList<Unit> newUnits) {
units = newUnits;
}
}
Above is the implementation of ArrayAdapter minus the getView() and getCount() methods. Now, in my activity I have this method:
public void updateUnitAdapter(ArrayList<Unit> units, final MeasurementType measurementType) {
//change the type
unitAdapter.setMeasurementType(measurementType);
//set the hashmap unit values
unitValues = new HashMap<String, Double>() {{
put(measurementType.getType(), DEFAULT_VALUE);
}};
//clear the current units for the previous measurement type
unitAdapter.clear();
//add the new units for the new measurement type
for(Unit u : units) {
unitAdapter.add(u);
}
//update the list view
unitAdapter.notifyDataSetChanged();
}
but when I step through in the debugger, it gets to the getView() method and when I check these variables, they are haven't changed to the new ones that I am setting them too, they stay the same...is there something I am not understanding about ArrayAdapter ?

ArrayAdapter has its own internal collection, which is modified when you call add() or clear(). In this case you are updating it, but (from your comments) it looks like you have also overriden getCount() and getView() to get those values from somewhere else (possibly the units member?).
If that's the case, you should update units intead of calling clear()/add().
Otherwise, remove the units field altogether and use getItem() to access the collection items.

Related

ArrayList Length gets 0 in Singleton

I am using a singleton for fetching data from a web service and storing the resulting data object in an ArrayList. It looks like this:
public class DataHelper {
private static DataHelper instance = null;
private List<CustomClass> data = null;
protected DataHelper() {
data = new ArrayList<>();
}
public synchronized static DataHelper getInstance() {
if(instance == null) {
instance = new DataHelper();
}
return instance;
}
public void fetchData(){
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions options = new QueryOptions();
options.setSortBy(Arrays.asList("street"));
query.setQueryOptions(options);
CustomClass.findAsync(query, new AsyncCallback<BackendlessCollection<CustomClass>>() {
#Override
public void handleResponse(BackendlessCollection<CustomClass> response) {
int size = response.getCurrentPage().size();
if (size > 0) {
addData(response.getData());
response.nextPage(this);
} else {
EventBus.getDefault().post(new FetchedDataEvent(data));
}
}
#Override
public void handleFault(BackendlessFault fault) {
EventBus.getDefault().post(new BackendlessFaultEvent(fault));
}
});
}
public List<CustomClass> getData(){
return this.data;
}
public void setData(List<CustomClass> data){
this.data = data;
}
public void addData(List<Poster> data){
this.data.addAll(data);
}
public List<CustomClass> getData(FilterEnum filter){
if(filter == FilterEnum.NOFILTER){
return getData();
}else{
// Filtering and returning filtered data
}
return getData();
}
}
The data is fetched correctly and the list actually contains data after it. Also, only one instance is created, as intended. However, whenever I call getData later, the length of this.data is 0. Because of this I also tried it with a subclass of Application holding the DataHelper object, resulting in the same problem.
Is there a good way of debugging this? Is there something like global watches in Android Studio?
Is there something wrong with my approach? Is there a better approach? I am mainly an iOS developer, so Android is pretty new to me. I am showing the data from the ArrayList in different views, thus I want to have it present in an the ArrayList as long as the application runs.
Thanks!
EDIT: Example use in a list view fragment (only relevant parts):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filter = FilterEnum.NOFILTER;
data = DataHelper.getInstance().getData(filter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
customClassListAdapter = new customClassListAdapter(getActivity(), data);}
EDIT2: Added code where I fetch the data from Backendless, changed reference of DataHelper to reference of data in first EDIT
EDIT3: I usa a local EventBus for notifying the list view about the new data. This looks like this and works (initially the data gets populated, but after e.g. applying a filter, the ArrayList I get with getData is empty):
#Subscribe
public void onMessageEvent(FetchedDataEvent event) {
customClassListAdapter.notifyDataSetChanged();
}
Try instead of keeping reference to your DataHelper instance, keeping reference to your list of retrieved items. F.e. when you first fetch the list (and it's ok as you say), assign it to a class member. Or itarate through it and create your own array list of objects for future use.
Okay I finally found the problem. It was not about the object or memory management at all. Since I give the reference on getData to my ArrayAdapter, whenever I call clear (which I do when changing the filter) on the ArrayAdapter, it empties the reference. I basically had to create a copy of the result for the ArrayAdapter:
data = new ArrayList<>(DataHelper.getInstance().getData(filter));
I was not aware of the fact that this is a reference at all. So with this the data always stays in the helper entirely. I only did this because this:
customClassListAdapter.notifyDataSetChanged();
does hot help here, it does not call getData with the new filter again.
Thanks everyone for your contributions, you definitely helped me to debug this.
It is likely that getData does get called before the data is filled.
A simple way to debug this is to add (import android.util.Log) Log.i("MyApp.MyClass.MyMethod", "I am here now"); entries to strategic places in fetchData, addData and getData and then, from the logs displayed by adb logcat ensure the data is filled before getData gets called.

On Android, how can I build a Realm Adapter from a Realm List?

I'm writing a recipe app on Android using Realm. I have a RealmList of type Ingredient in each Recipe object. The object creation code is working fine.
Now I'm writing the code for the Fragment that displays a single recipe. I was able to create a Realm Adapter for the all recipe titles list, since I built that list using a query like so:
public class RecipeTitleAdapter extends RealmBaseAdapter<RecipeTitle> implements ListAdapter {
public RecipeTitleAdapter(Context context, int resId,
RealmResults<RecipeTitle> realmResults,
boolean automaticUpdate) {
...
recipeTitles = RecipeTitle.returnAllRecipeTitles(realm);
final RecipeTitleAdapter adapter = new RecipeTitleAdapter(RecipeParserApplication.appContext, R.id.recipe_list_view, recipeTitles, true);
But now that I'm looking at the ingredients for a single recipe, I have a RealmList of Ingredients and not a RealmResults object. My ingredient adapter class has the same type of constructor as the recipe titles adapter, so I want to know how (or even if) I can make it work starting with a RealmList.
public class IngredientAdapter extends RealmBaseAdapter<Ingredient> implements ListAdapter {
private static class ViewHolder {
TextView quantity;
TextView unitOfMeasure;
TextView ingredientItemName;
TextView processingInstructions;
}
public IngredientAdapter(Context context, int resId,
RealmResults<Ingredient> realmResults,
boolean automaticUpdate) {
....
final IngredientAdapter adapter = new IngredientAdapter(RecipeParserApplication.appContext, R.id.ingredientListView, recipe.getIngredients(), true);
public RealmList<Ingredient> getIngredients() {
return ingredients;
}
Since recipe.getIngredients returns a RealmList, the line where the IngredientAdapter is assigned returns a compile error:
Error:(63, 43) error: constructor IngredientAdapter in class IngredientAdapter cannot be applied to given types;
required: Context,int,RealmResults,boolean
found: Context,int,RealmList,boolean
reason: actual argument RealmList cannot be converted to RealmResults by method invocation conversion
A RealmList behaves like an normal array, so if you cannot make a query that matches what you want to display, you can just use any of the normal adapters like e.g. a ArrayAdapter. The only advantage of using a RealmBaseAdapter is that it autorefreshes, but that is fairly easy to accomplish yourself:
// Pseudo code
ArrayAdapter adapter;
RealmChangeListener listener = new RealmChangeListener() {
public void onChange() {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
}
protected void onCreate(Bundle savedInstanceState) {
// ...
realm.addChangeListener(listener);
RealmList data = getRealmListData();
adapter = new ArrayAdapter(data);
listView.setAdapter(adapter);
}

Later Defining variable (else: NullPointerException)

I have an ArrayList, which is defined in one function, and gets called in another one. But when I add public ArrayList<String> list = new ArrayList<String>(); at the beginning, then run a function that adds items to that list and then try to call list in another function, it overwrites the previous set and just returns an empty list.
But when I just have public ArrayList<String> list; at the beginning, again add items in one function and then try to use list, it throws a NullPointerException.
I also tried having ArrayList<String> list = new ArrayList<String>(); in the onCreate method, but that's a NPE too.
So one function is just
public void setList() {
for(int x=0; x<=5;x++){
list.add(Integer.toString(x));
}
}
(the Integer.toString(x) is currently just for testing)
And the Other one is
public void setList() {
Log.d("Log",list.toString());
}
At the very beginning after public class MainActivity extends FragmentActivity {
I have tried
public ArrayList<String> list; //this throws a NPE
and
public ArrayList<String> list = new ArrayList<String>(); //this overwrites the variable each time it's called
How do I fix that?
Could the issue be that I change the displayed Fragment after the set method and before the get method is called?
Thank you very much
Use 1 list object at the top of the code and do not create new ones in the functions. It should look like this.
public static ArrayList<String> list = new ArrayList<String>();
// onCreate....
public void addElement() {
for(int x=0; x<=5;x++){
list.add(Integer.toString(x));
}
}
public void showElements() {
Log.d("Log",list.toString());
}

Compare last text of a TextView with new, updated one, in android home widget using AsyncTask?

I've made a simple home screen widget which has two TextView fields and a refresh button. TextViews are loaded from AsyncTask which downloads and parses small XML file. The refresh button calls onUpdate again (using PendingIntent), and eventually AsyncTask again. I have a simple model class XMLValues which can store parsed values from the XML (and which is initialized in the AppWidgetProvider class:
public class XmlValues {
private int mFieldValue1;
public int getFieldValue1() {
return mFieldValue1;
}
public void setFieldValue1(int FieldValue) {
mFieldValue1 = FieldValue;
private int mFieldValue2;
public int getFieldValue2() {
return mFieldValue2;
}
public void setFieldValue2(int FieldValue) {
mFieldValue2 = FieldValue;
}
}
In AsyncTask (I pass in it RemoteViews views, int appWidgetID, AppWidgetManager appWidgetManager) where the XML is downloaded and parsed, the postExecute method looks something like this:
protected void onPostExecute(XmlValues xv) {
if (isCancelled()) {
xv = null;
}
views.setTextViewText(R.id.textView1, xv.getFieldValue1());
views.setTextViewText(R.id.textView2, xv.getFieldValue1());
WidgetManager.updateAppWidget(WidgetID, views);
}
This all works (I load the TextViews with correct values), however I am stuck trying to do the following - since the XML values that I am parsing can potentially change, I would like (for example) to change the color of the TextViews if the new values that come from the new XML are larger then the current ones in the widget. How can I achieve this? I tried looking around but didn't manage to find how to affect the model class object created in the main widget class, from the onPostExecute method from AsyncTask.
You can use static int field where you can store last value:
private static int lastValue1 = -1; // you can use other initial value
private static int lastValue2 = -1;
You can use it then, for example:
if( lastValue1 < xv.getFieldValue1() ) {
lastValue1 = xv.getFieldValue1();
views.setTextViewText(R.id.textView1, xv.getFieldValue1());
}
// the same for second value.
Another solution is to store last value in SharedPreferenced.

Can't initialise ArrayList

Very simple error I'm sure, just having a mental block for some reason!
I have an object which contains an ArrayList of other objects. When I try to initialise the list in the constructor it does not seem to initialise and I get a nullpointer when I attempt to access the list within the code.
Order class variables:
private int covers;
private int table;
private ArrayList<MenuItem> items;
Here is the Order class:
public Order(int covers, int table) {
super();
this.covers = covers;
this.table = table;
this.items = new ArrayList<MenuItem>();
}
Here is the code within the MainActivity causing me problems:
order = new Order();
order.setCovers(2);
order.setTable(1);
order.addToOrder(new MenuItem("Item 1", 12.99));
Toast.makeText(getApplicationContext(), "order size: " + order.getItems().size(), Toast.LENGTH_SHORT).show();
I would expect the Toast to display "1". However when I ran debugger I noticed the order object ArrayList attribute was equal to null.
Any idea why? Thanks in advance!
EDIT: addToOrder method:
public void addToOrder(MenuItem m){
items.add(m);
}
You have failed to initialize items. Use
vorder = new Order(int1, int2);
instead of
vorder = new Order();
items is initialized inside
public Order(int covers, int table) {
}
not on the default constructor.

Categories

Resources