I have extensive use of ArrayAdapter in my app because most Activities are holding a ListView and I need some custom stuff in them.
I took a look at the test classes in the android developer documentation but wasn't able to find some examples or a proper testclass...
1) Are there any best practices for (unit)-testing ArrayAdapter in Android?
2) May I have chosen the wrong approach (with the adapters) and killed testability this way?
You can write the test extending AndroidTestCase It will looks something like this:
public class ContactsAdapterTest extends AndroidTestCase {
private ContactsAdapter mAdapter;
private Contact mJohn;
private Contact mJane;
public ContactsAdapterTest() {
super();
}
protected void setUp() throws Exception {
super.setUp();
ArrayList<Contact> data = new ArrayList<Contact>();
mJohn = new Contact("John", "+34123456789", "uri");
mJane = new Contact("Jane", "+34111222333", "uri");
data.add(mJohn);
data.add(mJane);
mAdapter = new ContactsAdapter(getContext(), data);
}
public void testGetItem() {
assertEquals("John was expected.", mJohn.getName(),
((Contact) mAdapter.getItem(0)).getName());
}
public void testGetItemId() {
assertEquals("Wrong ID.", 0, mAdapter.getItemId(0));
}
public void testGetCount() {
assertEquals("Contacts amount incorrect.", 2, mAdapter.getCount());
}
// I have 3 views on my adapter, name, number and photo
public void testGetView() {
View view = mAdapter.getView(0, null, null);
TextView name = (TextView) view
.findViewById(R.id.text_contact_name);
TextView number = (TextView) view
.findViewById(R.id.text_contact_number);
ImageView photo = (ImageView) view
.findViewById(R.id.image_contact_photo);
//On this part you will have to test it with your own views/data
assertNotNull("View is null. ", view);
assertNotNull("Name TextView is null. ", name);
assertNotNull("Number TextView is null. ", number);
assertNotNull("Photo ImageView is null. ", photo);
assertEquals("Names doesn't match.", mJohn.getName(), name.getText());
assertEquals("Numbers doesn't match.", mJohn.getNumber(),
number.getText());
}
}
Probably you will have to test getView several times with different arguments, to test all scenarios.
Related
updated my code. My issue happens when i back out of the activity. Listview items are lost. I checked the Sqlite database and all items are saved, just not showing up again on listView when I reStart-Activity.
MainActivity
private ListView lst;
private CustomeAdapter cv;
private EditText nameEd, middleEd, lastEd;
private ArrayList<People> peopleArrayList;
private DataHelper myData;
peopleArrayList = new ArrayList<>();
OnCreate.....
public void addPerosn(View view) {
String myName = nameed.getText().toString();
String myMiddle = middleed.getText().toString();
String myLast = lasted.getText().toString();
boolean insert = myData.addData(myName, myMiddle, myLast);
if (insert == true) {
peopleArrayList.add(new People(myName, myMiddle, myLast));
cv = new CustomeAdapter(this, peopleArrayList);
lst.setAdapter(cv);
nameed.setText("");
middleed.setText("");
lasted.setText("");
} else {
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
}
}
}
My DataHelper method i want to call to Show All
public Cursor showData(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor data = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
return data;
}
Any suggestions are appreciated . Thanks
Make sure you have overridden getCount and it returns proper count.
#Override
public int getCount() {
return items.length;
}
Apart from above solution, I would recomment you to do it in proper way
a) Create a model/pojo class say Person which will have firstName,lastName and middleName
b) create a data set of Person, i.e list of person
c) create a method addPerson in adapter class, and call whenever you want to add new Person data into the list. addPerson method will also refresh the adapter by calling notifyDataSetChanged
d) In activity create adapter object only once, later on just use method of it say adapter.addPerson(person)
I am trying to apply a tutorial on a basic app i am building as a method to learn Android Studio, and part of what i am doing is having a new window ( activity ) with a ListView to view information saved in a CSV file. When i load the main app, it is fine, but when i click on the button that suppose to take me to the new window where the List view is to show the CSV content, the app stops ( shut down ).
Attached, a screen shot of the project layout, and the window where the ListView is.
Following are the codes;
This is the code from the class where the code of the ListView is written, the class name is LogerView, and there is a button at the MainActivity class, the main window for the app, that would call the LogerView;
public class LogerView extends AppCompatActivity {
CSVAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewer);
ListView mList = (ListView) findViewById(R.id.mList);
mAdapter = new CSVAdapter(this, -1);
mList.setAdapter(mAdapter);
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
Toast.makeText(v.getContext(), mAdapter.getItem(pos).getListCode(), Toast.LENGTH_SHORT).show();
}
}
);
}
public void toasting(String msg) {
Toast.makeText(LogerView.this, msg, Toast.LENGTH_SHORT).show();
}
public void switchToMain(View v) {
Intent MainActivity = new Intent(this, MainActivity.class);
startActivity(MainActivity);
}
}
This is the Code in the CSVAdapter :
public class CSVAdapter extends ArrayAdapter<fileView>{
Context ctx;
public CSVAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
this.ctx = context;
loadArrayFromFile();
}
#Override
public View getView(final int pos, View convertView, final ViewGroup parent){
TextView mView = (TextView)convertView;
if(null == mView){
mView = new TextView(parent.getContext());
mView.setTextSize(15);
}
mView.setText(getItem(pos).getListCode());
mView.setText(getItem(pos).getListNumber());
mView.setText(getItem(pos).getListRadio());
mView.setText(getItem(pos).getListCheckBox());
mView.setText(getItem(pos).getListNoteText());
mView.setText(getItem(pos).getListTime());
return mView;
}
private void loadArrayFromFile(){
try {
String FILENAME = "entry_log.csv";
InputStream is = ctx.openFileInput(FILENAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
String[] RowData = line.split("/");
if (RowData.length < 6) {
Log.e("SOME_TAG", "Invalid or empty line! . . .");
continue;
} else {
fileView cur = new fileView();
cur.setListCode(RowData[0]);
cur.setListNumber(RowData[1]);
cur.setListRadio(RowData[2]);
cur.setListCheckBox(RowData[3]);
cur.setListNoteText(RowData[4]);
cur.setListTime(RowData[5]);
this.add(cur);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I renamed the PltInfo class to fileView, and the Code is:
package net.testerapp.loger;
public class fileView {
private String listCode, listNumber, listRadio, listCheckBox, listNoteText, listTime;
public String getListCode() {
return listCode;
}
public void setListCode(String listCode) {
this.listCode = listCode;
}
public String getListNumber() {
return listNumber;
}
public void setListNumber(String listNumber) {
this.listNumber = listNumber;
}
public String getListRadio() {
return listRadio;
}
public void setListRadio(String listRadio) {
this.listRadio = listRadio;
}
public String getListCheckBox() {
return listCheckBox;
}
public void setListCheckBox(String listCheckBox) {
this.listCheckBox = listCheckBox;
}
public String getListNoteText() {
return listNoteText;
}
public void setListNoteText(String listNoteText) {
this.listNoteText = listNoteText;
}
public String getListTime() {
return listTime;
}
public void setListTime(String listTime) {
this.listTime = listTime;
}
}
The MainActivity Code is as following, and so far this one operate in a perfect way, saving info, clearing text, but when clicked on the view Button which should switch to the PltInfo class to display the ListView, is where the app shut down. ( NO CHANGES TO THIS CODE )*
My CSV file is not located in an Assets folder as the tutorial, i view it through Android Device Monitor on this path emulator/data/data/net.testerapp.loger/files/entry_log.csv
so i created a string FILENAME = entry_log.csv;
and placed it in the code after InputStream is = ctx.openFileInput(FILENAME);
not sure if this is the problem and how to solve it ?
Plus the ListView layout, is not well organized in a way where it would show all the items, i just wanted to first test it, by showing the first item as in the tutorial but did not lunch the window at all in order to go from there reorganizing the layout of the List ... any guidance on how to fix that too will be appreciated.
The link to the tutorial is : https://www.youtube.com/watch?v=S8_HnA7aLd0
** Canceld the logcat entry since i am not getting an error now
[![enter image description here][2]][2]
[![enter image description here][3]][3]
Well, i went over the steps in the Tutorial and i redid it, this time i did not get any Error, but still no response from the app, i was apple to switch to the next window activity from the MainActivity after hitting the view button, but then in hte second window, i get an empty space, without the List, i made sure the list in the layout, and the id of the list match what is in the code but not sure why it is not applying the code !?
I edited the code in the main post, to what i have right now, and attached the current screen shots of the app.
One last thing this is the viewer layout .xml file :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="235dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:id="#+id/mList" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View File"
android:id="#+id/ViewFileBtn"
android:layout_marginTop="60dp"
android:layout_gravity="center"
android:onClick="ViewFile" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Back"
android:id="#+id/BackBtn"
android:layout_gravity="center|bottom"
android:onClick="switchToMain" />
</LinearLayout>
[enter image description here][4]
I am trying to understand the problem here, and awaiting some help, here are some screen shots i took running the debugger, which i am not really familiar with, thus kind of lost trying to understand the threads.
[![enter image description here][5]][5]
[enter image description here][6]
from the exception:
Caused by: java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
at
net.testerapp.loger.CSVAdapter.loadArrayFromFile(CSVAdapter.java:103)
it says you are trying to read array item at index 1, while the array does not have an item in that position
i.e: the array length is shorter than the position you are trying to read.
to avoid this, check the RowData length before creating the object of PltInfo
in method loadArrayFromFile():
while ((line = reader.readLine()) != null) {
//Split to separate the name from the capital
String[] RowData = line.split(",");
if(RowData.legth<6){
Log.e("SOME_TAG","invalid or empty line! skipping...");
continue;
}else{
//Create a State object for this row's data.
PltInfo cur = new PltInfo();
cur.setEntryCode(RowData[0]);
cur.setEntryNum(RowData[1]);
cur.setSelected(RowData[2]);
cur.setCheckBoxText(RowData[3]);
cur.setNoteText(RowData[4]);
cur.setGetNow(RowData[5]);
//Add the State object to the ArrayList (in this case we are the ArrayList).
this.add(cur);
}
}
I believe the reason for the crash is that you are executing loadArrayFromFile on the main thread, this is a long running process as it involves opening and reading a file. try wrapping loadArrayFromFile in an AsyncTask and it's better to move it out of the Adapter class.
**
EDIT
**
You can implement the AsyncTask like this:
private class OpenFileTask extends AsyncTask<Void, Void, ArrayList<PltInfo>> {
protected Long doInBackground(Void... params) {
ArrayList<PltInfo> data=loadArrayFromFile();
return data;
}
private void loadArrayFromFile(){
try {
ArrayList<PltInfo>data=new ArrayList<PltInfo>();
// Get input stream and Buffered Reader for our data file.
String FILENAME = "entry_log.csv";
InputStream is = ctx.openFileInput(FILENAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
//Read each line
while ((line = reader.readLine()) != null) {
//Split to separate the name from the capital
String[] RowData = line.split(",");
//Create a State object for this row's data.
PltInfo cur = new PltInfo();
cur.setEntryCode(RowData[0]);
cur.setEntryNum(RowData[1]);
cur.setSelected(RowData[2]);
cur.setCheckBoxText(RowData[3]);
cur.setNoteText(RowData[4]);
cur.setGetNow(RowData[5]);
//Add the State object to the ArrayList (in this case we are the ArrayList).
data.add(cur);
}
return data;
} catch (IOException e) {
e.printStackTrace();
}
}
protected void onPostExecute(ArrayList<PltInfo> result) {
if(result!=null){
}
}
}
Then pass the data to your adapter like this:
private ArrayList<PltInfo>mData;
public CSVAdapter(Context context, int textViewResourceId,ArrayList<PltInfo> data) {
super(context, textViewResourceId);
//Store a reference to the Context so we can use it to load a file from Assets.
this.ctx = context;
mData=data;
}
then call the async task from your activity like this:
OpenFileTask task=new OpenFileTask();
task.execute();
So, Problem solved, and the CODE is sound ... the file had to be uploaded again, and the if statement as #Yazan mentioned really helped ... Thanks to Freenode #android-dev and a Heor *** Zharf ** who insisted i learn how to use the Debugger and followed with me through the problem it is clear.
Must admit, the outcome of the listview is not what expected, i just need to go over the layout and figure out what is the problem with it.
I feel like a broken record.
After many attempts, I have failed at getting a listview through Parse data to display a specific set of information.
Here is my model...this is all data from users:
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
public Midwifefirm() {
// A default constructor is required.
}
//practice name
public String getPracticeName() {
return getString("practicename");
}
public void setPracticeName(String practicename) {
put("practicename", practicename);
}
//education
public String getEducation() {
return getString("education");
}
public void setEducation(String education) {
put("education", education);
}
//years in practice
public String getYearsinPractice() {
return getString("yearsinpractice");
}
public void setYearsinPractice(String yearsinpractice) {
put("yearsinpractice", yearsinpractice);
}
//practice philosophy
public String getPracticePhilosophy() {
return getString("practicephilosophy");
}
public void setPracticePhilosophy(String practicephilosophy) {
put("practicephilosophy", practicephilosophy);
}
I have this adapter; I am wondering what to place in the query section, as I just want to pull the data into the ListView that is defined in the data model:
public class CustomMidwifeAdapter extends ParseQueryAdapter<Midwifefirm> {
public CustomMidwifeAdapter(Context context) {
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
}
#Override
public View getItemView(Midwifefirm midwifefirm, View view, ViewGroup parent) {
if (view == null) {
view = View.inflate(getContext(), R.layout.activity_midwife_result_list, null);
}
//use midwifefirm as item view/list
super.getItemView(midwifefirm, view, parent);
// find in layout the practice name
TextView titleTextView = (TextView) view.findViewById(R.id.practicename);
//in the midwifefirm data model, call getPracticename
titleTextView.setText(midwifefirm.getString("practicename"));
// Add education view
TextView EducationView = (TextView) view.findViewById(R.id.education);
EducationView.setText(midwifefirm.getString("education"));
// Add yearsexperience view
TextView ExperienceView = (TextView) view.findViewById(R.id.yearsinpractice);
ExperienceView.setText(midwifefirm.getString("yearsinpractice"));
//Add practice philosophy view
TextView PracticePhilosophyView = (TextView) view.findViewById(R.id.practicephilosophy);
PracticePhilosophyView.setText(midwifefirm.getString("practicephilosophy"));
return view;
}
}
And here is the Main Activity:
public class MidwifeResultList extends ListActivity {
private ParseQueryAdapter<ParseObject> mainAdapter;
private CustomMidwifeAdapter midwifeListAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Initialize the subclass of ParseQueryAdapter
midwifeListAdapter = new CustomMidwifeAdapter(this);
// Default view is all meals
setListAdapter(mainAdapter);
}
Every time I run this, I get no results.
Thanks in advance for any help
Michael
I can tell you why I think it fails now and I can tell you why I'm very sure it will fail after you sort out the current issue.
It seems that you're trying to use different classes
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
and
ParseQuery query = new ParseQuery("Midwives");
You need to be consistent and use the same name. Either use Midwives or Midwifefirm for both. Let's assume you picked the latter. You're also saying
all that is stored in the user table...wasn't sure if I needed to create new tables.
The query above wants to get all entries of type Midwives. If there's no such type, it'll return nothing. So you have two options:
In you Parse dashboard, reate a class Midwifefirm (don't forget to update the String inside #ParseClassName above) and store your Midwifefirm data in there. You don't need to change your query for this.
Add a column to your ParseUser class, such as type, that you can set to Midwifefirm or whatever if that user is a Midwifefirm or whatever. Then in your query you need to add:
ParseQuery query = new ParseQuery("Midwives");
query.whereEquals("type", "Midwifefirm");
I greatly prefer the former.
Anyway, once your done that, the issue is that you're not using a custom view for this. You're relying on the one provided by Android by default for ListActivity. I am fairly sure it doesn't have any of the fields you're after, so you should create a custom view for this, then at the top of onCreate in your Activity make sure you use it
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_custom_view);
By the way, the following are redundant if you populate then in getItemView anyway:
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
One final advice: if you're still having issues, set breakpoints and do some investigations first. What you need to check is:
Whether you get anything at all from Parse when you do your query. Your adapter has an useful addOnQueryLoadListener that you may use to check whether anything's been retrieved at all.
If stuff is retrieved successfully, you need to check whether the list view is populated correctly. Again, use breakpoints, this time in getItemView maybe.
I'm going to do a wild guess here using the lovely brainwrecking API help of Parse.com about ParseQueryAdapters
Before continuing, may I mind you that my experience with ParseQueryAdapters is a minimum but I think I have a basic knowledge about them + I have some experience with Parse on its own. ANYHOW,
As an example they use both these
final ParseQueryAdapter adapter = new ParseQueryAdapter(this, "Midwives");
adapter.setTextKey("name");
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
and
// Instantiate a QueryFactory to define the ParseQuery to be used for fetching items in this
// Adapter.
ParseQueryAdapter.QueryFactory<ParseObject> factory =
new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery create() {
ParseQuery query = new ParseQuery("Midwives");
return query;
}
};
// Pass the factory into the ParseQueryAdapter's constructor.
ParseQueryAdapter<ParseObject> adapter = new ParseQueryAdapter<ParseObject>(this, factory);
adapter.setTextKey("name");
// Perhaps set a callback to be fired upon successful loading of a new set of ParseObjects.
adapter.addOnQueryLoadListener(new OnQueryLoadListener<ParseObject>() {
public void onLoading() {
// Trigger any "loading" UI
}
public void onLoaded(List<ParseObject> objects, ParseException e) {
// Execute any post-loading logic, hide "loading" UI
}
});
// Attach it to your ListView, as in the example above
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
To start of, the reason why I think nothing is loading inside your list has to do with a little mixup between the initilization of your ParseQueryAdapter and your custom adapter.
You configure the basic adapter, and also initialize a custom adapter but you don't do anything with the custom adapter, tho the custom adapter seems to contain the logics to load your data model.
I think what you're looking for is something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new CustomMidwifeAdapter<ParseObject>(this);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Default view is all meals
setListAdapter(mainAdapter);
}
All you need to pass is the context (aka "this"), and the constructor of your custom class will handle the factory internal
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
Tho to be honest since you do:
new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
I wonder if you'd need to change your "QueryFactory" to
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery(MidWifefirm.class);
return query;
}
});
Where you pass a class to the the query rather than the tableName, but I could be wrong on that one.
Either way I hope this has helped in some way!
Let me start off by saying this is NOT a question about scrolling ListViews. I do not want to know how to tell when a user scrolls to the bottom of a list, so please do not give me answers for that question, or mark this as a duplicate.
I am using a class that extends AsyncTaskLoader to populate a ListView with data from a web service.
Initially, I load 50 items and everything is working great. I need to know how to tell the Loader to load the next 50 items incrementally. I understand WHERE to do this in the ListView code, but I can't figure out the best way to tell the Loader that I want to load more data without resetting it and loading everything again.
Again, to clarify, the issue I'm trying to solve here is just notifying the loader that more data needs to be loaded. It already knows how to load more data when loadInBackground() is called a second time, and the ListView already knows where/when to notify the Loader, the question is just how.
Some of the relevant code:
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
m_adapter = new SearchAdapter(getActivity());
setListAdapter(m_adapter);
// if the loader doesn't already exist, one will be created
// otherwise the existing loader is reused so we don't have
// to worry about orientation and other configuration changes
getLoaderManager().initLoader(SEARCH_LOADER_ID, null, this);
}
#Override
public Loader<List<Result>> onCreateLoader(int id, Bundle args)
{
String query = args != null ? args.getString(QUERY_KEY) : "";
return new SearchLoader(getActivity(), query);
}
private class SearchAdapter extends ArrayAdapter<Result>
{
// ...
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// ...
if (position == getCount() - 2)
// TODO: Need to notify Loader here
// ...
}
}
private static class SearchLoader extends OurAsyncTaskLoader<List<Result>>
{
public SearchLoader(Context context, String query)
{
super(context);
m_query = query;
m_data = Lists.newArrayList();
m_loadedAllResults = false;
}
#Override
public List<Result> loadInBackground()
{
if (m_loadedAllResults)
return m_data;
// the Loader implementation does a == check rather than a .equals() check
// on the data, so we need this to be a new List so that it will know we have
// new data
m_data = Lists.newArrayList(m_data);
MyWebService service = new MyWebService();
List<Result> results = service.getResults(m_query, m_data.size(), COUNT);
service.close();
if (results == null)
return null;
if (results.size() < COUNT)
m_loadedAllResults = true;
for (Result result : results)
m_data.add(result)
return m_data;
}
private static final int COUNT = 50;
private final String m_query;
private boolean m_loadedAllResults;
private List<Result> m_data;
}
I figured out a way that works. In my SearchAdapter#getView() method, I have the following code:
private class SearchAdapter extends ArrayAdapter<Result>
{
// ...
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// ...
if (position == getCount() - 2)
getLoaderManager().getLoader(SEARCH_LOADER_ID).onContentChanged();
// ...
}
}
I still would like to know if this is the "best practice" way of doing it, but it seems to solve my problem for now.
In your scenario I will recommend you to use ForceLoadContentObserver which you can bind with a URI to the ContentResolver. Like this:
class SearchLoader ....
ForceLoadContentObserver contentObserver = new ForceLoadContentObserver();
....
#Override
public void onStartLoading() {
if (cacheResult == null || takeContentChanged()) { // This will see if there's a change notification to observer and take it.
onForceLoad();
} else {
deliverResult(cacheResult);
}
}
#Override
public Result loadInBackground() {
Result result = loadResult();
// notification uri built upon Result.BASE_URI so it receives all notifications to BASE_URI.
getContext().getContentResolver().registerContentObserver(result.getNotificationUri(), true, contentObserver);
}
#Override
public void onReset() {
// ... Do your clean stuff...
getContext().getContentResolver().unregisterContentObserver(contentObserver);
}
...
}
So you can notify your change everywhere by using:
context.getContentResolver().notifyChanged(Result.BASE_URI, null);
Even when the activity holding the loader is in the background or not possible to deliverResult. And you don't need to get loaders' instances.
All the notification is around Uri of object. Uri is a powerful representation of data in Android.
But I do have my confusion as well. In this scenario, both your approach and my approach assumes a content changes means loading more data. But what if you do need to reload all the data? What kind of notification you will use?
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.