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).
Related
i have listview with BaseAdapter this is my adapter code
public class MessageAdapter1 extends BaseAdapter {
Context context;
private int auth;
private List<MessageList> mMessages;
private LayoutInflater mInflater;
public MessageAdapter1(Context context, List<MessageList> messages) {
this.mMessages = messages;
this.context = context;
this.session = new SessionManager(context);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
auth = session.getUserId();
}
#Override
public int getItemViewType(int position) {
int type = -1;
String m = mMessages.get(position).getType();
int user_idx = mMessages.get(position).getUser_id();
if(auth != user_idx && m.equals("message")) type = 1;
else if(auth == user_idx && m.equals("message")) type = 0;
else if(auth != user_idx && m.equals("image")) type = 3;
else if(auth == user_idx && m.equals("image")) type = 4;
//other else if and types .....
return type;
}
#Override
public int getViewTypeCount() {
return 19;
}
#Override
public int getCount() {
return mMessages.size();
}
#Override
public Object getItem(int i) {
return mMessages.get(i);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case MessageList.TYPE_MESSAGE_RIGHT:
convertView = mInflater.inflate(R.layout.right_message, null);
holder.groupMessage = (LinearLayout)convertView.findViewById(R.id.messages);
break;
case MessageList.TYPE_MESSAGE_LEFT:
convertView = mInflater.inflate(R.layout.left_message, null);
holder.groupMessage = (LinearLayout)convertView.findViewById(R.id.messages);
break;
case MessageList.TYPE_ACTION:
convertView = mInflater.inflate(R.layout.typing, null);
holder.Indicator = (AVLoadingIndicatorView)convertView.findViewById(R.id.indicator);
break;
//other case break for other types ..
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
setMessage(holder,position);
return convertView;
}
private void setMessage(ViewHolder holder, int position){
MessageList m = mMessages.get(position);
//holder.groupMessage.add ... data
}
public static class ViewHolder {
private LinearLayout groupMessage;
private AVLoadingIndicatorView Indicator;
//more here other
}
}
I search in google and in stackoverflow all solution talk about Override
`getViewTypeCount` and `getItemViewType` and other solution not helped me
i see my code its correct but why its repeat or reorder item when scroll on
ListView
Because your item views appear to change based on the type of items in the list, you cannot cache your viewholder.
Try removing your if (convertView == null) { and accompanying else { statement.
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!
I'm trying to add separators between my list view items, I have sorted them into date order and added list entries where the separator needs to go but as soon as it gets to a seperator it stops, no error message or anything it just doesn't add the seperator. I've added breakpoints and it definitely runs the code to add it but it doesn't show up. Even if there are other items to add after the separator it still stops at the separator.
code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater itemInflater = LayoutInflater.from(getContext());
JourneyItem singleItem = list.get(position);
if(singleItem.isSeperator()){
//set customRow to seperator layout
View customRow = itemInflater.inflate(R.layout.journey_list_seperator, parent, false);
TextView monthText = (TextView) customRow.findViewById(R.id.seperatorMonthText);
TextView yearText = (TextView) customRow.findViewById(R.id.seperatorYearText);
Date current = new Date();
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
String dtp = GeneralUtil.SQLDateFormatToHuman(list.get(position).getDepartDateTime());
try {
current = df.parse(dtp);
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat sdfDate = new SimpleDateFormat("MMMM");
monthText.setText(sdfDate.format(current));
yearText.setText(list.get(position).getDepartDateTime().substring(6,10));
return customRow;
}else {
View customRow = itemInflater.inflate(R.layout.custom_journeyitem_row, parent, false);
TextView titleText = (TextView) customRow.findViewById(R.id.titleDisplay);
TextView fromText = (TextView) customRow.findViewById(R.id.fromLocationDisplay);
TextView departText = (TextView) customRow.findViewById(R.id.departDateTimeDisplay);
TextView toText = (TextView) customRow.findViewById(R.id.toLocationDisplay);
TextView colourLbl = (TextView) customRow.findViewById(R.id.colourDisplay);
titleText.setText(singleItem.getTitle());
fromText.setText("From: " + singleItem.getFromLocation());
departText.setText(singleItem.getDepartDateTime());
toText.setText("To: " + singleItem.getToLocation());
colourLbl.setBackgroundColor(singleItem.getColourCode());
return customRow;
}
Create Coustom adapter like this.
class CustomAdapter extends BaseAdapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private ArrayList<String> mData = new ArrayList<String>();
private TreeSet<Integer> sectionHeader = new TreeSet<Integer>();
private LayoutInflater mInflater;
public CustomAdapter(Context context) {
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(final String item) {
mData.add(item);
notifyDataSetChanged();
}
public void addSectionHeaderItem(final String item) {
mData.add(item);
sectionHeader.add(mData.size() - 1);
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
return sectionHeader.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int rowType = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.snippet_item1, null);
holder.textView = (TextView) convertView.findViewById(R.id.text);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.snippet_item2, null);
holder.textView = (TextView) convertView.findViewById(R.id.textSeparator);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(mData.get(position));
return convertView;
}
public static class ViewHolder {
public TextView textView;
}
}
Complete link here.
I have a conversation mode in my application where I wish to load one layout for one user and another layout for the other. It need not always be alternating hence I cannot use a simple "%2" to achieve it.
Based on a flag I am assigning a dynamic layout, which works. My problem is that as I scroll the layouts get distorted as in, conversation of user_1 will get layout_2 or conversation of user_2 will get layout_1, absolutely random.
I did something similar to an answer I saw here:
https://stackoverflow.com/a/16774696/4810718
There were a few posts about randomized data. That is not my issue, the order of the list items does not change however the layout get's randomly applied. I read that the items in view + 1 are kept in temporary memory, regarding this another thing I noticed was: as I keep adding items such that the scrollbar comes into picture when I add a second item outside the visibility it tends to get the layout of the topmost item (first item) visible. Scrolling would later give me seemingly randomized results.
public class ConversationAdapter extends BaseAdapter
{
private LayoutInflater inflater;
private ArrayList<ConversationContent> objects;
ImageView user;
static int ind = 0;
private class ViewHolder
{
TextView textView1;
TextView textView2;
TextView textView3;
}
public ConversationAdapter(Context context, ArrayList<ConversationContent> objects)
{
inflater = LayoutInflater.from(context);
this.objects = objects;
}
public int getCount()
{
return objects.size();
}
public ConversationContent getItem(int position)
{
return objects.get(position);
}
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if(convertView == null)
{
holder = new ViewHolder();
if (Main_Page3.convFlag == 1)
{
convertView = inflater.inflate(R.layout.conversation_item_1, null);
}
else
{
convertView = inflater.inflate(R.layout.conversation_item_2, null);
}
holder.textView1 = (TextView) convertView.findViewById(R.id.trans);
holder.textView1.setTypeface(Main_Activity.fontC);
holder.textView2 = (TextView) convertView.findViewById(R.id.lang);
holder.textView2.setTypeface(Main_Activity.fontC);
holder.textView3 = (TextView) convertView.findViewById(R.id.user);
holder.textView3.setTypeface(Main_Activity.fontC);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.textView1.setText(objects.get(position).getTranslatedText());
holder.textView2.setText(objects.get(position).getTranslationString());
SpannableString originalTextString = new SpannableString("\n" + objects.get(position).getOriginalText());
originalTextString.setSpan(new RelativeSizeSpan(0.5f), 0, originalTextString.length(), 0);
holder.textView1.append(originalTextString);
holder.textView3.setText(objects.get(position).getUser());
return convertView;
}
}
So, that's the code I've written. A possible solution I thought of was if I used an array of views and loaded them accordingly, it may work? I'm really not really sure how I should be going about doing this - I'm still pretty new to Android.
I've searched a bit but could not get a helpful solution. Please direct me to a helpful solution you find or, a working answer would be most appreciable. Thank you.
I think the best way to achieve what you want is to put the flag to determine which layout to use on your ConversationContent object, then override getViewTypeCount() and getItemViewType(int position) something like this:
#Override
int getViewTypeCount() {
return 2;
}
#Override
int getItemViewType(int position) {
if (objects.get(position).isReply()) { //isReply can be whatever you want to determine whether to change layout
return 1;
}
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if(convertView == null)
{
holder = new ViewHolder();
if (getItemViewType(position) == 1)
{
convertView = inflater.inflate(R.layout.conversation_item_1, null);
}
else
{
convertView = inflater.inflate(R.layout.conversation_item_2, null);
}
holder.textView1 = (TextView) convertView.findViewById(R.id.trans);
holder.textView1.setTypeface(Main_Activity.fontC);
holder.textView2 = (TextView) convertView.findViewById(R.id.lang);
holder.textView2.setTypeface(Main_Activity.fontC);
holder.textView3 = (TextView) convertView.findViewById(R.id.user);
holder.textView3.setTypeface(Main_Activity.fontC);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.textView1.setText(objects.get(position).getTranslatedText());
holder.textView2.setText(objects.get(position).getTranslationString());
SpannableString originalTextString = new SpannableString("\n" + objects.get(position).getOriginalText());
originalTextString.setSpan(new RelativeSizeSpan(0.5f), 0, originalTextString.length(), 0);
holder.textView1.append(originalTextString);
holder.textView3.setText(objects.get(position).getUser());
return convertView;
}
For listview adapter, if you want to show different layout,
like conversion mode. you would better override the following two methods:
//set your layout type here
public int getItemViewType(int position)
{
return 0;
}
//the layout count in your adapter
public int getViewTypeCount()
{
return 0;
}
Here is an example you can refer to:
public class ChatMessageAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
private List<ChatMessage> mDatas;
public ChatMessageAdapter(Context context, List<ChatMessage> mDatas)
{
mInflater = LayoutInflater.from(context);
this.mDatas = mDatas;
}
#Override
public int getCount()
{
return mDatas.size();
}
#Override
public Object getItem(int position)
{
return mDatas.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public int getItemViewType(int position)
{
ChatMessage chatMessage = mDatas.get(position);
if (chatMessage.getType() == Type.INCOMING)
{
return 0;
}
return 1;
}
#Override
public int getViewTypeCount()
{
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ChatMessage chatMessage = mDatas.get(position);
ViewHolder viewHolder = null;
if (convertView == null)
{
if (getItemViewType(position) == 0)
{
convertView = mInflater.inflate(R.layout.item_from_msg, parent,
false);
viewHolder = new ViewHolder();
viewHolder.mDate = (TextView) convertView
.findViewById(R.id.id_form_msg_date);
viewHolder.mMsg = (TextView) convertView
.findViewById(R.id.id_from_msg_info);
} else
{
convertView = mInflater.inflate(R.layout.item_to_msg, parent,
false);
viewHolder = new ViewHolder();
viewHolder.mDate = (TextView) convertView
.findViewById(R.id.id_to_msg_date);
viewHolder.mMsg = (TextView) convertView
.findViewById(R.id.id_to_msg_info);
}
convertView.setTag(viewHolder);
} else
{
viewHolder = (ViewHolder) convertView.getTag();
}
//set data here
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
viewHolder.mDate.setText(df.format(chatMessage.getDate()));
viewHolder.mMsg.setText(chatMessage.getMsg());
return convertView;
}
private final class ViewHolder
{
TextView mDate;
TextView mMsg;
}
}
I'm trying to scroll my simple listview, but unfortunately it's not scrolling smoothly.
I can't figure out what seem to be the problem. I'm not doing anything fancy.
public class ArticleRowAdapter extends BaseAdapter {
private static final int TYPE_MAINARTICLE = 0;
private static final int TYPE_ARTICLE = 1;
private static final int TYPE_MAX_COUNT = TYPE_ARTICLE + 1;
private Context context;
private ArrayList<ArticleRow> mData = new ArrayList<ArticleRow>();
private LayoutInflater mInflater;
public ArticleRowAdapter(Context context,ArrayList<ArticleRow> data) {
this.context = context;
this.mData = data;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
static class ArticleRowHolder
{
TextView tvTitle;
ImageView ivImage;
TextView tvSubTitle;
}
public void addItem(final ArticleRow item) {
mData.add(item);
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
return position == 0 ? TYPE_MAINARTICLE : TYPE_ARTICLE;
}
#Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public ArticleRow getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ArticleRowHolder holder = null;
int type = getItemViewType(position);
if(convertView == null)
{
holder = new ArticleRowHolder();
switch(type)
{
case TYPE_MAINARTICLE:
convertView = mInflater.inflate(R.layout.mainarticle,parent, false);
break;
case TYPE_ARTICLE:
convertView = mInflater.inflate(R.layout.article,parent , false);
break;
}
holder.tvTitle = (TextView)convertView.findViewById(R.id.articleTitle);
holder.ivImage = (ImageView)convertView.findViewById(R.id.articleImage);
holder.tvSubTitle = (TextView)convertView.findViewById(R.id.articleSubTitle);
convertView.setTag(holder);
}
else
{
holder = (ArticleRowHolder)convertView.getTag();
}
ArticleRow articleRow = mData.get(position);
holder.tvTitle.setText((CharSequence)articleRow.title);
holder.tvSubTitle.setText(Html.fromHtml(articleRow.subTitle));
if(articleRow.imageURL == null)
holder.ivImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.dunk));
else
{
holder.ivImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.dunk));
}
return convertView;
}
I can say that the reason of lags are imageViews, thats for sure:
holder.ivImage = (ImageView)convertView.findViewById(R.id.articleImage);
try to not to fill imageViews with images and see what happens :). Also check google's BitmapFun and LazyList - both about images in list/grid views