I am working on a project which require me to develop a autocomplete input suggestion box the problem is the suggestion items are categorized they each category is supposed to be highlighted and unclickable.
I have implemented custom ArrayAdapter for this purpose but could't figure out how to make categories un clickable
here is my custom array adapter code
public class CustomAutoCompleteAdapter extends ArrayAdapter<String> {
private static final int TYPE_ITEM = 0;
private static final int TYPE_CATEGORY = 1;
private TreeSet<Integer> sectionHeader = new TreeSet<Integer>();
private LayoutInflater mInflater;
public CustomAutoCompleteAdapter(#NonNull Context context, #LayoutRes int resource, #NonNull List<String> objects) {
super(context, resource, objects);
mInflater = LayoutInflater.from(context);
}
public int getItemViewType(int position) {
return sectionHeader.contains(position) ? TYPE_CATEGORY : TYPE_ITEM;
}
public void addHeader(String item) {
super.add(item);
sectionHeader.add(this.getCount() - 1);
}
#Override
public int getViewTypeCount() {
return 2;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){
int type = getItemViewType(position);
holder = new ViewHolder();
if(type == TYPE_ITEM){
convertView = mInflater.inflate(R.layout.item_layout,null);
holder.textView = (TextView) convertView.findViewById(R.id.text);
}
else{
convertView = mInflater.inflate(R.layout.category_layout,null);
holder.textView = (TextView) convertView.findViewById(R.id.category);
convertView.setClickable(false);
}
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(this.getItem(position));
return convertView;
}
private static class ViewHolder {
public TextView textView;
}
}
In your CustomAutoCompleteAdapter, override isEnabled() method to disable view type TYPE_CATEGORY.
Add below code in your CustomAutoCompleteAdapter :
#Override
public boolean isEnabled(int position)
{
return ((getItemViewType(position) != TYPE_CATEGORY));
}
Hope this will help~
Try to remove focus from view by setting your convertview and textview both "unfocusable()". like this:
convertView.setFocusable(false);
holder.textview.setFocusable(false);
Hope it'll work!
Related
Hello I realize there are multiple people with a similar issue but the reason for me adding this as a new question is because this is an adapter with two holders. I have a listview that has a header cell and the information cell. the error that I am encountering its
java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView.
Which only happens when I scroll the list down and then go back up. I am assuming the holder is getting destroyed and when I call the line to add text the holder is null but I am not sure. Thank you for your help in advance!
Custom Adapter:
private class MyCustomAdapter extends ArrayAdapter<sources> {
private ArrayList<sources> sources_list = new ArrayList<sources>();
private TreeSet<Integer> sectionHeader = new TreeSet<Integer>();
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private LayoutInflater vi;
public MyCustomAdapter(Context context, int textViewResourceId, ArrayList<sources> sources_list) {
super(context, textViewResourceId, sources_list);
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(final sources item) {
sources_list.add(item);
notifyDataSetChanged();
}
public void addSectionHeaderItem(final sources item) {
sources_list.add(item);
sectionHeader.add(sources_list.size() - 1);
notifyDataSetChanged();
}
public int getItemViewType(int position) {
return sectionHeader.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
public int getCount() {
return sources_list.size();
}
public int getViewTypeCount() {
return 2;
}
public sources getItem(int position) {
return sources_list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public class ViewHolder {
TextView code;
CheckBox name;
}
public class ViewHolder2 {
TextView separator;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
ViewHolder2 holder2 = null;
Log.v("ConvertView", String.valueOf(position));
int rowType = getItemViewType(position);
if (convertView == null) {
switch (rowType) {
case TYPE_ITEM:
convertView = vi.inflate(R.layout.source_cell, null);
holder = new ViewHolder();
holder.code = (TextView)
convertView.findViewById(R.id.code);
holder.name = (CheckBox)
convertView.findViewById(R.id.checkBox1);
break;
case TYPE_SEPARATOR:
holder2 = new ViewHolder2();
convertView = vi.inflate(R.layout.source_header, null);
holder2.separator = (TextView)
convertView.findViewById(R.id.separator);
break;
}
convertView.setTag(holder);
} else {
if (rowType == TYPE_ITEM) {
holder = (ViewHolder)
convertView.getTag(R.layout.source_cell);
} else {
holder2 = (ViewHolder2) convertView.getTag(R.layout.source_header);
}
}
if (rowType == TYPE_ITEM) {
sources n_source = sources_list.get(position);
holder.code.setText(n_source.getCode().toUpperCase());
holder.name.setTag(n_source);
} else {
sources n_source = sources_list.get(position);
holder2.separator.setText(n_source.getCode().toUpperCase());
}
return convertView;
}
}
Your setTag/getTag appears to be wrong. You need to use setTag(r.layout.blah, holder) to match your getTag(r.layout.blah).
I am displaying a listview in which, the row item has two textview. The second textview is for the name of companies and the first textview is for the starting letter of the companies. How this can be achieved ? Need help !!
public class ExampleAdapter extends BaseAdapter {
Context context;
ArrayList<ExamplePojo> items = new ArrayList<>();
public ExampleAdapter(Context context, ArrayList<ExamplePojo> items) {
this.context = context;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return items.indexOf(items.get(position));
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null){
convertView = inflater.inflate(R.layout.example_row_item, null);
holder = new ViewHolder();
holder.txtSentence = (TextView) convertView.findViewById(R.id.txtSentence);
holder.txtInitialLetter = (TextView) convertView.findViewById(R.id.txtInitialLetter);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
final ExamplePojo pojo = items.get(position);
holder.txtSentence.setText(pojo.getSentence());
holder.txtInitialLetter.setText(pojo.getSentence().charAt(0));
return convertView;
}
public class ViewHolder{
TextView txtSentence, txtInitialLetter;
}
}
Here is code for showing first character in first textview and complete name in other textview.Also put below lines in if(convertView== null) block
final ExamplePojo pojo = items.get(position);
holder.txtSentence.setText(pojo.getSentence());
holder.txtInitialLetter.setText(pojo.getSentence().substring(0, 1));
I was using standart android ListView with simle_list_item_multiple_choice item layout and custom adapter. It saved it's stated on pause and restored on resume. But after few days of working on I've implemented SectionIndexer, StickyListHeadersAdapter interface within my adapter and changed ListView to StickyListHeadersListView from this library. Also there were other changes in application but these are only attached to ListView. However now when my application resumes working listview is coming scrolled to begining and with all items unchecked. I've tryed to remove SectionIndexer interface and sticky headers support but it had no effect. Maybe there is some hidden options of listview which I need to enable?
(tried saveState property - no effect)
public class WordAdapter extends ArrayAdapter<Word> implements SectionIndexer, StickyListHeadersAdapter{
// -----------------------------------------------------------------------
//
// Fields
//
// -----------------------------------------------------------------------
private int mResID;
private List<Word> mList;
private String[] mSectionNames;
private StickyListHeadersListView mListView;
private int[] mSectionIndexes;
private boolean mHeadersEnabled;
// -----------------------------------------------------------------------
//
// Constructor
//
// -----------------------------------------------------------------------
public WordAdapter(Context context, StickyListHeadersListView listView, int resID, List<Word> list, String[] sectionNames, int[] sectionIndexes, boolean enableHeaders) {
super(context, resID, list);
mResID = resID;
mList = list;
mListView = listView;
mSectionIndexes = sectionIndexes;
mSectionNames = sectionNames;
mHeadersEnabled = enableHeaders;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(mResID, parent, false);
holder = new ViewHolder();
holder.root = convertView;
holder.textView = (TextView) convertView.findViewById(android.R.id.text1);
holder.checkBox = (CheckBox) convertView.findViewById(android.R.id.checkbox);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.textView.setText(getItem(position).getValue());
if(mListView.isItemChecked(position))
holder.root.setBackgroundColor(getContext().getResources().getColor(R.color.highlight));
else
holder.root.setBackgroundColor(getContext().getResources().getColor(android.R.color.transparent));
return convertView;
}
#Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
if(!mHeadersEnabled)
return new View(getContext());
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_header, parent, false);
holder.labelView = (TextView) convertView.findViewById(R.id.list_item_header_label);
convertView.setTag(holder);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
String label;
if(mSectionNames != null){
label = mSectionNames[getSectionForPosition(position)];
holder.labelView.setText(label);
}
return convertView;
}
#Override
public long getHeaderId(int position) {
long result = getSectionForPosition(position);
return result;
}
#Override
public int getPositionForSection(int section) {
return (mSectionIndexes == null || section < 0 ) ? 0 : (section == mSectionIndexes.length ? mList.size():mSectionIndexes[section]);
}
#Override
public int getSectionForPosition(int position) {
if(mSectionIndexes != null && position >= mSectionIndexes[0]) {
for(int i = 0; i < mSectionIndexes.length; ++i)
if(position < mSectionIndexes[i])
return i-1;
return (mSectionIndexes.length - 1);
}
return 0;
}
#Override
public Object[] getSections() {
return mSectionNames == null ? new Object[0] : mSectionNames;
}
// -------------------------------------------------------------------------------
//
// Internal classes
//
// -------------------------------------------------------------------------------
private static class ViewHolder {
public View root;
public TextView textView;
public CheckBox checkBox;
}
private static class HeaderViewHolder {
public TextView labelView;
}
Here is adapter code.
I have a dilemma using
if (convertview==null){
(my code)
}
or not. Without this piece of code, my listview is not very fast, it locks for a few ms sometimes and you can easy notice this in use. It just doesn't work how its meant to work.
But when this piece of code, my listitems will start recounting after a while (10 or so) and i have a few returning listitems in my list (with the header i used). I used this tutorial for getting my listview with sections link. The length of the list is good.
Ofcourse my list is totally useless with a view repeating items (nicely sectioned by the way) but i also dont want it to be slow.
Does anyone know what to do?
Below is my adapter:
public class DelftAdapter extends BaseAdapter {
private Activity activity;
private List<ListItem> listItems;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
private final int[] bgColors = new int[] { R.color.list_odd, R.color.list_even };
public DelftAdapter(Activity a, ArrayList<ListItem> li) {
activity = a;
listItems = li;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return listItems.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final ListItem li = listItems.get(position);
if (li != null) {
if(li.isSection()){ // is sectionheader
SectionItem si = (SectionItem)li;
vi = inflater.inflate(R.layout.sectionedlistitem, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
final TextView sectionView = (TextView) vi.findViewById(R.id.list_header_title);
sectionView.setText(si.getTitle());
}else{ // no sectionheader
ListData ld = (ListData)li;
vi = inflater.inflate(R.layout.singlelistitem, null);
TextView tvNames=(TextView)vi.findViewById(R.id.tvname);
TextView tvTip=(TextView)vi.findViewById(R.id.tvtip);
ImageView image=(ImageView)vi.findViewById(R.id.image);
tvNames.setText(ld.name);
tvTip.setText(ld.tip);
if (listItems.get(position) != null ){
imageLoader.DisplayImage(ld.photoUrl, image);
}
else{
image.setImageURI(Uri.fromFile(new File("//assets/eten.png")));
}
// alternating colors
int colorPos = position % bgColors.length;
vi.setBackgroundResource(bgColors[colorPos]);
}
}
return vi;
}
}
Consider using getItemViewType() and getViewTypeCount() with recycling convertView. Those are used when you have list items with various layouts. You definitely should recycle convertView.
See also http://android.amberfog.com/?p=296
In your case:
private static final int TYPE_ITEM = 0;
private static final int TYPE_SECTION = 1;
#Override
public int getItemViewType(int position) {
return listItems.get(position).isSection() ? TYPE_SECTION : TYPE_ITEM
}
#Override
public int getViewTypeCount() {
return 2; // sectionheader and regular item
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
if (convertView == null) {
switch (type) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.singlelistitem, null);
...
break;
case TYPE_SECTION:
convertView = mInflater.inflate(R.layout.sectionedlistitem, null);
...
break;
}
} else {
...
}
return convertView;
}
Also use ViewHolder pattern to achieve better performance.
Simple but little tricky, if I have
list.setAdapter(new ArrayAdapter<String>(this,R.layout.double_row, R.id.doubleRow, articleItemsHelper));
it works if articleItemsHelper is String, but I wanna have HTML formatting in there so when articleItemsHelper is type Spanned this (adapter) doesn't work.
ArrayList<Spanned> articleItemsHelper = new ArrayList<Spanned>();
What's the solution?
EDIT: here is the solution - custom adapter
private static class SpannedAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<Spanned> mArticleList;
public SpannedAdapter(Context context, ArrayList<Spanned> articleList) {
mInflater = LayoutInflater.from(context);
mArticleList = articleList;
}
public int getCount() {
return mArticleList.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.single_row, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.singleRow);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(mArticleList.get(position));
return convertView;
}
static class ViewHolder {
TextView text;
}
}
Then just regularly call
list.setAdapter(new SpannedAdapter(this, articleItemsHelper));
where
articleItemsHelper
is
ArrayList<Spanned>
Old thread but I found an other way to do it, it can help people:
simpleAdpt = new ArrayAdapter<Note>(this, R.layout.notelist, listeNotes ){
public View getView(int position, View view, ViewGroup viewGroup)
{
View v = super.getView(position, view, viewGroup);
Note n = (Note) this.getItem(position);
((TextView)v).setText(Html.fromHtml("<b>"+n.getTitre() + "</b> <br/>"+n.getNote()));
return v;
}
};
This is how ArrayAdapter set the text of the rows:
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
}
As you can see, what it would do in your case is to call the toString method, and that's why it does not work. So, go ahead and write your own adapter, you have no choice in this case.