setListItemChecked not highlighting all of the list items - android

Situation
I have a muliple choice mode ListView with two view types- normal and header. I load the data from the contacts app by retrieving a cursor with the names and emails and make use of the AlphabetIndexer class. My adapter extends the SimpleCursorAdapter class and implements the SectionIndexer. Moreover I override the getCount() method of my adapter so that it returns the count of the cursor + the count of the sections. In addition, my layouts are correct and items highlight in the onListItemClickListener, according to the user actions.
Problem
However, I want to highlight all the items with a Check All Button but the code fails to do that.
for (int i = 0; i <= adapter.getCount() - 1; i++) {
adapter.getItem(i);
if (adapter.getItemViewType(i) == InviteContactListAdapter.TYPE_NORMAL) {
listView.setItemChecked(i, true);
}
}
It changes correctly the layout of the items with position smaller than the cursror.getCount() and then refuses to mark the rest with a bigger index.I log the ListView.getCheckedItemPositions()
and this list includes all the items, including the ones whose layout has not been checked.
Log.d("checkedItems:", listView.getCheckedItemPositions()):
So their state is changed but not their layout.
Example
I have a list with 55 contacts and 20 section headers. When i run the Select All Button items with position 0... 55 get highlighted. Items from 56 to 75 get only get checked, not highlighted.
Code
public class InviteContactListAdapter extends SimpleCursorAdapter implements
SectionIndexer {
public static final int TYPE_HEADER = 1;
public static final int TYPE_NORMAL = 0;
public static final int TYPE_COUNT = 2;
private AlphabetIndexer indexer;
private int[] usedSectionNumbers;
private Map<Integer, Integer> sectionToPosition;
private Context context;
private HashMap<Integer, Integer> sectionToOffset;
public InviteContactListAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to, 0);
ArrayList<String> stringCollection = new ArrayList<String>();
Character firstLetter;
String firstLetterAsString;
while (c.moveToNext()) {
firstLetter = c.getString(
c.getColumnIndex(ContactsContract.Data.DISPLAY_NAME))
.charAt(0);
firstLetter = Character.toUpperCase(firstLetter);
firstLetterAsString = firstLetter.toString();
if (!stringCollection.contains(firstLetterAsString)
&& Character.isLetter(firstLetter)) {
stringCollection.add(firstLetterAsString);
}
}
Collections.sort(stringCollection);
String alphabet = " ";
for (String s : stringCollection) {
alphabet = alphabet + s;
}
c.moveToFirst();
Log.d("length", "" + alphabet.length());
this.context = context;
indexer = new AlphabetIndexer(c,
c.getColumnIndexOrThrow(ContactsContract.Data.DISPLAY_NAME),
alphabet);
sectionToPosition = new TreeMap<Integer, Integer>();
sectionToOffset = new HashMap<Integer, Integer>();
final int count = super.getCount();
int i;
for (i = count - 1; i >= 0; i--) {
sectionToPosition.put(indexer.getSectionForPosition(i), i);
}
i = 0;
usedSectionNumbers = new int[sectionToPosition.keySet().size()];
for (Integer section : sectionToPosition.keySet()) {
sectionToOffset.put(section, i);
usedSectionNumbers[i] = section;
i++;
}
for (Integer section : sectionToPosition.keySet()) {
sectionToPosition.put(section, sectionToPosition.get(section)
+ sectionToOffset.get(section));
}
Log.d("", "");
}
#Override
public int getCount() {
if (super.getCount() != 0) {
return super.getCount() + usedSectionNumbers.length;
}
return 0;
}
#Override
public Object getItem(int position) {
if (getItemViewType(position) == TYPE_NORMAL) {
return super.getItem(position
- sectionToOffset.get(getSectionForPosition(position)) - 1);
}
return null;
}
#Override
public int getPositionForSection(int section) {
if (!sectionToOffset.containsKey(section)) {
int i = 0;
int maxLength = usedSectionNumbers.length;
while (i < maxLength && section > usedSectionNumbers[i]) {
i++;
}
if (i == maxLength)
return getCount();
return indexer.getPositionForSection(usedSectionNumbers[i])
+ sectionToOffset.get(usedSectionNumbers[i]);
}
return indexer.getPositionForSection(section)
+ sectionToOffset.get(section);
}
#Override
public int getSectionForPosition(int position) {
int i = 0;
int maxLength = usedSectionNumbers.length;
while (i < maxLength
&& position >= sectionToPosition.get(usedSectionNumbers[i])) {
i++;
}
return usedSectionNumbers[i - 1];
}
#Override
public Object[] getSections() {
return indexer.getSections();
}
// nothing much to this: headers have positions that the sectionIndexer
// manages.
#Override
public int getItemViewType(int position) {
if (position == getPositionForSection(getSectionForPosition(position))) {
return TYPE_HEADER;
}
return TYPE_NORMAL;
}
#Override
public int getViewTypeCount() {
return TYPE_COUNT;
}
// return the header view, if it's in a section header position
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int type = getItemViewType(position);
LayoutInflater inflater = LayoutInflater.from(context);
if (type == TYPE_HEADER) {
if (convertView == null) {
convertView = inflater.inflate(
R.layout.list_item_alphabet_section_header, parent,
false);
}
((TextView) convertView.findViewById(R.id.letter_header))
.setText((String) getSections()[getSectionForPosition(position)]);
return convertView;
}
return super.getView(
position - sectionToOffset.get(getSectionForPosition(position))
- 1, convertView, parent);
}
// these two methods just disable the headers
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
if (getItemViewType(position) == TYPE_HEADER) {
return false;
}
return true;
}

EDIT
I think the issue might be that you are calling the super methods all over the place, which in my opinion means that the default behavior of the SimpleCursorAdapter has (which includes highlighting a selection) only applies to the first size of the cursor items.
In other words, highlighting a selected row is not a default "feature" of an Adapter, but rather something you must implement explicitly.
While the SimpleCursorAdapter has the highlighting "built-in", it only does so for a number of items equal to the Cursor's size.
I don't see how you can change this, other than managing the views yourself manually in the getView method (i.e. do the highlighting yourself by changing the background of the views).

Related

Highlight effect while pressing item in custom ListView adapter

There's a system visual effect everytime you click a view in Android. In Lollipop it's the ripple effect. When I created a ListView and associated it to an ordinary ArrayAdapter, this effect was present. Now that I've added a custom ListView, this effect is lost.
Now, I've tried to isolate what the problem is, and since using the same list item layout with a default adapter worked nicely, I would say that the problem is on my custom adapter.
I've seen many solutions related to this case that just implemented the ripple effect calling some drawables; this is not what I'm trying to do. The ripple effect shows only because I'm running the app on Android 5, now what I want to do is to have the default system highlight effect for my items when they're being clicked.
Here are the (hopefully) related pieces of my custom adapter:
public class CustomCardSetsAdapter extends BaseAdapter {
List<Card> totalList;
ArrayList<Boolean> hiddenItems;
ListView parentLV;
Integer curPosition = -1;
public static int selectedRowIndex;
public CustomCardSetsAdapter(CardSets cardList, ListView parentListView)
{
this.parentLV = parentListView;
assignSetValues(cardList);
totalList = cardList.getBlackrockMountain();
totalList.addAll(cardList.getClassic());
totalList.addAll(cardList.getCurseofNaxxramas());
totalList.addAll(cardList.getGoblinsvsGnomes());
Collections.sort(totalList,
new Comparator<Card>() {
public int compare(Card f1, Card f2) {
return f1.toString().compareTo(f2.toString());
}
});
hiddenItems = new ArrayList<>();
for (int i = 0; i < totalList.size(); i++) {
if(!totalList.get(i).getCollectible())
hiddenItems.add(true);
else
hiddenItems.add(false);
}
}
#Override
public int getCount() {
return (totalList.size() - getHiddenCount());
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
final int index = getRealPosition(position);
if(convertView == null) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
convertView = inflater.inflate(R.layout.card_list_item, parentLV, false);
}
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer prevPosition = curPosition;
curPosition = position;
if(prevPosition >= parentLV.getFirstVisiblePosition() &&
prevPosition <= parentLV.getLastVisiblePosition())
{
View view = parentLV.getChildAt(prevPosition- parentLV.getFirstVisiblePosition());
parentLV.getAdapter().getView(prevPosition,view, parentLV);
}
v.setBackgroundColor(Color.WHITE);
}
});
Card curCard = totalList.get(index);
TextView cardName = (TextView) convertView.findViewById(R.id.cardName);
cardName.setText(curCard.getName());
setRarityColor(curCard,cardName);
TextView manaCost = (TextView) convertView.findViewById(R.id.manaCost);
manaCost.setText((curCard.getCost()).toString());
ImageView setIcon = (ImageView) convertView.findViewById(R.id.setIcon);
setSetIcon(curCard,setIcon);
if(position == curPosition)
convertView.setBackgroundColor(Color.WHITE);
else
convertView.setBackgroundColor(Color.TRANSPARENT);
return convertView;
}
#Override
public int getItemViewType(int position) {
return R.layout.card_list_item;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean isEmpty() {
return false;
}
private int getHiddenCount()
{
int count = 0;
for(int i = 0;i <totalList.size();i++)
if(hiddenItems.get(i))
count++;
return count;
}
private int getRealPosition(int position) {
int hElements = getHiddenCountUpTo(position);
int diff = 0;
for(int i=0;i<hElements;i++) {
diff++;
if(hiddenItems.get(position+diff))
i--;
}
return (position + diff);
}
private int getHiddenCountUpTo(int location) {
int count = 0;
for(int i=0;i<=location;i++) {
if(hiddenItems.get(i))
count++;
}
return count;
}
}
Thanks in advance.
in your ListView XML, add:
android:drawSelectorOnTop="true"
I also think you are using your adapter wrong...
Use the ViewHolder Pattern on your Adapter:
public class CustomCardSetsAdapter extends BaseAdapter {
List<Card> totalList;
ArrayList<Boolean> hiddenItems;
ListView parentLV;
Integer curPosition = -1;
public static int selectedRowIndex;
private class ViewHolderRow{
TextView cardName;
TextView manaCost;
ImageView setIcon;
}
public CustomCardSetsAdapter(CardSets cardList, ListView parentListView)
{
this.parentLV = parentListView;
assignSetValues(cardList);
totalList = cardList.getBlackrockMountain();
totalList.addAll(cardList.getClassic());
totalList.addAll(cardList.getCurseofNaxxramas());
totalList.addAll(cardList.getGoblinsvsGnomes());
Collections.sort(totalList,
new Comparator<Card>() {
public int compare(Card f1, Card f2) {
return f1.toString().compareTo(f2.toString());
}
});
hiddenItems = new ArrayList<>();
for (int i = 0; i < totalList.size(); i++) {
if(!totalList.get(i).getCollectible())
hiddenItems.add(true);
else
hiddenItems.add(false);
}
}
#Override
public int getCount() {
return (totalList.size() - getHiddenCount());
}
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
final int index = getRealPosition(position);
ViewHolderRow theRow;
if(convertView == null) {
theRow = new ViewHolderRow();
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
convertView = inflater.inflate(R.layout.card_list_item, parentLV, false);
// Cache your views
theRow.cardName = (TextView) convertView.findViewById(R.id.cardName);
theRow.manaCost = (TextView) convertView.findViewById(R.id.manaCost);
theRow.setIcon = (ImageView) convertView.findViewById(R.id.setIcon);
// Set the Tag to the ViewHolderRow
convertView.setTag(theRow);
}else{
// get the Row to re-use
theRow = (ViewHolderRow) convertView.getTag();
}
//... Removed convertView.setOnClickListener
Card curCard = totalList.get(index);
// Set Items
theRow.cardName.setText(curCard.getName());
setRarityColor(curCard,theRow.cardName);
theRow.manaCost.setText((curCard.getCost()).toString());
setSetIcon(curCard,theRow.setIcon);
if(position == curPosition){
convertView.setBackgroundColor(Color.WHITE);
}else{
convertView.setBackgroundColor(Color.TRANSPARENT);
}
return convertView;
}
#Override
public int getItemViewType(int position) {
return R.layout.card_list_item;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean isEmpty() {
return false;
}
private int getHiddenCount()
{
int count = 0;
for(int i = 0;i <totalList.size();i++)
if(hiddenItems.get(i))
count++;
return count;
}
private int getRealPosition(int position) {
int hElements = getHiddenCountUpTo(position);
int diff = 0;
for(int i=0;i<hElements;i++) {
diff++;
if(hiddenItems.get(position+diff))
i--;
}
return (position + diff);
}
private int getHiddenCountUpTo(int location) {
int count = 0;
for(int i=0;i<=location;i++) {
if(hiddenItems.get(i))
count++;
}
return count;
}
}
Set an onListItemClickListener instead of using this on the entire convertView...
yourListView.setOnItemClickListener(ListListener);
private final OnItemClickListener ListListener = new OnItemClickListener{
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
// ... Do something on click
}
}

Using complex header layouts in listview

I have an android app that populates a listview with a custom adapter. This adapter adds sections to the listview with headers. This adapter is shown below:
public class SeparatedListAdapter extends BaseAdapter {
public final Map<String,Adapter> sections = new LinkedHashMap<String,Adapter>();
public final ArrayAdapter<String> headers;
public final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(Context context) {
headers = new ArrayAdapter<String>(context, R.layout.list_header);
}
public void addSection(String section, Adapter adapter) {
this.headers.add(section);
this.sections.put(section, adapter);
}
public Object getItem(int position) {
for(Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if(position == 0) return section;
if(position < size) return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
public int getCount() {
// total together all sections, plus one for each section header
int total = 0;
for(Adapter adapter : this.sections.values())
total += adapter.getCount() + 1;
return total;
}
public int getViewTypeCount() {
// assume that headers count as one, then total all sections
int total = 1;
for(Adapter adapter : this.sections.values())
total += adapter.getViewTypeCount();
return total;
}
public int getItemViewType(int position) {
int type = 1;
for(Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if(position == 0) return TYPE_SECTION_HEADER;
if(position < size) return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
public boolean areAllItemsSelectable() {
return false;
}
public boolean isEnabled(int position) {
return (getItemViewType(position) != TYPE_SECTION_HEADER);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int sectionnum = 0;
for(Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if(position == 0) return headers.getView(sectionnum, convertView, parent);
if(position < size) return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
#Override
public long getItemId(int position) {
return position;
}
}
Which is then called by the following code:
SeparatedListAdapter adapter;
public Map<String,?> createItem(String name) {
Map<String,String> item = new HashMap<String,String>();
item.put(ITEM_VALUE, name);
return item;
}
List<Map<String,?>> item = new LinkedList<Map<String,?>>();
item.add(createItem("value"));
adapter.addSection("Header", new SimpleAdapter(this, item, R.layout.list_item, new String[] { "value" }, new int[] { R.id.lblValue }));
listview.setAdapter(adapter);
Now until recently, my list_header xml file had a TextView root element. At this point it was working perfectly. But then I wanted to expand on the header to contain more than one view. The new root element of the list_header is now a RelativeLayout.
Now when I try and start the activity, I get the following exception: IllegalStateException: ArrayAdapter requires the resource ID to be a Text View. I understand the error but since I am relatively new to Android, I am unsure how to get around this problem. How can I modify my adapter to handle headers which contain a RelativeLayout as the root element, instead of a TextView?
Edit:
I believe the answer lies in the constructor method of the adapter?
Edit: I believe the answer lies in the constructor method of the
adapter?
Precisely, you should use this one:
headers = new ArrayAdapter<String>(context, R.layout.list_header, R.id.the_id_of_textview);
Also, have a look at this answer.

how to add separators in a listview at specific positions?

I have a ListView with custom Adapter . I have to add at position 2,6,9 separators. How to do that?
Here is my code
class MyIndexerAdapter<T> extends ArrayAdapter<T> implements SectionIndexer {
ArrayList<String> myElements;
HashMap<String, Integer> alphaIndexer;
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
TreeSet mSeparatorsSet = new TreeSet();
String[] sections;
LayoutInflater mInflater;
public MyIndexerAdapter(Context context, int textViewResourceId,
List<T> objects) {
super(context, textViewResourceId, objects);
mInflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myElements = (ArrayList<String>) objects;
// here is the tricky stuff
alphaIndexer = new HashMap<String, Integer>();
// in this hashmap we will store here the positions for
// the sections
int size = elements.size();
for (int i = size - 1; i >= 0; i--) {
String element = elements.get(i);
alphaIndexer.put(element.substring(0, 1), i);
//We store the first letter of the word, and its index.
//The Hashmap will replace the value for identical keys are putted in
}
// now we have an hashmap containing for each first-letter
// sections(key), the index(value) in where this sections begins
// we have now to build the sections(letters to be displayed)
// array .it must contains the keys, and must (I do so...) be
// ordered alphabetically
Set<String> keys = alphaIndexer.keySet(); // set of letters ...sets
// cannot be sorted...
Iterator<String> it = keys.iterator();
ArrayList<String> keyList = new ArrayList<String>(); // list can be
// sorted
while (it.hasNext()) {
String key = it.next();
keyList.add(key);
}
Collections.sort(keyList);
sections = new String[keyList.size()]; // simple conversion to an
// array of object
keyList.toArray(sections);
// ooOO00K !
}
public int getItemViewType(int position)
{
return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
public int getViewTypeCount()
{
return TYPE_MAX_COUNT;
}
public int getPositionForSection(int section) {
// Log.v("getPositionForSection", ""+section);
String letter = sections[section];
return alphaIndexer.get(letter);
}
public int getSectionForPosition(int position) {
// you will notice it will be never called (right?)
Log.v("getSectionForPosition", "called");
getSections();
return 0;
}
public Object[] getSections() {
return sections; // to string will be called each object, to display
// the letter
}
public void onListItemClick(ListView parent,View v, int position, long id)
{
Toast.makeText(getContext(), "you have selected" + elements.get(position), Toast.LENGTH_SHORT).show();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
System.out.println("getView " + position + " " + convertView + " type = " + type);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.main, null);
holder.textView = (TextView)convertView.findViewById(R.id.text);
//break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.item1, null);
holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.textView.setText(elements.get(position));
return convertView;
}
}
}
In your getView() of MyIndexerAdapter, use the position argument to check whether it is 2.6.9 and then add the separator in code
if(position==2 || position==6||position==9){
//code for adding separators to the convertview
}
Try the below completed code, it was running for me :
This code also shows how to use adapter methods like getItemViewType(), getViewTypeCount(), getView() when needed to display different types of views at different positions.
It also shows how to implement a SectionIndexer and its methods.
I added some random data at position 2,6,9 where you wanted the separators. This made things easier.
public class FastScrollActivity extends Activity {
ListView myListView;
ArrayList<String> elements;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// elements
String s = "QWERTZUIOPASDFGHJKLYXCVBNM";
Random r = new Random();
elements = new ArrayList<String>();
for (int i = 0; i < 300; i++) {
elements.add(s.substring(r.nextInt(s.length())));
}
Collections.sort(elements); // Must be sorted!
//for 2,6,9 Adding some random data--use an array of positions if you want to :
elements.add(2,"Don't care");
elements.add(6,"Don't care");
elements.add(9,"Don't care");
// listview
myListView = (ListView) findViewById(R.id.myListView);
myListView.setFastScrollEnabled(true);
myListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Toast.makeText(FastScrollActivity.this, "clicked pos = "+arg2, Toast.LENGTH_SHORT).show();
}
});
//myListView.
MyIndexerAdapter<String> adapter = new MyIndexerAdapter<String>(
this, R.layout.simple_layout,
elements);
myListView.setAdapter(adapter);
// if (myListView.getFirstVisiblePosition() > adapter.getItemId( adapter.getCount()) || myListView.getLastVisiblePosition() <= adapter.getCount()) {
// myListView.smoothScrollToPosition( adapter.getCount());}
}
class MyIndexerAdapter<T> extends ArrayAdapter<String> implements SectionIndexer {
ArrayList<String> myElements;
HashMap<String, Integer> alphaIndexer;
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
TreeSet<String> mSeparatorsSet = new TreeSet<String>();
String[] sections;
LayoutInflater mInflater;
int MytextViewResourceId;
public MyIndexerAdapter(Context context, int textViewResourceId,
ArrayList<String> objects) {
super(context, textViewResourceId, objects);
mInflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myElements = objects;
// here is the tricky stuff
alphaIndexer = new HashMap<String, Integer>();
// in this hashmap we will store here the positions for
// the sections
//Adding positions in the separator set
mSeparatorsSet.add("2");
mSeparatorsSet.add("6");
mSeparatorsSet.add("9");
int size = elements.size();
for (int i = size - 1; i >= 0; i--) {
String element = elements.get(i);
alphaIndexer.put(element.substring(0, 1), i);
//We store the first letter of the word, and its index.
//The Hashmap will replace the value for identical keys are putted in
}
// now we have an hashmap containing for each first-letter
// sections(key), the index(value) in where this sections begins
// we have now to build the sections(letters to be displayed)
// array .it must contains the keys, and must (I do so...) be
// ordered alphabetically
Set<String> keys = alphaIndexer.keySet(); // set of letters ...sets
// cannot be sorted...
Iterator<String> it = keys.iterator();
ArrayList<String> keyList = new ArrayList<String>(); // list can be
// sorted
while (it.hasNext()) {
String key = it.next();
keyList.add(key);
}
Collections.sort(keyList);
sections = new String[keyList.size()]; // simple conversion to an
// array of object
keyList.toArray(sections);
// ooOO00K !
}
public int getItemViewType(int position)
{
return mSeparatorsSet.contains(position+"") ? TYPE_SEPARATOR : TYPE_ITEM;
}
public int getViewTypeCount()
{
return TYPE_MAX_COUNT;
}
public int getPositionForSection(int section) {
// Log.v("getPositionForSection", ""+section);
String letter = sections[section];
return alphaIndexer.get(letter);
}
public int getSectionForPosition(int position) {
// you will notice it will be never called (right?)
Log.v("getSectionForPosition", "called");
getSections();
return 0;
}
public Object[] getSections() {
return sections; // to string will be called each object, to display
// the letter
}
public void onListItemClick(ListView parent,View v, int position, long id)
{
Toast.makeText(getContext(), "you have selected" + elements.get(position), Toast.LENGTH_SHORT).show();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
System.out.println("getView " + position + " " + convertView + " type = " + type);
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.simple_layout, null);
holder.textView = (TextView)convertView.findViewById(R.id.textView1);
holder.textView.setBackgroundColor(android.R.attr.colorBackground);
switch (type) {
//break;
case TYPE_SEPARATOR:
/*convertView = mInflater.inflate(R.layout.item1, null);
holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
*/
holder.textView .setOnClickListener(null);
holder.textView .setOnLongClickListener(null);
holder.textView .setLongClickable(false);
holder.textView.setFocusable(false);
holder.textView.setTextColor(Color.WHITE);
holder.textView.setBackgroundColor(Color.RED);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
if(type==TYPE_ITEM)
holder.textView.setText(elements.get(position));
//holder.textView.setMinHeight(android.R.attr.listPreferredItemHeight);
//holder.textView.setTextAppearance(getContext(), android.R.attr.textAppearanceLarge);
return convertView;
}
}
/* public void quickScroll(View v) {
String alphabet = (String)v.getTag();
int index = 0;
//find the index of the separator row view
list.setSelectionFromTop(index, 0);
}*/
public static class ViewHolder {
public TextView textView;
}
}
the xml I used is :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>
</RelativeLayout>

Changing the Adapter at runtime

I am using an adapter to display the result of a cursor on a separate screen in alphabetical order. The problem is I need to use an EditText field to filter the result.
When I do this with the filter adapter always gives error due to the index properties of each alphabetical letter which he must redo. So I thought I'd change the adapter to a simple, no alphabetical division only when the user makes the filter for the search field.
I leave the original adapter and the adapter without a filter to search I add the filter that will run another cursor. This exchange of Adapter at runtime given problem: when I change the X Adapter (without filterqueryprovider) for the Y-Adapter (with filterqueryprovider) works normal .. but when the reverse is true filterqueryprovider just running in the X Adapter and the result of the cursor does not appear. How to solve this problem?
See the code below the adapter and my Activity
Adapter:
/**
* CursorAdapter that uses an AlphabetIndexer widget to keep track of the section indicies.
* These are the positions where we want to show a section header showing the respective alphabet letter.
* #author Eric
*
*/
public class OrdemAlfabeticaAdapter extends SimpleCursorAdapter implements SectionIndexer{
private static final int TYPE_HEADER = 1;
private static final int TYPE_NORMAL = 0;
private static final int TYPE_COUNT = 2;
private AlphabetIndexer indexer;
private int[] usedSectionNumbers;
private Map<Integer, Integer> sectionToOffset;
private Map<Integer, Integer> sectionToPosition;
private Context context;
public OrdemAlfabeticaAdapter(Context context, int layout, Cursor c, String coluna,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
indexer = new AlphabetIndexer(c, c.getColumnIndexOrThrow(coluna), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
sectionToPosition = new TreeMap<Integer, Integer>(); //use a TreeMap because we are going to iterate over its keys in sorted order
sectionToOffset = new HashMap<Integer, Integer>();
final int count = super.getCount();
int i;
//temporarily have a map alphabet section to first index it appears
//(this map is going to be doing somethine else later)
for (i = count - 1 ; i >= 0; i--){
sectionToPosition.put(indexer.getSectionForPosition(i), i);
}
i = 0;
usedSectionNumbers = new int[sectionToPosition.keySet().size()];
//note that for each section that appears before a position, we must offset our
//indices by 1, to make room for an alphabetical header in our list
for (Integer section : sectionToPosition.keySet()){
sectionToOffset.put(section, i);
usedSectionNumbers[i] = section;
i++;
}
//use offset to map the alphabet sections to their actual indicies in the list
for(Integer section: sectionToPosition.keySet()){
sectionToPosition.put(section, sectionToPosition.get(section) + sectionToOffset.get(section));
}
}
#Override
public int getCount() {
if (super.getCount() != 0){
//sometimes your data set gets invalidated. In this case getCount()
//should return 0 and not our adjusted count for the headers.
//The only way to know if data is invalidated is to check if
//super.getCount() is 0.
return super.getCount() + usedSectionNumbers.length;
}
return 0;
}
#Override
public Object getItem(int position) {
if (getItemViewType(position) == TYPE_NORMAL){//we define this function in the full code later
//if the list item is not a header, then we fetch the data set item with the same position
//off-setted by the number of headers that appear before the item in the list
return super.getItem(position - sectionToOffset.get(getSectionForPosition(position)) - 1);
}
return null;
}
#Override
public int getPositionForSection(int section) {
if (! sectionToOffset.containsKey(section)){
//This is only the case when the FastScroller is scrolling,
//and so this section doesn't appear in our data set. The implementation
//of Fastscroller requires that missing sections have the same index as the
//beginning of the next non-missing section (or the end of the the list if
//if the rest of the sections are missing).
//So, in pictorial example, the sections D and E would appear at position 9
//and G to Z appear in position 11.
int i = 0;
int maxLength = usedSectionNumbers.length;
//linear scan over the sections (constant number of these) that appear in the
//data set to find the first used section that is greater than the given section, so in the
//example D and E correspond to F
while (i < maxLength && section > usedSectionNumbers[i]){
i++;
}
if (i == maxLength) return getCount(); //the given section is past all our data
return indexer.getPositionForSection(usedSectionNumbers[i]) + sectionToOffset.get(usedSectionNumbers[i]);
}
return indexer.getPositionForSection(section) + sectionToOffset.get(section);
}
#Override
public int getSectionForPosition(int position) {
int i = 0;
int maxLength = usedSectionNumbers.length;
//linear scan over the used alphabetical sections' positions
//to find where the given section fits in
while (i < maxLength && position >= sectionToPosition.get(usedSectionNumbers[i])){
i++;
}
return usedSectionNumbers[i-1];
}
#Override
public Object[] getSections() {
return indexer.getSections();
}
//nothing much to this: headers have positions that the sectionIndexer manages.
#Override
public int getItemViewType(int position) {
if (position == getPositionForSection(getSectionForPosition(position))){
return TYPE_HEADER;
}
return TYPE_NORMAL;
}
#Override
public int getViewTypeCount() {
return TYPE_COUNT;
}
//return the header view, if it's in a section header position
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int type = getItemViewType(position);
if (type == TYPE_HEADER){
if (convertView == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
// convertView = getLayoutInflater().inflate(R.layout.header, parent, false);
convertView = inflater.inflate(R.layout.cabecalho_divisao_alfabetica, parent, false);
}
((TextView)convertView.findViewById(R.id.header)).setText((String)getSections()[getSectionForPosition(position)]);
return convertView;
}
return super.getView(position - sectionToOffset.get(getSectionForPosition(position)) - 1, convertView, parent);
}
//these two methods just disable the headers
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
if (getItemViewType(position) == TYPE_HEADER){
return false;
}
return true;
}
public AlphabetIndexer getIndexer() {
return indexer;
}
}
My Activity:
public class BuscaProprietarioActivity extends ListActivity {
private SimpleCursorAdapter adapter1;
private SimpleCursorAdapter adapter2;
private EditText filterEditText;
private LinearLayout viewNenhumRegistroEncontrado;
private LinearLayout viewResultado;
private final String[] colunas = new String[] { ProprietarioProvider.Columns.ID,
ProprietarioProvider.Columns.NOME, ProprietarioProvider.Columns.TELEFONE };
private Cursor cursor;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.proprietario_busca);
filterEditText = (EditText) findViewById(R.id.busca_proprietario_campo_busca);
ContentResolver cr = getContentResolver();
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
String s = "'%" + query + "%'";
String selection = ProprietarioProvider.Columns.NOME + " LIKE " + s;
cursor = cr.query(ProprietarioProvider.CONTENT_URI, colunas, selection, null,
ProprietarioProvider.Columns.NOME + " COLLATE LOCALIZED ASC");
} else {
cursor = cr.query(ProprietarioProvider.CONTENT_URI, colunas, null, null,
ProprietarioProvider.Columns.NOME + " COLLATE LOCALIZED ASC");
}
if (cursor.moveToNext()) {
viewResultado = (LinearLayout) findViewById(R.id.busca_proprietario_resultado);
viewResultado.setVisibility(View.VISIBLE);
adapter1 = new OrdemAlfabeticaAdapter(this, R.layout.list_item_proprietario,
cursor, ProprietarioProvider.Columns.NOME, new String[]{ProprietarioProvider.Columns.NOME,
ProprietarioProvider.Columns.TELEFONE},
new int[]{R.id.list_item_proprietario_nome, R.id.list_item_proprietario_telefone});
setListAdapter(adapter1);
adapter2 = new SimpleCursorAdapter(getApplicationContext(),
R.layout.list_item_proprietario, cursor, new String[]{ProprietarioProvider.Columns.NOME,
ProprietarioProvider.Columns.TELEFONE},
new int[]{R.id.list_item_proprietario_nome, R.id.list_item_proprietario_telefone});
adapter2.setFilterQueryProvider(filterQueryProvider);
filterEditText = (EditText) findViewById(R.id.busca_proprietario_campo_busca);
filterEditText.addTextChangedListener(filterTextWatcher);
} else {
// mostra tela de registro nao encontrado
viewNenhumRegistroEncontrado = (LinearLayout) findViewById(R.id.busca_proprietario_nenhum_registro_encontrado);
viewNenhumRegistroEncontrado.setVisibility(View.VISIBLE);
}
}
private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
String selection = ProprietarioProvider.Columns.NOME + " LIKE '"+constraint+"%'";
cursor = getContentResolver().query(ProprietarioProvider.CONTENT_URI, colunas, selection, null,
ProprietarioProvider.Columns.NOME + " COLLATE LOCALIZED ASC");
return cursor;
}
};
private TextWatcher filterTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (s != null && s.length() > 0) {
setListAdapter(adapter2);
getListView().setTextFilterEnabled(true);
adapter2.getFilter().filter(s.toString());
getListView().setFastScrollEnabled(false);
} else {
setListAdapter(adapter1);
getListView().setFastScrollEnabled(true);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
};
}
If you have already set your adapter, setting it again won't update the UI. you will need to use the adapter.notifyDataSetChanged()
https://stackoverflow.com/a/7920243/563306

Android: SeperatedList (How to get address book effect?)

Hallo All,
I have a ListView, which kontains a RadioGroup and a Button in each row. The ListView works well. Now i want to add a header to the ListView, to get the effect like the address book. I fund a Java Class from the Internet, which looks like this:
package com.aiquan.android.wljs_ncre3_free;
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
public class SeparatedListAdapter extends BaseAdapter {
public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>();
public final ArrayAdapter<String> headers;
public final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(Context context) {
headers = new ArrayAdapter<String>(context, R.layout.list_header);
}
public void addSection(String sectionHeader, Adapter sectionAdapter) {
this.headers.add(sectionHeader);
this.sections.put(sectionHeader, sectionAdapter);
}
public Object getItem(int position) {
for (Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0)
return section;
if (position < size)
return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
public int getCount() {
// total together all sections, plus one for each section header
int total = 0;
for (Adapter adapter : this.sections.values())
total += adapter.getCount() + 1;
return total;
}
public int getViewTypeCount() {
// assume that headers count as one, then total all sections
int total = 1;
for (Adapter adapter : this.sections.values())
total += adapter.getViewTypeCount();
return total;
}
public int getItemViewType(int position) {
int type = 1;
for (Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0)
return TYPE_SECTION_HEADER;
if (position < size)
return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
public boolean areAllItemsSelectable() {
return false;
}
public boolean isEnabled(int position) {
return (getItemViewType(position) != TYPE_SECTION_HEADER);
}
// #Override
public View getView(int position, View convertView, ViewGroup parent) {
int sectionnum = 0;
for (Object section : this.sections.keySet()) {
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0)
return headers.getView(sectionnum, convertView, parent);
if (position < size)
return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
// #Override
public long getItemId(int position) {
return position;
}
}
I try to use this class (without any change) to solve my problem. But i got a very strange effect: the part with RadioGroup and Buttons are not displayed, but the part with Strings are displayed correctly! And i got no error messages from Eclipse. Here is my code:
SeparatedListAdapter adapter = new SeparatedListAdapter(this);
adapter.addSection("Header 1", new ArrayAdapter<String>(this,
R.layout.list_item, new String[] { "First item", "Item two" }));
adapter.addSection("Header 2", new ExerciseAdapter(this,
R.layout.sc_for_exam, n_exercises));
adapter.addSection("Header 3", new ArrayAdapter<String>(this,
R.layout.list_item, new String[] { "lala", "lolo" }));
setListAdapter(adapter);
n_exercises ist an ArrayList, which contains the List Items(each list item contains a RadioGroup and a Button). ExerciseAdapter extends from ArrayAdapter.
Now i got this effect:
Does anybody know what is wrong and how i can solve this problem? Thank you very much!!
Here is the code of ExerciseAdapter. They are in the same class like the code above.
private class ExerciseAdapter extends ArrayAdapter<Exercise_SC> {
public ExerciseAdapter(Context context, int textViewResourceId,
ArrayList<Exercise_SC> exes) {
super(context, textViewResourceId, exes);
}
public View getView(final int position, View convertView,
ViewGroup parent) {
View row = convertView;
SC_ViewWrapper wrapper;
RadioGroup rg;
if (row == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Training")) {
row = vi.inflate(R.layout.sc_for_training, parent, false);
} else {
row = vi.inflate(R.layout.sc_for_exam, parent, false);
}
wrapper = new SC_ViewWrapper(row);
row.setTag(wrapper);
rg = wrapper.getRadioGroup();
RadioGroup.OnCheckedChangeListener l = new RadioGroup.OnCheckedChangeListener() {
// #Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
Integer myPosition = (Integer) group.getTag();
Exercise_SC eRow = getExerciseRow(myPosition);
eRow.setCheckedRB(checkedId);
switch (checkedId) {
case R.id.RB_A:
n_exercises.get(myPosition).setCustomerAnswer("A");
break;
case R.id.RB_B:
n_exercises.get(myPosition).setCustomerAnswer("B");
break;
case R.id.RB_C:
n_exercises.get(myPosition).setCustomerAnswer("C");
break;
case R.id.RB_D:
n_exercises.get(myPosition).setCustomerAnswer("D");
break;
default:
n_exercises.get(myPosition).setCustomerAnswer("");
}
}
};
rg.setOnCheckedChangeListener(l);
} else {
wrapper = (SC_ViewWrapper) row.getTag();
rg = wrapper.getRadioGroup();
}
Exercise_SC myExe = getExerciseRow(position);
int size = ChooseMode_Act.size;
wrapper.getTi().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getTVExer().setText(myExe.getExerciseText());
wrapper.getTVExer().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getA().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getTVA().setText(myExe.getAnswerA());
wrapper.getTVA().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getB().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getTVB().setText(myExe.getAnswerB());
wrapper.getTVB().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getC().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getTVC().setText(myExe.getAnswerC());
wrapper.getTVC().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getD().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getTVD().setText(myExe.getAnswerD());
wrapper.getTVD().setTextSize(TypedValue.COMPLEX_UNIT_DIP, size);
wrapper.getImageView().setImageResource(myExe.getImageSrc());
if (ChooseMode_Act.modeInfo.equalsIgnoreCase("Training")) {
wrapper.getButton().setOnClickListener(new OnClickListener() {
// #Override
public void onClick(View v) {
Toast toast = Toast.makeText(
getApplicationContext(),
"题-"
+ (position + 1)
+ ": "
+ n_exercises.get(position)
.getCorrectAnswer(),
Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.show();
}
});
}
rg.setTag(new Integer(position));
rg.check(myExe.getCheckedRB());
return row;
}
}
IMO SeparatedListAdapter.getView(...) looks suspicious. Put logging on it (or debug) to study how it works and what it returns. Looks like it never returns a View from ExerciseAdapter.
UPDATE: on a second thought I realized it does call ExerciseAdapter for a View. So probably focus on what ExerciseAdapter.getView(...) returns being called from the SeparatedListAdapter.getView(...) context. Also could you post the ExerciseAdapter code?
Try this:
public int getViewTypeCount() {
// assume that headers count as one, then total all sections
//int total = 1;
//int total = 0;
int total = headers.getCount() + 1;
for (Adapter adapter : this.sections.values())
//total += adapter.getViewTypeCount();
total += (adapter.getCount() + 10);
return total;
}
Don't know why but it worked for me. I read a tip from some other question that getItemViewType() couldn't be greater than getViewTypeCount().

Categories

Resources