I have a BaseExpandableListAdapter class and getChildView() has to return only a couple rows. But, of course, returning null will crash the app and setting the visibility to GONE will create a blank space in the list.
So, the user has a checkbox in each row, and when one is checked the next time he opens the list it won't appear (like being deleted from the ArrayAdapter, but I can't do that).
Any good option?
EDIT
#Override
public int getChildrenCount(int groupPosition) {
int childrenCount = categories.get(groupPosition).size();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
/** If the user wants to remove the completed items */
if (settings.getBoolean("deleteCompletedItemsPref", false) == true) {
for (int childPosition=0; childPosition<childrenCount; childPosition++) {
if (categories.get(groupPosition).getItem(childPosition).isChecked()) {
parent.removeViewAt(childPosition);
}
}
}
return childrenCount;
}
Here I run the group's children and try to remove the unwanted views, although I can't remove views here because is an AdapterView.
your BaseExpandableListAdapter must have implemented a getChildrenCount() method. you can make it return the size of your category list (or maybe a copy of the list).
public int getChildrenCount(int groupPosition) {
return categories.get(groupPosition).size();
}
Then simply remove the child position if it's checked (preferably in a click listener). Of course you'd also need to adjust your data set accordingly. perhaps make it an arraylist if it isn't then remove the same position in it.
Related
I implement RecyclerView which have two ViewType. The list is dynamic and when user scroll down/up it add some items. In my RecyclerView just one of the item has different ViewType (consider this as expandable list which only one of item expand at a time).
I save position for expanded item But when new data added this position changed and I lost expanded item. Because data added in scroll down/up, updating expanded item according to size of new data is not good Idea.
One thing to mention is that I want to scroll to expanded item at first load. so I guess saving position would be best choice. (I guess but I'm not sure);
I want to know what's the efficient way to handle this issue?
http://tutorials.jenkov.com/java-collections/hashcode-equals.html
Implement hashcode and equals method, using this get the position for the expanded model object.
Example :
public class Employee {
protected long employeeId;
protected String firstName;
protected String lastName;
public boolean equals(Object o){
if(o == null) return false;
if(!(o instanceof) Employee) return false;
Employee other = (Employee) o;
if(this.employeeId != other.employeeId) return false;
if(! this.firstName.equals(other.firstName)) return false;
if(! this.lastName.equals(other.lastName)) return false;
return true;
}
public int hashCode(){
return (int) employeeId;
}
}
// To get the index of expanded item
int expandedItemIndex = EmployeeList.indexOf(expandedEmployeeModel);
recyclerView.scrollToPosition(expandedItemIndex);
Likely you use model for loading data to RecyclerView. And this model (for ex. ContactModel) contains different values for your ViewHolders.
What is point, that you use special references for that saved position. What you need to do, it's just put that position (of expanded item) to current model. And after all most works fine.
hi I am using ExpandableListview in android. My requirement is, at a time one group plus its child group must selected(using check boxes). If I go for another group then the previously selected group should get unchecked.
But From my code when I check first group automatically the last group will check. This is my issue and below is my code. Thanks in advance.
listView.setOnGroupClickListener(new OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
CheckBox last = holder.groupCHeck;
CheckBox current = (CheckBox) v
.findViewById(R.id.groupCheckBox);
last.setChecked(false);
current.setChecked(true);
if (listExpandedPosition.size() >= 1) {
if (listExpandedPosition.get(0) != groupPosition) {
listView.collapseGroupWithAnimation(listExpandedPosition
.get(0));
last.setChecked(false);
}
} else {
}
if (listView.isGroupExpanded(groupPosition)) {
listView.collapseGroupWithAnimation(groupPosition);
} else {
listView.expandGroupWithAnimation(groupPosition);
}
if (!listExpandedPosition.isEmpty())
listExpandedPosition.clear();
listExpandedPosition.add(groupPosition);
holder.groupCHeck = current;
return true;
}
});
This is a common issue that usually happens with listview,expandable listview and gridview.These widgets doesn't maintain states of checked checkbox.
Have a look at this example,you will get the idea how to deal with that...
http://lalit3686.blogspot.in/2012/06/today-i-am-going-to-show-how-to-deal.html
Make a SparseBooleanArray or Arraylist and hold Checked and Unchecked states in the array itself and set it accordingly, Or if you are using a custom Java Pojo/Object add another field for getting setting a boolean for checked/unchecked state. This is a common scenario because AdapterView recycle views and items for saving memory.
I want to filter/hide specific Items in the RecycleView if the item content matches with an preference set in SharedPreferences.
I guess I have to somehow prevent from these specific items from getting inflated in the adapter but I have no idea how.
Any ideas?
Cheers!
An adapter is the Model part of the Model-View-Controller design pattern for using ListView, GridView, and RecyclerView. So you have to think of it this way: The adapter, at any moment, has to reflect what you want to have displayed in the RecyclerView.
So here's an example: Let's say you have four items, and you want to filter the third item because it matches your preference. Your adapter's getCount() method must return 3. For getView(), position == 0 must return the first item view, position == 1 must return your second item view, and position == 2 must return your fourth item view.
It's up to your adapter code to figure out all calculations and offsets to make sure that it is always presenting a consistent state to the view. So for example, let's say you have a String array with the items, and an index dontshow pointing to the array item that shouldn't be displayed. You need to do something like this for getView():
int index = position; // position is input parameter
if (index >= dontshow) {
index++; // skip over the don't-show item
}
String item = items[index];
// now construct your view from this item
And
#Override
public int getCount() {
return items.length - 1;
}
Then when you make changes to your model, calling notifyDataSetChanged() tells the RecyclerView it has to call getCount() and getView() on your adapter all over again to redisplay the changed data.
I have an ActionBarActivity with a GridView.
The GridView has 2 columns in portrait and 3 columns in landscape.
When I select items in portrait (starting my ActionMode) and then rotate the device, the selected item highlighting shifts one item to the left. For example, if I select the second item and rotate, the first item will be highlighted. If I select the first item and rotate, no items are highlighted.
The actual selection in the code is correct, just the highlighting is wrong.
I notice it does not do this if I keep the numColumns the same for portrait and landscape.
I believe this issue started occurring after I changed my activity to an ActionBarActivity so it could be a bug..
Anyone know why or how to fix it?
I had a similar scenario and ended up solving the issue be creating a custom grid item with a boolean field to keep track of whether the item is selected or not and then highlighting the item appropriately through the custom adapter. Below is a rough outline of what I did:
(1) I created a custom grid item with a boolean field, which we will call selectedStatus for simplicity's sake. I also added the corresponding methods to my grid item class to get the selected status:
public boolean getSelectedStatus ()
{
return selectedStatus;
}
public void setSelectedStatus (boolean paramSelectedStatus)
{
this.selectedStatus = paramSelectedStatus;
}
(2) I then created a custom Adapter that extends BaseAdapter to handle the custom grid object I created. In this Adapter I check the if the selected status of the grid object is true or false and highlight the item accordingly, shown below:
#Override
public View getView (final int position, View convertView, ViewGroup parent)
{
// rest of getView() code...
if (!yourGridObject.getSelectedStatus())
{
convertView.setBackgroundColor(Color.TRANSPARENT);
}
else
{
convertView.setBackgroundColor(Color.LTGRAY);
}
// rest of getView() code...
return convertView;
}
(3) Lastly, you add the onItemClickListener to set the selected status and the background color of the grid items when they are selected (clicked):
yourGridView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
YourGridObject yourGridObject = (YourGridObject) parent.getItemAtPosition(position);
if (!yourGridObject.getSelected())
{
view.setBackgroundColor(Color.LTGRAY);
yourGridObject.setSelected(true);
}
else
{
view.setBackgroundColor(Color.TRANSPARENT);
yourGridObject.setSelected(false);
}
}
});
Implementing selection this way ensures that the highlighting (selection) of the grid items will not change when the number of columns and rows swap since the selection status is contained within the grid objects themselves.
You don't need to manually handle selection of items as suggested by Willis. Android fully supports what you are asking. I will assume you are using an ArrayAdapter however this answer would apply to all adapters. Note some adapters (like CursorAdapter) won't suffer from your posted problem and don't require the following solution because it's already doing it internally.
The problem is solved in two parts. One, the adapter must enable stable Ids. Two, your adapter must actually return stable ids. You will need to extend the ArrayAdapter or which ever adapter you are using. Then ensure you have defined the following methods as shown below.
private class MyAdapter extends ArrayAdapter<YourObjects> {
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getItemId(int position) {
//Return a unique and stable id for the given position
//While unique, Returning the position number does not count as stable.
//For example:
return getItem(position).methodThatReturnsUniqueValue();
}
}
Most adapters do not enable hasStableIds. It's primarily only used when enabling a choiceMode. Which I assume you are doing here. By returning true, you are essentially telling Android to keep track of activated (highlighted) items based on their ID value instead of their position number.
Even with stable Ids enabled, you have to actually return an ID that is unique and stable across positional changes. Since most adapters do NOT enable stable IDs, they usually only return the position number as the stable id. Technically, if an item's position never changes over time then the position number "could" be used as the stable id. However, the safest way to return a stable/unique ID is to have one assigned to the class object being stored in the adapter and pull from that.
the problem goes is that everytime I add more than one Child to a GroupItem in my ExpandableListView, the item duplicates itself with the previous child. I have narrowed it down to the getChild() method in my ExpandableListView (custom) adapter.
The getChild() method looks like this:
public Object getChild(int groupPosition, int childPosition) {
return rCollection.get(weekData.get(groupPosition)).get(childPosition);
}
Where rCollection = Map<String, List<Custom>>;
and weekData = List<String>;
How do I change the getChild to return only one item per request so I dont get duplicates? I cannot use a for-loop because if I change the groupPosition and childPosition to Arrays I will have to implement the method again.
To Illustrate, this is what the issue looks like:
How do I exclude the Item 2 from being picked up by the first time it is called, and how do I exclude item 1 from being called the second time it is called? And so on.
Thank you for reading and all the help.
#Override
public int getChildCount(.....)
{
return 2;
}
If you are sure that for every item there are only 2 subitems
Sorry my bad this is the right method you want.
EDIT
#Override
public int getChildrenCount(int groupPosition)
{
return (rCollection.get(weekdata.get(groupPosition())).size();
}
This will return the size (length) of your List<Custom> for a map.
balance paranthesis if needed
am not sure if you found a solution but here is what worked for me; since i was returning a recyclerview in the child view, just return 1 in getChildrenCount() method.
That's it
I dug it out of some old repo, I think this is what I ended up doing.
public int getChildrenCount(int groupPosition) {
// Fixes a bunch of NPE when the groupPosition = NULL. This way it checks and returns 0 when it = null.
List<MySQLWeek> group = rCollection.get(weekData.get(groupPosition));
if (group == null){
return 0;
}
return group.size();
}