I have an expandable list in android and I can fill it with string easily but now I want to add a drawable on each row of the list (a different one on each row), what's the easiest way to do this with the code I already have? Thanks.
public class Physical extends ExpandableListActivity {
public static final int GROUPS = 1;
public SimpleExpandableListAdapter expListAdapter;
private boolean expanded = true;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.phys_sub);
String [] cats = { "phy1", "phys2" };
List<Map<String, String>> groupData = new ArrayList<Map<String, String>>();
List<List<Map<String, String>>> childData = new ArrayList<List<Map<String, String>>>();
for (int i = 0; i < GROUPS; i++) {
Map<String, String> curGroupMap = new HashMap<String, String>();
groupData.add(curGroupMap);
curGroupMap.put( "group", "Categories" );
List<Map<String, String>> children = new ArrayList<Map<String, String>>();
for( int n = 0 ; n < cats.length ; n++ ) {
Map<String, String> curChildMap = new HashMap<String, String>();
curChildMap.put( "child", cats[n] );
children.add(curChildMap);
}
childData.add(children);
}
expListAdapter =
new SimpleExpandableListAdapter(
/* context */
this,
/* creates Group list */
groupData,
/*Group item layout XML */
R.layout.group_row,
/* the key of each group element (not child's) */
new String[] { "group" },
/* data under the key goes into this TextView */
new int[] { R.id.group_text },
/* creates Child List (sub-level entries) */
childData,
/* layout for children in list */
R.layout.child_sub,
/* the key of each child element */
new String[] { "child" },
/* data under the child keys go into this textView */
new int[] { R.id.child_text }
);
/* sets up and initializes the adapter for the list */
setListAdapter(expListAdapter);
getExpandableListView().setOnGroupExpandListener( new OnGroupExpandListener() {
public void onGroupExpand(int groupPosition) {
expanded = true;
}
});
getExpandableListView().setOnGroupCollapseListener( new OnGroupCollapseListener() {
public void onGroupCollapse(int groupPosition) {
expanded = false;
}
});
if (expanded != false ) {
getExpandableListView().expandGroup(0);
}
}
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, final int childPosition, long id) {
switch (childPosition) {
case 0:
Intent spine_plus_intent = new Intent(Physical.this, SpinePlus.class);
startActivity(spine_plus_intent);
finish();
break;
}
return true;
}
}
you'd define a custom expandable list adapter and redefine the getchildview / getgroupview.
From there you have 2 routes to follow: one, you can programmatically add the imageviews into the child/group view (i'd discourage you from this approach), or you define an xml layout file for the child/group view and inflate it (and possibly edit the content dynamically)
Simple adapters aren't flexible enough to achieve good results. Expecially when you want custom views for their elements. (Not to mention scalability: always project and code keeping in mind that your requirements may enlarge in the future and a flexible base allows more freedom and prevents you from scratching too much code)
Related
Hi i have one xml parsing example.here i have to add expanadable listview.how is add here.please help me.
this is my code:
public class SingleMenuItemActivity extends ExpandableListActivity {
static final String KEY_ARTIST = "payment_method";
static final String KEY_SUBTOTAL = "subtotal";
static final String KEY_DISCOUNT = "discount";
#SuppressWarnings("unchecked")
public void onCreate(Bundle savedInstanceState) {
try{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent in = getIntent();
String subtotal = in.getStringExtra(KEY_SUBTOTAL);
String discount = in.getStringExtra(KEY_DISCOUNT);
String payment_method = in.getStringExtra(KEY_ARTIST);
TextView lblSub = (TextView) findViewById(R.id.subtotal_label);
TextView lblTotal = (TextView) findViewById(R.id.total_label);
TextView lblPayment = (TextView) findViewById(R.id.payment_label);
lblSub.setText(subtotal);
lbldiscount.setText(discount);
lblPayment.setText(payment_method);
SimpleExpandableListAdapter expListAdapter =
new SimpleExpandableListAdapter(
this,
createGroupList(), // Creating group List.
R.layout.group_row, // Group item layout XML.
new String[] { "Order Info" }, // the key of group item.
new int[] { R.id.order},
// ID of each group item.-Data under the key goes into this TextView.
createChildList(), // childData describes second-level entries.
R.layout.single_list_item, // Layout for sub-level entries(second level).
new String[] {"payment_label:"}, // Keys in childData maps to display.
new int[] { R.id.payment_label} // Data under the keys above go into these TextViews.
);
setListAdapter( expListAdapter ); // setting the adapter in the list.
}catch(Exception e){
System.out.println("Errrr +++ " + e.getMessage());
}
}
/* Creating the Hashmap for the row */
#SuppressWarnings("unchecked")
private List createGroupList() {
ArrayList result = new ArrayList();
for( int i = 0 ; i < 1 ; ++i ) { // 15 groups........
HashMap m = new HashMap();
m.put( "Order Info","Order Info " + i ); // the key and it's value.
result.add( m );
}
return (List)result;
}
/* creatin the HashMap for the children */
#SuppressWarnings("unchecked")
private List createChildList() {
ArrayList result = new ArrayList();
for( int i = 0 ; i < 2 ; ++i ) { // this -15 is the number of groups(Here it's fifteen)
/* each group need each HashMap-Here for each group we have 3 subgroups */
ArrayList secList = new ArrayList();
for( int n = 0 ; n < 1 ; n++ ) {
HashMap child = new HashMap();
child.put( "payment_label", "payment_label " + n );
secList.add( child );
}
result.add( secList );
}
return result;
}
public void onContentChanged () {
System.out.println("onContentChanged");
super.onContentChanged();
}
/* This function is called on each child click */
public boolean onChildClick( ExpandableListView parent, View v, int groupPosition,int childPosition,long id) {
System.out.println("Inside onChildClick at groupPosition = " + groupPosition +" Child clicked at position " + childPosition);
return true;
}
/* This function is called on expansion of the group */
public void onGroupExpand (int groupPosition) {
try{
System.out.println("Group exapanding Listener => groupPosition = " + groupPosition);
}catch(Exception e){
System.out.println(" groupPosition Errrr +++ " + e.getMessage());
}
}
}
if i have to click particular product means it is go to next activity.the next activity have detailed description with expandable button.if i have to click the detailed discription means it is expanded and displayed payment_method,subtotal,discount otherwise it is stay in less.please help me.how is to do....How is develop my code with above application.
my main.xml file is :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ExpandableListView android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/bkg"/>
<TextView android:id="#+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="No items"/>
Here i got the output is zero...what changes is i can to do here.please help me.
check the below code snippet:
ListView lv;
lv = (ListView) findViewById(R.id.ListView01);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
gotoActivity();
}
});
start your activity in gotoactivity method using startActivityforResult method to take the values
ListViews have always been my weak point and right now I am practicing putting a Listview, within a Listview. Anyway, I first call my ListView at the start of my program and it loads it with an array saved in my strings.xml:
String[] departments = getResources().getStringArray(
R.array.departments_array);
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item,
departments));
setContentView(R.layout.main);
ListView lv = getListView();
lv.setTextFilterEnabled(true);
What I want to do is update this ListView with a new array of values each time a list item is clicked. The reason why I am trying to do it this way is because I plan on having 27 different arrays with different values for each position, and I feel it would be lighter on my resources if instead of making a ListView for each array of items, I would update this one ListView. I know I am probably not doing this the most efficient way, but if there is another way of implementing my idea please tell me.
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
switch (position) {
case 0:
try {
//It is here that i dont know what to do, I was going to call
//the Listview the same way i did previously using my setlistadapter,
//but i kept getting errors about the code being undefined
String[] listitems1 = getResources().getStringArray(
R.array.items_array);
} catch (ClassCastException e) {
Toast.makeText(getApplicationContext(), "Error",
Toast.LENGTH_SHORT).show();
}
break;
case 1:
try {
//The listview will be changed again here
} catch (ClassCastException e) {
Toast.makeText(getApplicationContext(), "Error",
Toast.LENGTH_SHORT).show();
}
break;
}
};
});
Have you thought of using a BaseAdapter and setting it as the list adapter
http://developer.android.com/reference/android/widget/BaseAdapter.html
Your approach is wrong( if I understand what are you doing). Instead of replacing the adapter of the ListView every time the user clicks(and simply setting a new adapter should work) a element in the initial list you should start a new activity passing the clicked position and in your new activity set the adapter on a ListView with the correct array based on that position.
A small example:
Main class:
/**
* The main class with the initial 27 items array.
*/
public class Class1 extends ListActivity {
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// start the second activity that will show our array depending on the
// position clicked
Intent i = new Intent(this, Class2.class);
// put the position in the Intent so we can know in the other activity
// what array to load.
i.putExtra("pos", position);
startActivity(i);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I just used a simple array of 2 items, you'll load your 27 items
// array
String[] items = { "1", "2" };
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items));
}
}
Secondary activity that will show the array based on the previously selected position:
public class Class2 extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get the Intent that started the activity
Intent i = getIntent();
// find out what position did that other activity send to us.
int position = i.getIntExtra("pos", -1);
// load the ListView with an adapter based on the array that you
// want(according to that position)
if (position == 0) {
// the first element in the main list
String[] items = getResources().getStringArray(R.array.a1);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items));
} else if (position == 1) {
// the second element in the main list
String[] items = getResources().getStringArray(R.array.a2);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items));
} else {
// etc
}
}
}
Luksprog's answer is indeed correct, and it is very useful for lists many levels deep (you do not put limits, just keep spawning new activity instances with the proper list loaded)
BUT
If your list isn't more than 2 levels deep you can use ExpandableListActivity instead of ListActivity which is basically an enhanced version of the single-level list you're using which natively handle group collapsing/expanding and therefore you do not need the spawn of a new activity for each sublevel.
again note that this approach works only for lists which do not go deeper than 2 levels
ExpandableListActivity documentation
ExpandableListView documentation
ExpandableListAdapter documentation - you should be fine with the BaseExpandableListAdapter implementation
And here you have some nice example from Google itself:
public class ExpandableList3 extends ExpandableListActivity {
private static final String NAME = "NAME";
private static final String IS_EVEN = "IS_EVEN";
private ExpandableListAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<Map<String, String>> groupData = new ArrayList<Map<String, String>>();
List<List<Map<String, String>>> childData = new ArrayList<List<Map<String, String>>>();
for (int i = 0; i < 20; i++) {
Map<String, String> curGroupMap = new HashMap<String, String>();
groupData.add(curGroupMap);
curGroupMap.put(NAME, "Group " + i);
curGroupMap.put(IS_EVEN, (i % 2 == 0) ? "This group is even" : "This group is odd");
//filling with dummy data...
List<Map<String, String>> children = new ArrayList<Map<String, String>>();
for (int j = 0; j < 15; j++) {
Map<String, String> curChildMap = new HashMap<String, String>();
children.add(curChildMap);
curChildMap.put(NAME, "Child " + j);
curChildMap.put(IS_EVEN, (j % 2 == 0) ? "This child is even" : "This child is odd");
}
childData.add(children);
}
// Set up our adapter
mAdapter = new SimpleExpandableListAdapter(
this,
groupData,
android.R.layout.simple_expandable_list_item_1,
new String[] { NAME, IS_EVEN },
new int[] { android.R.id.text1, android.R.id.text2 },
childData,
android.R.layout.simple_expandable_list_item_2,
new String[] { NAME, IS_EVEN },
new int[] { android.R.id.text1, android.R.id.text2 }
);
setListAdapter(mAdapter);
}
}
I'm writing simple filter in Android and want to use ExpandableListAdapter with check boxes.
I have no problem with creating list or checking checkbox. but I really don't know how to remember choices. After closing group and opening again or when I try to open different group checkboxes are changes.
I tried to reading on the net about that but I didn`t find how to solve my problem.
Here is my code:
public class TasksFilterDialog extends Dialog{
private static final String TAG = "Filter Dialog";
private static final String ChildID = "ChildID";
private static final String GroupID = "GroupID";
private boolean[] statusCheck;
ExpandableListView list;
SimpleExpandableListAdapter listAdapter;
public TasksFilterDialog(Context context) {
super(context);
statusCheck = new boolean[Tasks.Status.values().length];
for(int i=0; i<statusCheck.length; i++)
statusCheck[i]=true;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "Creating dialog");
this.setContentView(R.layout.tasks_filter);
list = (ExpandableListView)findViewById(R.id.filter_list);
setListAdapter(); // Setting Adapter Values to use on list
list.setAdapter(listAdapter); // Setting creating adapter to list
setOnChildClickListeners();
Log.d(TAG, "Creating dialog successfull");
}
private void setListAdapter() {
Log.d(TAG, "Setting list adapter");
listAdapter = new SimpleExpandableListAdapter(this.getContext(),
getGroups(),
R.layout.tasks_filter_groups_layout,
new String[] {GroupID},
new int[] {R.id.filter_group_text},
getChilds(),
R.layout.tasks_filter_simple_element,
new String[] {ChildID},
new int[] {R.id.filter_simple_element_text});
Log.d(TAG, "Setting list adapter successfull");
}
private List<HashMap<String, String>> getGroups() {
Log.d(TAG, "Adding groups values");
List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
HashMap<String, String> statusMap = new HashMap<String, String>();
statusMap.put(GroupID, "Status");
list.add(statusMap);
HashMap<String, String> usersMap = new HashMap<String, String>();
usersMap.put(GroupID, "Users");
list.add(usersMap);
Log.d(TAG, "Adding groups values successfull");
return list;
}
private List<List<HashMap<String, String>>> getChilds() {
Log.d(TAG, "Adding childs values");
List<List<HashMap<String, String>>> list = new ArrayList<List<HashMap<String, String>>>();
List<HashMap<String, String>> secList = new ArrayList<HashMap<String, String>>();
HashMap<String, String> map;
Tasks.Status status[] = Tasks.Status.values();
for(int i=0; i<status.length; i++) {
Log.d(TAG, "Adding child" + status[i].toString());
map = new HashMap<String, String>();
map.put(ChildID, status[i].toString());
secList.add(map);
}
list.add(secList);
list.add(secList);
Log.d(TAG, "Adding childs values succesfull");
return list;
}
private void setOnChildClickListeners() {
list.setOnChildClickListener(new OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView list, View v,
int group, int id, long arg4) {
CheckBox cb = (CheckBox) v.findViewById(R.id.filter_simple_element_check);
cb.toggle();
switch(group){
case 0:
if(statusCheck[id])
statusCheck[id]=false;
else
statusCheck[id]=true;
break;
case 1:
//TODO After getting Tasks function
}
return false;
}
});
}
}
It depends on how much you want to engineer it. A Map> would work. The Map takes a position and gives you a set of checked children. To determine if a child is checked or not, call Map.get() on group, and then see if the set contains the id of the child.
If you want to see if the second child of the third group is checked, you could do:
boolean isChecked = yourMap.get(3).contains(2);
To set a child checked:
yourMap.get(groupNum).add(childNum);
To set a child unchecked:
yourMap.get(groupNum).remove(childNum);
You would need to initialize the map to contain empty sets for every group to avoid NPEs, but thats easy to do when you first create the map. I'm sure you could come up with a cleaner way to do this, but its simple and it would work.
I'm writing a news-app and I'm having some trouble with displaying a custom list. All I want is that list items have 2 TextViews in them:
News-Title and
News-Description
These are contained in 2 static arrays: homeScreen.title[] and homeScreen.descriptionLong[].
Lower you have my code for the HashMap and the Adapter:
final static ArrayList> data = new ArrayList>();
static{
HashMap<String, String> row = new HashMap<String, String>();
for (int i = 0; i<HomeScreen.arrayLength; i++){
row.put("Title", HomeScreen.title[i]);
row.put("Description", HomeScreen.descriptionLong[i]);
data.add(row);
}
}
SimpleAdapter adapter = new SimpleAdapter(this,
data,
R.layout.mainmenu,
new String[] {"Title", "Description"},
new int[] { R.id.textView1, R.id.textView2});
setListAdapter(adapter);
}
public void onItemClick(SimpleAdapter arg0, View arg1, int position,
long id) {
selectedNews = position;
Toast.makeText(getApplicationContext(), "This is: " + selectedNews, Toast.LENGTH_LONG).show();
Intent intent = new Intent(MainMenu.this, ReadNews.class);
startActivity(intent);
}
The problem I'm having is that it only displays my 20'th (last) news information and also the default OnItemClick isn't working anymore. I'd appreciate your help...
You should put the row instantiation inside the for loop:
for (int i = 0; i<HomeScreen.arrayLength; i++){
HashMap<String, String> row = new HashMap<String, String>();
row.put("Title", HomeScreen.title[i]);
row.put("Description", HomeScreen.descriptionLong[i]);
data.add(row);
}
Update:
Your OnItemClickListener registration should look something like this:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// your code here
}
});
I am working on a ListActivity that has a inner class that is a child of SimpleAdapter that implements SectionIndexer.
class MySimpleAdapter extends SimpleAdapter implements SectionIndexer {
HashMap<String, Integer> letters;
Object[] sections;
AlphabetIndexer alphaIndexer;
public MySimpleAdapter(Context context, List<? extends Map<String, ?>> data,
int resource, String[] from, int[] to,
HashMap<String, Integer> letters, Object[] sections) {
super(context, data, resource, from, to);
this.letters = letters;
this.sections = sections;
}
#SuppressWarnings("unchecked")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.common_image_row1, null);
}
HashMap<String, Object> data = (HashMap<String, Object>) getItem(position);
Integer idArtist = Integer.parseInt((String) data.get("idArtist"));
convertView.setTag(idArtist);
((TextView) convertView.findViewById(R.id.item_name))
.setText((String) data.get("sName"));
ImageView image = (ImageView) convertView.findViewById(R.id.item_image);
image.setTag((String) data.get("sImageUrl"));
image.setImageResource(R.drawable.default_artwork);
((TextView) convertView.findViewById(R.id.item_count))
.setText((String) data.get("iSongs") + " Songs");
if (!bScrolling) {
new API.DownloadImagesTask().execute(image);
}
return convertView;
}
public int getPositionForSection(int section) {
String letter = (String) sections[section];
return letters.get(letter);
}
public int getSectionForPosition(int position) {
return 0;
}
public Object[] getSections() {
return sections;
}
}
The activity is receives a JSON object in a separate AysncTask. This object is made up of various JSONArrays whose keys are first letter of the item the items of the array. So, the array is made up of a bunch of items that begin with the letter "B". Therefore, that JSONArray's key is "B".
{
B: ["ball", "buck", "bill"]
C: ["charlie", "chuck", "chap"]
}
The object does not necessarily have all of the letters of the alphabet. I am also aware that order is not guaranteed with JSONObjects so I sort them.
List> maps = new ArrayList>();
ArrayList sections = new ArrayList();
HashMap letters = new HashMap();
String[] alphabet = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N"
,"O","P","Q","R","S","T","U","V","W","X","Y","Z"};
int k = 0;
for (int i = 0; i < alphabet.length; i++) {
if (playlistArtists.optJSONArray(alphabet[i]) != null) {
try {
JSONArray artists = playlistArtists.getJSONArray(alphabet[i]);
sections.add(alphabet[i]);
for (int j = 0; j < artists.length(); j++) {
JSONObject artist = (JSONObject) artists.get(j);
HashMap<String, String> map= new HashMap<String, String>();
map.put("idArtist", artist.getString("idArtist"));
map.put("sName", artist.getString("sName"));
map.put("sImageUrl", artist.getString("sImageUrl-thumb"));
map.put("iSongs", artist.getString("iSongs"));
maps.add(map);
letters.put(alphabet[i], k);
k++;
}
} catch (JSONException e) {
Log.d(TAG,"JSONException in Music::GetMusicCatlog.doInBackground");
e.printStackTrace();
}
}
}
SimpleAdapter adapter = new MySimpleAdapter(Catalog.this,
maps,
R.layout.common_image_row1,
new String[] {"idArtist","sName", "sImageUrl", "iSongs" },
new int[] {R.id.item_id, R.id.item_name, R.id.item_image, R.id.item_count },
letters,
sections.toArray());
setListAdapter(adapter);
The problem I am having is with FastScroll. I have everything working for the most part. The list is grouped by first letter and when using FastScroll the letter appears in the popup and goes to the correct group. The issue is when I let go of the FastScroll after going to the desired section the FastScroll "Jumps" to a random part of the list. It doesn't stay in the location where I let go of it. I think it is going to an arbitrary place in the section because I do not have the SectionIndexer implemented right. I think the problem is with this method.
public int getSectionForPosition(int position) {
return 0;
}
I am just not sure how to implement the SectionIndexer methods properly...
TL;DR First answer can't be correct. If you need a sample implementation of the getSectionForPosition method, see here.
You're right, it looks like a problem with your implementation of getSectionForPosition. The answer provided so far can't be correct because you need to return the index of the section a certain position is related to and not the position a section is related to (that is what you getPositionForSection method should do).
So you should rework your getSectionForPosition method (again). You need a valid mapping for every position to a section. Documentation says:
Given a position within the adapter, returns the index of the corresponding section within the array of section objects.
If you need an example of how to build such a mapping, see here. Implemented correctly, it will solve the problem of a jumping scrollbar and instead will position it properly.
I saw many time getSectionForPosition implemented in the same way like getPositionForSection
public int getPositionForSection(int section) {
String letter = (String) sections[section];
return letters.get(letter);
}
public int getSectionForPosition(int position) {
String letter = (String) sections[position];
return letters.get(letter);
}