I have 2 tables, Logs and Price. Content from table logs is displayed into textviews for each item. Now I would like to display some content form table Price into the same base adapter.
Is it possible and how should I done that?
This is my activity with base adapter in which i displayed content form table logs. How should I display here content form table Price?
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.display_logs_listview);
boolean sort = getIntent().getBooleanExtra("sort", false);
mainListView = (ListView) findViewById(R.id.ListViewItem);
final String place = (String) getIntent().getExtras().get("keyPlace");
dbHandler = new LogsDBHandler(this);
ArrayList<Logs> logsList = sort ? dbHandler.getAllLogsByPlace() : dbHandler.getAllLogs(place);
TextView result = (TextView) findViewById(R.id.LogMassResult);
double sum = 0.0;
for( int i=0; i<logsList.size(); i++) {
sum += logsList.get(i).getResult();
}
result.setText(String.format("%.2f", sum));
listAdapter = new LogsArrayAdapter(logsList);
mainListView.setAdapter(listAdapter);
}
private class LogsArrayAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Logs> logsList;
private List<Price> priceList;
public LogsArrayAdapter(List<Logs> logsList) {
inflater = LayoutInflater.from(DisplayLogs.this);
this.logsList = logsList;
}
#Override
public int getCount() {
return logsList.size();
}
#Override
public Object getItem(int position) {
return logsList.get(position);
}
#Override
public long getItemId(int position) {
return logsList.get(position).getId();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.activity_display_logs, parent, false);
}
Logs log = logsList.get(position);
((TextView) convertView.findViewById(R.id.textPlace)).setText(log.getPlace());
((TextView) convertView.findViewById(R.id.textNumber)).setText(log.getPlate_number());
((TextView) convertView.findViewById(R.id.textSort)).setText(log.getSort_id());
((TextView) convertView.findViewById(R.id.textGrade)).setText(log.getGrade());
((TextView) convertView.findViewById(R.id.textDiameter)).setText(log.getDiameter());
((TextView) convertView.findViewById(R.id.textLength)).setText(log.getLength());
Log.d("Value", log.getCreatedAt());
try {
Date dt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(log.getCreatedAt());
((TextView) convertView.findViewById(R.id.textDate)).setText(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(dt));
} catch (ParseException e) {
e.printStackTrace();
}
Log.d("Masa Trupca", String.format("%.2f", log.getResult()));
String final_result = String.format("%.2f", log.getResult());
((TextView) convertView.findViewById(R.id.textAmount)).setText(final_result);
return convertView;
}
}
and this is my dbQuery for getting price. I created this in my Logs class. Here I'm displaying price based on parameters in string.
public Cursor getPrice() {
Cursor cursor = db.query("Price", new String[]{"price_stump_kn", "price_stump_eur", "road_price_kn", "road_price_eur"}, "sort = ? AND grade = ? AND length = ? BETWEEN diameter_dg = ? AND diameter_gg = ?",
new String[]{getSort_id(), getGrade(), getLength(), getDiameter(), getDiameter()}, null, null, null);
if (cursor.moveToFirst()) {
do {
Price price = new Price();
price.setStumpPrice_kn(cursor.getString(0));
price.setStumpPrice_eur(cursor.getString(1));
price.setRoadPrice_kn(cursor.getString(2));
price.setRoadPrice_eur(cursor.getString(3));
} while (cursor.moveToNext());
}
return cursor;
}
So how should I display content from two tables inside one base adapter (listview)?
It depends on how the records in those two tables are related. Your remark that you included a DB query in the Logs class (which I suppose is a domain class, not a DAO), I suspect that your class structure is somewhat confusing. Therefore, I try to sketch a class structure for each of the two ways of mixing your logs and prices.
Solution A: Each log is connected to a price, and data of both are to be displayed in one item.
class LogDAO extends SQLiteOpenHelper {
...
public Log getLogs(some selection parameters) {
Get logs according to selection parameters, and for each log
call getPrice(selection parameter according to log just found)
log.setPrice(price just found)
...
Now, in your adapter, the items are Logs, and with log.getPrice() you can get the price attributes and are free to mix log and price attributes in your adapter to display them in your view item.
Solution B: There is a mixed list -- some items are logs, others are prices
The key to this is that you can dynamically decide, for each item, which layout to use in your adapter. So the structure will be:
class LogPriceDAO extends SQLiteOpenHelper {
...
public Object getLogsAndPrices(some selection parameters)
Get logs and prices in some sequence, according to your business
logic and the selection parameters (If logs and prices have some
common superclass, use that instead of Object)
...
class LogsAndPricesAdapter extends BaseAdapter {
...
#Override
public View getView (int i, View view, ViewGroup viewGroup) {
...
Object currObject = this.getItem(i); // or common superclass
...
View v;
if (currObject instanceOf Log) {
v = layoutInflater.inflate(R.layout.log_layout, null)
now fill fields of your log layout
...
} else {
if (currObject instanceOf Price) {
v = layoutInflater.inflate(R.layout.price_layout, null)
now fill fields of your price layout
...
return v
The instanceOf operator is considered bad style by some people. So the immaculate way is to define a common superclass for Log and Price that offers an operation public Boolean isLog() from which the caller can decide which type of object it got.
Related
How can I make the phone number 3456781276 which is in my phone contacts appear at the very top of my listview, and then all other contacts below that as normal? I believe I pass that value into my custom adapter and into my getView() but not at all sure how to proceed. Can you help?
In my ListView I show all my phone contacts with the following code:
class LoadContact extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids) {
// we want to delete the old selectContacts from the listview when the Activity loads
// because it may need to be updated and we want the user to see the updated listview,
// like if the user adds new names and numbers to their phone contacts.
selectPhoneContacts.clear();
// we have this here to avoid cursor errors
if (cursor != null) {
cursor.moveToFirst();
}
try {
// get a handle on the Content Resolver, so we can query the provider,
cursor = getApplicationContext().getContentResolver()
// the table to query
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
null,
null,
// display in ascending order
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
// get the column number of the Contact_ID column, make it an integer.
// I think having it stored as a number makes for faster operations later on.
// get the column number of the DISPLAY_NAME column
int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
// get the column number of the NUMBER column
int phoneNumberofContactIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
cursor.moveToFirst();
// We make a new Hashset to hold all our contact_ids, including duplicates, if they come up
Set<String> ids = new HashSet<>();
do {
System.out.println("=====>in while");
// get a handle on the display name, which is a string
name = cursor.getString(nameIdx);
// get a handle on the phone number, which is a string
phoneNumberofContact = cursor.getString(phoneNumberofContactIdx);
//----------------------------------------------------------
// get a handle on the phone number of contact, which is a string. Loop through all the phone numbers
// if our Hashset doesn't already contain the phone number string,
// then add it to the hashset
if (!ids.contains(phoneNumberofContact)) {
ids.add(phoneNumberofContact);
System.out.println(" Name--->" + name);
System.out.println(" Phone number of contact--->" + phoneNumberofContact);
SelectPhoneContact selectContact = new SelectPhoneContact();
selectContact.setName(name);
selectContact.setPhone(phoneNumberofContact);
selectPhoneContacts.add(selectContact);
}
} while (cursor.moveToNext());
} catch (Exception e) {
Toast.makeText(NewContact.this, "what the...", Toast.LENGTH_LONG).show();
e.printStackTrace();
// cursor.close();
} finally {
}
if (cursor != null) {
cursor.close();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
adapter = new SelectPhoneContactAdapter(selectPhoneContacts, NewContact.this);
// we need to notify the listview that changes may have been made on
// the background thread, doInBackground, like adding or deleting contacts,
// and these changes need to be reflected visibly in the listview. It works
// in conjunction with selectContacts.clear()
adapter.notifyDataSetChanged();
listView.setAdapter(adapter);
//this function measures the height of the listview, with all the contacts, and loads it to be that
//size. We need to do this because there's a problem with a listview in a scrollview.
justifyListViewHeightBasedOnChildren(listView);
}
}
My model, getters and setters, is like:
public class SelectPhoneContact {
String phone;
public String getPhone() {return phone;}
public void setPhone(String phone) {
this.phone = phone;
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And my custom adapter:
public class SelectPhoneContactAdapter extends BaseAdapter {
//define a list made out of SelectContacts and call it theContactsList
public List<SelectPhoneContact> theContactsList;
//define an array list made out of SelectContacts and call it arraylist
private ArrayList<SelectPhoneContact> arraylist;
Context _c;
//define a ViewHolder to hold our name and number info, instead of constantly querying
// findviewbyid. Makes the ListView run smoother
ViewHolder v;
public SelectPhoneContactAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
theContactsList = selectPhoneContacts;
_c = context;
this.arraylist = new ArrayList<SelectPhoneContact>();
this.arraylist.addAll(theContactsList);
Collections.sort(this.arraylist, new Comparator<SelectPhoneContact>() {
#Override
public int compare(SelectPhoneContact t1, SelectPhoneContact t2) {
if(t2.getPhone().equals ("3456781276")) { // put the phone number you want on top here
return 1;
} else {
return t1.getName().compareTo(t2.getName());
}
}
});
}
#Override
public int getCount() {
return arraylist.size();
}
#Override
public Object getItem(int i) {
return arraylist.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
static class ViewHolder {
// In each cell in the listview show the items you want to have
// Having a ViewHolder caches our ids, instead of having to call and load each one again and again
CheckBox checkbox;
TextView title, phone, lookup;
// CheckBox check;
}
#Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
//we're naming our convertView as view
View view = convertView;
if (view == null) {
//if there is nothing there (if it's null) inflate the layout for each row
LayoutInflater li = (LayoutInflater) _c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = li.inflate(R.layout.phone_inflate_listview, null);
//or else use the view (what we can see in each row) that is already there
} else {
view = convertView;
}
v = new ViewHolder();
// So, for example, title is cast to the name id, in phone_inflate_listview,
// phone is cast to the id called no etc
v.title = (TextView) view.findViewById(R.id.name);
// v.check = (CheckBox) view.findViewById(R.id.check);
v.phone = (TextView) view.findViewById(R.id.no);
// store the holder with the view
final SelectPhoneContact data = (SelectPhoneContact) arraylist.get(i);
v.title.setText(data.getName());
v.phone.setText(data.getPhone());
view.setTag(data);
return view;
}
}
What about using add(int index, E element)?
if (/* check your condition here: is it the number you are looking for? */) {
// insert the contact at the beginning
selectPhoneContacts.add(0, selectContact);
} else {
// insert it at the end (default)
selectPhoneContacts.add(selectContact);
}
Try to modify your adapter's constructor like this:
public SelectPhoneContactAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
theContactsList = selectPhoneContacts;
_c = context;
this.arraylist = new ArrayList<SelectPhoneContact>();
this.arraylist.addAll(theContactsList);
Collections.sort(this.arraylist, new Comparator<SelectPhoneContact>() {
#Override
public int compare(SelectPhoneContact t1, SelectPhoneContact t2) {
if(t2.getPhone().equals("3456781276")) { // put the phone number you want on top here
return 1;
} else {
return t1.getName().compareTo(t2.getName());
}
}
});
}
So we are basically sorting the ArrayList before the adapter starts using it.
So in this example, I am putting the phone number "3456781276" on top of everything else. If the phone number is NOT "3456781276", it will sort all the items by the name. (If you don't want to sort it by name, just remove the else statement.
Hope this helps.
EDIT:
in getView(), change:
final SelectPhoneContact data = (SelectPhoneContact) theContactsList.get(i);
to:
final SelectPhoneContact data = (SelectPhoneContact) arraylist.get(i);
Change getCount() method like this:
#Override
public int getCount() {
return arraylist.size();
}
Change getItem() method like this:
#Override
public Object getItem(int i) {
return arraylist.get(i);
}
You must use arraylist everywhere since that is the list we are sorting.
An easy way to achieve this, just use a view in XML which contains your phone number, and set it to invisible in default, if the list shows, set the view to be visible. I hope this post help you!!!
You can easily manipulate with items positions in ArrayList with Collections.swap(); by looping through your contacts and by simply checking is number matching your number if does put it on the top for example:
Collections.swap(myArrayList, i, 0);
Refering to: http://www.java2s.com/Code/Java/Collections-Data-Structure/SwapelementsofJavaArrayList.htm
I'm creating android app with lots of data user enters.
Now I need to display some data just like on the image below
I made first part of image, where all data is listed. Now I need to made some recapitulation where data is grouped by sort. If there are items with the same grade it needs to place them under the same row and count how many pieces of them are and also sum their masses and prices.
This is part of code where I'm displaying itemsList:
public class LogsRecapitulation extends AppCompatActivity {
private ListView mainListView;
private BaseAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_listview);
mainListView = (ListView) findViewById(R.id.ListViewItem);
//recieve RecepitID in displayLogs.activity
final long forwardedId = (long) getIntent().getExtras().get(String.valueOf("recepitID"));
List<Logs> logsList = new Select().from(Logs.class).where("Receipt = " + forwardedId).execute();
listAdapter = new RecapitulationArrayAdapter(logsList);
mainListView.setAdapter(listAdapter);
}
private class RecapitulationArrayAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Logs> logsList;
public RecapitulationArrayAdapter(List<Logs> logsList) {
inflater = LayoutInflater.from(LogsRecapitulation.this);
this.logsList = logsList;
}
#Override
public int getCount() {
return logsList.size();
}
#Override
public Object getItem(int position) {
return logsList.get(position);
}
#Override
public long getItemId(int position) {
return logsList.get(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.logs_recapitulation, parent, false);
}
Logs log = logsList.get(position);
((TextView) convertView.findViewById(R.id.rec_log_sort)).setText(log.sort_id);
((TextView) convertView.findViewById(R.id.rec_log_class)).setText(log.grade);
((TextView) convertView.findViewById(R.id.rec_log_count)).setText(String.valueOf(logsList.size()));
((TextView) convertView.findViewById(R.id.rec_logs_mass)).setText(String.format("%.2f m3", log.getM3()));
if (log.receipt.priceType.equals("Na panju")) {
((TextView) convertView.findViewById(R.id.rec_log_price_default)).setText(String.valueOf(log.price.stumpPrice_kn));
} else {
((TextView) convertView.findViewById(R.id.rec_log_price_default)).setText(String.valueOf(log.price.roadPrice_kn));
}
if (log.receipt.priceType.equals("Na panju")) {
((TextView) convertView.findViewById(R.id.rec_calculated_price)).setText(String.format("%.2f KN", log.price.stumpPrice_kn * log.getM3()));
} else {
((TextView) convertView.findViewById(R.id.rec_calculated_price)).setText(String.format("%.2f KN", log.price.roadPrice_kn * log.getM3()));
}
return convertView;
}
}
This upper code is not so important, but more important to me is to made this recapitulation and group items by sort and in each sort by grade.
Question: How should I group items by sort and in each sort group them by grade (just like on image up)?
Any help is welcome!
I don't have access to your database schema, but the SQL query you might use to produce such a result where you group by "Sorts" is as follows:
SELECT Sort, Grade, COUNT(DISTINCT ID) AS Pieces, ROUND(SUM(Mass), 2) AS
TotalMass, Price, SUM(MassPrice) AS TotalMassPrice
FROM LogsTable
GROUP BY Sort, Grade
ORDER BY Sort DESC, Grade DESC;
producing a table like the following:
There's a problem with this query if the price for a given combination of Sort-Grade is not constant. In that case you might use AVG(Price) AS AveragePrice instead of using Price, as in the example.
The "grand total" row can be produced with a SQL query as such:
SELECT COUNT(DISTINCT ID) AS TotalPieces, ROUND(SUM(Mass), 2) AS GrandTotalMass, AVG(Price) AS AveragePrice, SUM(MassPrice) AS GrandTotalMassPrice
FROM LogsTable;
producing a table like the following:
It's clear to me that the number actually being reported in the grand total row in your screenshot is not the average price, but some other metric.
You can easily convert these SQL queries to your query framework of choice. I haven't done so as you seem to demonstrate knowledge of how to do this already.
I want to make a dynamic ListView with numbered items. Here I have retrieved values from SQLite database into the ListView and actually it is numbered according to their id's which are stored in database. But I'm using a dynamic list view, so I want to show numbered items starting from 1 in each time I load the ListView. For example people booking flight tickets for different dates and flight authorities will display a final ListView for the current date including persons who booked for that date.
consider today is 10-12-2014
person A booked ticket for the date 12-12-2014, so his "id" might be "1" in database.
person B booked ticket for the date 13-12-2014,so his "id" might be "2" in database.
person c booked ticket for the date 13-12-2014,so his "id" might be "3" in database.
but when the day "13-12-2014" comes person B's id should be "1"(no need to have any connection with database, just enough a numberd representation to show today's list is this.
like
1.person B
2.person C
thats all.
This is my displayadapter class:
public class DisplayAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String> id;
private ArrayList<String>name;
private ArrayList<String>phone;
public DisplayAdapter(Context c, ArrayList<String> id,ArrayList<String> name, ArrayList<String> phone) {
this.mContext = c;
this.id = id;
this.name = name;
this.phone = phone;
}
public int getCount() {
// TODO Auto-generated method stub
return id.size();
}
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
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.viewthem, null);
mHolder = new Holder();
mHolder.txt_id = (TextView) child.findViewById(R.id.d);
mHolder.txt_name = (TextView) child.findViewById(R.id.nm);
mHolder.txt_phone = (TextView) child.findViewById(R.id.ph);
child.setTag(mHolder);
} else {
mHolder = (Holder) child.getTag();
}
mHolder.txt_id.setText(id.get(pos));
mHolder.txt_name.setText(name.get(pos));
mHolder.txt_phone.setText(phone.get(pos));
return child;
}
public class Holder {
TextView txt_id;
TextView txt_name;
TextView txt_phone;
}
}
This is my dbhelper class:
mydb = new DBhelper(this);
SQLiteDatabase database = mydb.getWritableDatabase();
Cursor mCursor=database.rawQuery("SELECT * FROM contacts WHERE dt='"+d+"'", null);
userId.clear();
user_name.clear();
user_phone.clear();
if (mCursor.moveToFirst()) {
do {
userId.add(mCursor.getString(mCursor.getColumnIndex(DBhelper.CONTACTS_COLUMN_ID)));
user_name.add(mCursor.getString(mCursor.getColumnIndex(DBhelper.CONTACTS_COLUMN_NAME)));
user_phone.add(mCursor.getString(mCursor.getColumnIndex(DBhelper.CONTACTS_COLUMN_PHONE)));
} while (mCursor.moveToNext());
}
DisplayAdapter disadpt = new DisplayAdapter(token.this,userId, user_name, user_phone);
obj.setAdapter(disadpt);
disadpt.notifyDataSetChanged();
mCursor.close();
If you want the list to simply be numbered (as you said, without regard to the actual IDs in the database) you simply need to use the pos parameter you receive in getView(). And you'd probably want to use pos + 1 so the list will be 1-based (more user friendly).
Assuming mHolder.txtId is the TextView you want to use to display the numbers, as I said in my comment, this should work:
mHolder.txt_id.setText(String.valueOf(pos + 1));
If you get an error when trying this please explain exactly what error.
I have data in an SQLite table in the following format:
id|datetime|col1|col2
1|2013-10-30 23:59:59|aaa|aab
2|2013-10-30 23:59:59|abb|aba
3|2013-10-30 23:59:59|abb|aba
4|2013-10-31 23:59:59|abb|aba
5|2013-10-31 23:59:59|abb|aba
I would like to implement an ExpandableListView so that the data would grouped by datetime and shown like that:
> 2013-10-30 23:59:59 // Group 1
1|aaa|aab
2|abb|aba
3|abb|aba
> 2013-10-31 23:59:59 // Group 2
4|abb|aba
5|abb|aba
I have a custom CursorAdapter that I can easily use to populate ListView, showing date for every single item but I don't know how to "group" the data and populate it on an ExpandableListView - could you please give me any hints?
I have a custom CursorAdapter that I can easily use to populate
ListView, showing date for every single item but I don't know how to
"group" the data and populate it on an ExpandableListView - could you
please give me any hints?
Here is solution that currently i'm using in my projects:
You cannot (shouldn't) use CursorAdapter because it's not suitable for your solution. You need to create and implement own Adapter by extending from BaseExpandableListAdapter
Then since you want to create "your own grouping" you need to change your current application logic:
You need to create collection of objects returned from database (for
demonstrating i will use name Foo)
Solution:
So your Foo object should looks like (due to your requirements, name of variables is only created to explain idea of solution) this:
public class Foo {
private String title;
private List<Data> children;
public void setChildren(List<Data> children) {
this.children = children;
}
}
Where title will be date column from your database and children will be columns for specific (unique) date.
Due to your example:
id|datetime|col1|col2
1|2013-10-30 23:59:59|aaa|aab
2|2013-10-30 23:59:59|abb|aba
3|2013-10-30 23:59:59|abb|aba
4|2013-10-31 23:59:59|abb|aba
5|2013-10-31 23:59:59|abb|aba
One specific date (title property of Object Foo) have more associated rows so this will be simulated with defined collection of children in Foo object.
So now you need in your getAll() method (method that returns data from database usually called similarly like this) of your DAO object (object that comunicates with database, it's only terminology) create Foo objects in this logic.
Since you need to properly initialise collection of children for each unique date, you need to use two select queries. Your first select will return distinct dates - so if you have in database 40 rows with 10 different (unique) dates so your select will contain 10 rows with these unique dates.
OK. Now you have "groups" for your ListView.
Now you need to create for each created "group" its children. So here is comming second select that will select all rows and with correct condition you'll assign for each "group" Foo object own collection of children.
Here is pseudo-code #1:
String query = "select * from YourTable";
Cursor c = db.rawQuery(query, null);
List<Data> childen = new ArrayList<Data>();
if (c != null && c.moveToFirst()) {
for (Foo item: collection) {
// search proper child for current item
do {
// if current row date value equals with current item datetime
if (item.getTitle().equals(c.getString(2))) {
children.add(new Data(column3, column4)); // fetch data from columns
}
} while (c.moveToNext());
// assign created children into current item
item.setChildren(children);
// reset List that will be used for next item
children = null;
children = new ArrayList<Data>();
// reset Cursor and move it to first row again
c.moveToFirst();
}
}
// finally close Cursor and database
So now your collection is "grouped" and now the remaining work is on your implemented ListAdapter - it's not tricky.
All what you need is to properly implement getGroupView() and getChildView() methods.
In "group method" you will inflate and initialise rows with titles from collection of Foo objects. These rows will become groups in ListView.
In "child method" you'll do same things but you won't inflate titles from collection but children of current Foo object from collection. These rows will become childs of one specific group.
Notes:
Due to #1. I simplified source code for demostrating purposes. But "in action" you can change a few things:
Instead of c.getString(2) for getting second column is generally
recommended to use column name so you should use
c.getColumnIndex("columnName") instead.
Is good practise to wrap source-code to try-finally block and in
finnaly block close and release used sources like cursors and
databases.
Instead of "reusing" same collection of children how in example,
you can create public method in Foo class that will add item into
collection directly (snippet of code #2).
Snippet of code #2:
public class Foo {
private String title;
private List<Data> children = new ArrayList<Data>();
public void addChild(Data child) {
this.children.add(child);
}
...
}
Summary:
Hope you understood me (i tried to to explain things in as simple way as possible, unfortunetly personal communication face to face is the best but this solution is not available for us) and also i hope that i helped you to solve your problem.
Your kind of data modeling and representation can be modeled in Java code as a LinkedHashMap<String, List<Data>>, where Data could look like:
public class Data {
private int id;
private String col1;
private String col2;
public Data(int id, String col1, String col2) {
this.id = id;
this.col1 = col1;
this.col2 = col2;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCol1() {
return col1;
}
public void setCol1(String col1) {
this.col1 = col1;
}
public String getCol2() {
return col2;
}
public void setCol2(String col2) {
this.col2 = col2;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(id).append(", ").append(col1).append(", ").append(col2);
return sb.toString();
}
}
Why LinkedHasMap? Because you need to preserve the order in which you insert the data. So your SQLite reading method could look like this:
public LinkedHashMap<String, List<Data>> readData(SQLiteDatabase db) {
LinkedHashMap<String, List<Data>> result = new LinkedHashMap<String, List<Data>>();
Cursor cursor = null;
try {
cursor = db.query("MY_TABLE", new String[] {
"datetime", "id", "col1", "col2"
}, null, null, null, null, "datetime, id ASC");
while (cursor.moveToNext()) {
String dateTime = cursor.getString(cursor.getColumnIndex("datetime"));
int id = cursor.getInt(cursor.getColumnIndex("id"));
String col1 = cursor.getString(cursor.getColumnIndex("col1"));
String col2 = cursor.getString(cursor.getColumnIndex("col2"));
List<Data> list = null;
if (result.containsKey(dateTime)) {
list = result.get(dateTime);
} else {
list = new ArrayList<Data>();
result.put(dateTime, list);
}
list.add(new Data(id, col1, col2));
}
} catch (Exception ex) {
Log.e("TAG", null, ex);
} finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
A basic adapter would look like this:
public class ExpAdapter extends BaseExpandableListAdapter {
private LinkedHashMap<String, List<Data>> input;
private LayoutInflater inflater;
public ExpAdapter(LayoutInflater inflater, LinkedHashMap<String, List<Data>> input) {
super();
this.input = input;
this.inflater = inflater;
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return getChildData(groupPosition, childPosition);
}
private Data getChildData(int groupPosition, int childPosition) {
String key = getKey(groupPosition);
List<Data> list = input.get(key);
return list.get(childPosition);
}
private String getKey(int keyPosition) {
int counter = 0;
Iterator<String> keyIterator = input.keySet().iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
if (counter++ == keyPosition) {
return key;
}
}
// will not be the case ...
return null;
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return getChildData(groupPosition, childPosition).getId();
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
TextView simpleTextView = null;
if (convertView == null) {
// inflate what you need, for testing purposes I am using android
// built-in layout
simpleTextView = (TextView) inflater.inflate(android.R.layout.simple_list_item_1,
parent, false);
} else {
simpleTextView = (TextView) convertView;
}
Data data = getChildData(groupPosition, childPosition);
simpleTextView.setText(data.toString());
return simpleTextView;
}
#Override
public int getChildrenCount(int groupPosition) {
String key = getKey(groupPosition);
return input.get(key).size();
}
#Override
public Object getGroup(int groupPosition) {
return getKey(groupPosition);
}
#Override
public int getGroupCount() {
return input.size();
}
#Override
public long getGroupId(int groupPosition) {
return 0;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
TextView simpleTextView = null;
if (convertView == null) {
// inflate what you need, for testing purposes I am using android
// built-in layout
simpleTextView = (TextView) inflater.inflate(android.R.layout.simple_list_item_1,
parent, false);
} else {
simpleTextView = (TextView) convertView;
}
simpleTextView.setText(getKey(groupPosition));
return simpleTextView;
}
#Override
public boolean hasStableIds() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
While its simple use in a basic activity would be something like this:
public class MyExpandableActivity extends FragmentActivity {
private ExpandableListView expListView;
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.expandable_layout);
expListView = (ExpandableListView) findViewById(R.id.exp_listview);
fillList();
}
private void fillList() {
LinkedHashMap<String, List<Data>> input = getMockList(); // get the collection here
ExpAdapter adapter = new ExpAdapter(LayoutInflater.from(this), input);
expListView.setAdapter(adapter);
}
}
Activity's simple layout:
<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/exp_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Makes sense?
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);