nullPointer when findViewById() in SimpleCursorAdapter - android

I was using SimpleCursorAdapter with an xml file with some views defined in it:
<LinearLayout ...>
<ImageView android:id="#+id/listIcon" />
<TextView android:id="#+id/listText" />
</LinearLayout>
My aim was to set the text color of the TextView, and the background color of the LinearLayout (that is, each row in the ListView) programmatically; the color is returned from a database.
I was getting NPEs when trying to manipulate the TextView for example, after it had found it with no complaints:
TextView tv = (TextView) findViewById(R.id.listText);
tv.setTextColor(color); // NPE on this line
Which is fair; if there's multiple entries in the list, it's reasonable to assume that "R.id.listText" will not work. So I extended SimpleCursor Adapter:
public View getView(int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
TextView text = (TextView) row.findViewById(R.id.listText);
// ImageView icon = (ImageView) row.findViewById(R.id.listIcon);
// If there's an icon defined
if (mIcon_id != 0) {
// icon.setImageResource(mIcon_id);
}
// If text color defined
if (mTextColor != 0) {
text.setTextColor(mTextColor);
}
// If background color set
if (mBackgroundColor != 0) {
row.setBackgroundColor(mBackgroundColor);
}
return(row);
}
And I get two different errors:
A similar NPE is thrown at
"text.setTextColor(mTextColor)"
If the lines with the ImageView are
uncommented, I get a
"ClassCastException:
android.widget.TextView" where I am
calling
"row.findViewById(R.id.listIcon)"
For reference, I was trying to use Commonsware's sample code, applying it to my situation. link (pdf)
Changed to this:
public View getView(int position, View convertView, ViewGroup parent) {
convertView = super.getView(position, convertView, parent);
if (convertView == null) convertView = View.inflate(mContext, R.layout.theme_item, null);
TextView text = (TextView) convertView.findViewById(R.id.listText_tv);
ImageView icon = (ImageView) convertView.findViewById(R.id.listIcon_iv);
// If there's an icon defined
if (mIcon_id != 0) {
icon.setImageResource(mIcon_id);
}
// If text color defined
if (mTextColor != 0) {
text.setTextColor(mTextColor);
}
// If background color set
if (mBackgroundColor != 0) {
convertView.setBackgroundColor(mBackgroundColor);
}
bindView(convertView, mContext, mCursor);
return(convertView);
}
Now I get a ClassCastException in the next activity (on list item click). Nothing has been modified in the next activity; it worked when using a SimpleListAdapter for the list which had entries (upon which clicking would lead to Activity2), so I think it's still something I'm doing wrong in the this extended class.

It's not true that convertView will always be an existing instance; you should check if it's null and then instantiate it. If not, you can change it just as you did.
This should be like:
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null)
convertView = //inflate your row here
View row = convertView;
//Manipulate the row here
return(row);
}

I would modify the getView method:
public View getView(int position, View convertView, ViewGroup parent) {
convertView = View.inflate(getContext(), R.layout.myLayout, null);
TextView text = (TextView) convertView.findViewById(R.id.listText);
ImageView icon = (ImageView) convertView.findViewById(R.id.listIcon);
// If there's an icon defined
if (mIcon_id != 0) {
icon.setImageResource(mIcon_id);
}
// If text color defined
if (mTextColor != 0) {
text.setTextColor(mTextColor);
}
// If background color set
if (mBackgroundColor != 0) {
convertView.setBackgroundColor(mBackgroundColor);
}
return convertView;
}

I think that you're getting NPE because you're trying to create a textview and an imageview in a view where they aren't there.
When you want inflate a ListView with entries from a database, in your activity you define main.xml whith a ListView:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView1">
</ListView>
and in the onCreate method you set the view to this xml with setContentView(R.layout.main);. Then you create your cursor to your database and your custom adapter:
MySimpleCursorAdapter adapter = new MySimpleCursorAdapter(this, R.layout.entry,
names, new String[] {Phones.NAME, Phones.NUMBER}, new int[] {
R.id.listIcon, R.id.listText});
startManagingCursor(cursor);
ListView listView = (ListView) findViewById(R.id.listView1);
listView.setAdapter(adapter);
and you define an entry.xml with your listIcon and listText, where the adapter points. In my example, I'm querying the names and numbers from contact list.
In your custom adapter, you should access to your textview and imageview inside getView or bindView without any problem.
Here you have and example to get all the contacts in your contact list with its picture, name and number, but using ListActivity instead of activity, and only one xml with two text views and an imageview. If you use ListActivity you don't need to use a ListView and you don't need to set the content view in the activity.
I hope it helps!

Don't forget to put : layout_width and layout_heigth for each of your views .

Related

How to write a 3 line list in android

http://developer.android.com/design/media/lists_main.png
As you can see above, I would like to create the 3-line list.
I have looked everywhere but I can't seem to find any documentation on how to do it.
How do I write my adapter for this kind of ListView?
Do I have to extend ListActivity, include a ListView in my .xml layout, or both?
Can somebody provide further insight into it? I'll be very grateful...
You create an XML-Layout for your list-item (row) with three TextViews in a vertical LinearLayout. Then you need to subclass the ArrayAdapter to fill the TextViews. In the getView you fill the lines.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.listitem, null);
}
TextView text1 = (TextView) convertView.findViewById(R.id.textfield1);
if (text1 != null) {
text1.setText(contentArray.get(position).valueForField1);
}
// same for the other fields
return v;
}

Unwanted occasional empty item appearing in ListView

I have a ListView in which each row is a TextView, and display a line of text. I'm getting a problem where occasionally an unwanted empty row appears. The empty row goes away once list scrolls past that particular area.
I've verified my list rows contain the correct information by using the following code after pausing the app in the debugger. Nothing in the output shows up empty or null, etc.
for (int i = 0; i<list.getChildCount(); i++) {
System.out.print((TextView) list.getChildAt(i)).getText());
}
This shows the information I expected.
I also checked the data backing my Adapter for empty entries, new lines, etc.
My getView() method inside the Adapter is as follows:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView t;
if (convertView == null) {
convertView = mInflator.inflate(R.layout.single_message_row, null);
t = (TextView) convertView;
t.setMovementMethod(LinkMovementMethod.getInstance());
t.setTextSize(mMsgSize);
}
else {
t = (TextView) convertView;
}
CharSequence text = get(position);
t.setText(text);
return t;
}
Below is an image demonstrating the problem (the area in red):
Try after changing getView method as:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row==null){
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.single_message_row, parent, false);
}
TextView t=(TextView)row.findViewById(R.id.yourtextview);
t.setText("position "+position);
t.setMovementMethod(LinkMovementMethod.getInstance());
t.setTextSize(mMsgSize);
CharSequence text = get(position);
t.setText(text);
return row;
}
It seems the problem was caused by using match_parent for my TextView width in the ListView. Changing it to wrap_content seems to have fixed it.
For an unwanted empty list item occurring in the ListView, I tried this:
List<String> listofItems;
String strlist=listofItems.get(position);
if(strlist.isEmpty())
{
remove(strlist);
}
'position' is what i got as a parameter in View getView method, because i was implementing a custom adapter.
it worked fine for me!

Change row height in listActivity when pressed

I have created a ListActivity class, with a custom Adapter.
Rows are simple: TextView and a Button.
Implemented getView method is as below:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.row, null);
}
TextView tV = (TextView) convertView.findViewById(R.id.tv);
tV.setText(MY_LIST[position]);
return convertView;
}
Now I want that each row, when pressed, dynamically add another button to itself, below the others components, and that consequently, the row height is increased.
How can I perform this steps?
New button should be hidden by default. When user clicked on row your handler makes something like mHiddenButton.setVisibility(View.VISIBLE).

AlertDialog with single choice list - I need some items nonclickable

I have AlertDialog with single choice list.
I want to put some 'fake' items inside - like labels of following items. I'm using different layout for regular item and for 'label' item. It it OK.
My problem is: How to make labels NON clickable?
Here is my getView code:
// #Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (m_data.get(position).BaseElementType == ElementType.Divider)
{
convertView = m_li.inflate(this.m_groupResurceID, null);
TextView post = (TextView)convertView.findViewById(R.id.text1);
post.setText(m_data.get(position).TypeToString());
post.getClickable();
}
else
{
convertView = m_li.inflate(this.m_itemResurceID, null);
TextView post = (TextView)convertView.findViewById(R.id.text1);
post.setText(m_data.get(position).Header);
ImageView img = (ImageView)convertView.findViewById(R.id.image1);
Drawable dr = m_data.get(position).TypeToIconId();
dr.setColorFilter(BGMapsApp.IconColor, PorterDuff.Mode.SRC_ATOP);
img.setImageDrawable(dr);
}
The answer is so simple!
Just put this to adapter code:
public boolean isEnabled(int position)
{
//return super.isEnabled(position);
return (m_data.get(position).BaseElementType != ElementType.Divider);
}
Now some items become non clickable :)

ListView with a TextEdit header messes up

I have a ListView, and I have added a header (with getListView().addHeaderView) that simply contains a TextEdit widget.
Then when I tap the TextEdit to start writting, the keyboard appears and it messes up the list!
If I tap everywhere else to hide the keyboard, the list messes up again!
I don't know why is this happening. I thought it was something related with the onConfigurationChanged method, but after implementing it (and adding the corresponding attribute in the manifest file) the problem persists.
How could I fix it? Why is Android messing up my list?
EDIT:
My list uses a custom adapter, this is the getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v != null) {
return v;
}
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_row, null);
ListTask list_item = items.get(position);
if (list_item != null) {
TextView item_name = (TextView) v.findViewById(R.id.item_name);
item_name.setText(list_item.getTitle());
}
return v;
}
The problem is not the value of my items, but their order. They are displayed in a different order when the keyboard appears, but the values are correct.
EDIT2:
Ok, I have changed my getView method with rekaszeru's suggestion and now it works as expected. But now I'm facing another problem: what if my items have two textviews?
Let's say the second textview is optional, and "Item 1" and "Item 3" have it, but "Item 2" does not, so it's initialized as a void String (length == 0).
The first time the list is displayed, it shows "Item1" and "Item 3" with their second textview, and "Item 2" without it. That's correct. But when the keyboard appears, the "Item 2" takes the second textview of another item and displays it!
This is the modified code I have right now:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.list_row, null);
}
ListTask list_item = items.get(position);
TextView item_name = (TextView) convertView.findViewById(R.id.item_name);
TextView item_optional_text = (TextView) convertView.findViewById(R.id.item_optional_text);
item_name.setText(list_item.getTitle());
// if the item has defined the optional text, make some room and display it
if (item_optional_text.isNotEmpty()) {
LayoutParams layout_params = (LayoutParams) item_name.getLayoutParams();
layout_params.topMargin = 10;
layout_params.height = -2; // -2: wrap_content
item_name.setLayoutParams(layout_params);
item_optional_text.setText(list_item.getOptionalText());
}
return convertView;
}
The isNotEmpty() does this in the Item class:
public boolean isNotEmpty() {
return this.optional_text.length() > 0;
}
Maybe it's too complex to understand in a written question. If so, I can make a short video showing the problem and my source code. Thanks in advance for your help guys.
Your row recycling is messed up. Android is not changing the order of the items, you are.
Right now, if you are passed a row to recycle, you return it without modification. This is a mistake. You are supposed to modify the contents of the row to reflect the data at the supplied position. The only piece of logic you can skip in this case is inflating a brand-new row.
Here is a free excerpt from one of my books that goes through all of this.
You should override the getView method in your ListAdapter implementation, and make sure that you always assign a new value to the view that you are returning (or at least always update it to contain the proper data).
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
convertView = inflater.inflate(R.layout.list_row, parent, false);
//set the necessary data in your TextViews, Checkboxes, etc...
return convertView;
}
If you don't inflate your item renderer, then you can instantiate it from code, like:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
convertView = new TextView([...]);
convertView.setText(textBasedOnYourData);
return convertView;
}
Edit
As #CommonsWare noted, attention should be payed to the recycling of your list item renderer. So instead of instantiating it every time, you should check whether it already exists or not, and update the underlying TextView afterwards.
So I'd suggest give a try to this slightly modified getView implementation:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.list_row, null);
}
ListTask list_item = items.get(position);
TextView item_name = (TextView) convertView.findViewById(R.id.item_name);
//the item should never be null, but just in case:
item_name.setText((list_item == null) ? "" : list_item.getTitle());
return convertView;
}

Categories

Resources