i've got a listView with some data that i inflated to get some nice background color. The problem is that i want to get some awesome separators and i'm unable to setDividerHeight() depending on the row's data, because it seems that i can't inflate two views on the same getView() method, here's some code:
public View getView(int position, View convertView, ViewGroup parent){
String myText = getItem(position).toString();
String firstLetter = Character.toString(myText.charAt(0));
if(convertView == null){
convertView = this.inflater.inflate(R.layout.lettersrows, null);
}
TextView tv = (TextView)convertView.findViewById(R.id.label);
tv.setText(this.list.get(position));
tv.setTextSize(25);
convertView.setBackgroundColor((position & 1) == 1 ? Color.WHITE : Color.LTGRAY);
/**This is what i want to do*/
if(!firstLetter.equals("A")){
convertView = this.inflater.inflate(R.layout.letters, null);
ListView lv = (ListView)convertView.findViewById(R.id.letters_listview);
lv.setDividerHeight(3);
}
return convertView;
}
The error i'm getting is a NullPointerException at: tv.setText(this.list.get(position));
I guess that dues to that the convertView is now a ListView that's why it doesn't find where to set the text. How could i fix this problem.
Best regards.
You can use your custom Adapter to inflate as many different types of layout as you want.
For this, you just need to change your getViewTypeCount method to return the type of different views you want (2 in your example, regular item and separator) and adapt your getView() method to chose the correct view type to display.
Everything is explained in this great tutorial
Note: In the tutorial, they do that by implementing a getItemViewType() method. This can be useful in some cases.
Related
I try to add dynamic number of cell inside listview using custom cell layout and ArrayAdapter and it done well. But i face a problem when i want each cell may different follow type of data.
Example i have 3 categories inside a listview :
Video (video_custom_cell.xml)
Photo (photo_custom_cell.xml)
Audio (audio_custom_cell.xml)
if data is video then i use cell number one, else if data is photo i use photo_custom_cell and else i use audio_custom_cell.
What i have tried only can reuse a custom cell for dynamic number of row, but i still not find how to use custom cell follow by type of data inside cell.
Can anyone help me to explain an example for my problem?
Thank you.
You can use a custom adapter for this and inside the getView function you do for example:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView;
if (convertView == null) {
if(items.get(position).type.equals("video")) {
rowView = getActivity().getLayoutInflater().inflate(
R.layout.video_custom_cell, null);
}else if(items.get(position).type.equals("photo")) {
rowView = getActivity().getLayoutInflater().inflate(
R.layout.photo_custom_cell, null);
}
} else {
rowView = convertView;
}
}
In my listview I have a custom Adapter, which I build using a TreeMap, I defined the getView method which is as follows. I am trying to strike out the text in a certian textview of the listview on click, only those textviews will be striken off which contain a certain pattern of characters (y#y). However on clicking one row in the listview I am getting strike out effect on some other row.
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.chklistlayout, parent, false);
}
TextView textView = ((TextView) convertView.findViewById(R.id.textView1));
TextView imageview = ((TextView) convertView.findViewById(R.id.textView2));
textView.setText(values[position]);
imageview.setText(mValues[position]);
String s = mValues[position];
if (s.contains("y#y")) {
System.out.println("In if of getview");
System.out.println(s);
imageview.setPaintFlags(imageview.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
}
return convertView;
}
}
I tried using a holder pattern too, using a static holder class, but the problem seems to persist. Any pointers?
this answer is half mine and half from Muhammad Babar that we both commented on the question making together a quite nice answer:
use else block to handle this, imageview.setPaintFlags() not to strike
that happens
Because of the convertView. When you scroll the list the same view
that was used before is give back to getView method. So the same
imageView that you painted with StrikeThrough before is still painted
with it. So you have to revert that action.
more over, because of this recycling, usually dealing with Adapters you must ALWAYS undo/revert/repaint/change text/change image to all the elements to make sure it will be on screen the way you want.
ps.: now you should apply a Holder pattern to get better performance.
For my application i retrieve a number from the database. When the activity starts up it has to show the number in a different color then the other numbers in the list.
After retrieving the data from the database this is my code:
int row = 5;
TextView child = (TextView)ListView.GetChildAt(row);
child.SetTextColor(Color.Red);
This code was placed in the OnCreate function. I kept getting a null value back for the child textview. I then found out that the reason for the null value is that in the OnCreate function the listview still needs to be rendered. I then moved the code to the OnStart() function but this didn't work either.
Can anyone tell me how I should retrieve the child row from the listview during the creation/start of the activity?
even if you will be able to do it this way you will experience problems with this view getting recycled .. (you will see other views getting colored with red when you scroll up and down).
You need to override your adapter and set the view's color in the position you want
under getView() -
TextView myText = (TextView) view.findViewById(R.Id ....
if (pos==5)
myText.setTextColor(Color.Red);
else
myText.setTextColor(Color.Black); //original color..
EDIT:
you don't need to have a custom xml. if you find android's xml you can find its id. I believe its android.R.id.text1 . so your adapter should look something like
myAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1) {
#Override
public View getView(int position, View v, ViewGroup parent) {
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(android.R.layout.simple_list_item_1, null);
}
View view = super.getView(position, v, parent);
if (position==5)
view.setTextColor(Color.Red);
else
view.setTextColor(Color.Black); //original color..
}
myList.setAdapter(myAdapter);
not sure I got it all right but something like that..
hope it helps.
I'm trying to do a List that shows an image and a simple text describing the image.
In my search on internet I found many ways to do this. Some people using ArrayAdapter, others using SimpleCursorAdapter. One thing I notice, many people are creating classes inheriting from ListActivity and in the setListAdapter method they are inserting other classes derived from Array or SimpleCursor adapter.
First question: is this the best way to do this?
I created a LinearLayout with a ListView inside. And to insert rows, another layout was created with an ImageView and a TextView.
Second question: is this correct?
I'm confusing about creation of this type of component. Is this the correct way to do this?
Yes, this is correct, although you will need to use a CursorAdapter instead of a SimpleCursorAdapter, since the point of a SimpleCursorAdapter is to populate a row with only a TextView in it.
You will have a getView method on your CursorAdapter that expands your row layout:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { // we don't have a recycled view
convertView = LayoutInflator.from(getContext()).inflate(
R.layout.row, parent, false);
}
// setup our row
TextView text = (TextView) convertView.findViewById(R.id.text_view);
text.setText( ... );
ImageView image = (ImageView) convertView.findViewById(R.id.image_view);
image.setImageBitmap( ... );
return convertView;
}
When you're setting the text and image of your views, you can use adapter methods like getItem to access the underlying data you need.
I have a ListView in a custom ArrayAdapter that displays an icon ImageView and a TextView in each row. When I make the list long enough to let you scroll through it, the order starts out right, but when I start to scroll down, some of the earlier entries start re-appearing. If I scroll back up, the old order changes. Doing this repeatedly eventually causes the entire list order to be seemingly random. So scrolling the list is either causing the child order to change, or the drawing is not refreshing correctly.
What could cause something like this to happen? I need the order the items are displayed to the user to be the same order they are added to the ArrayList, or at LEAST to remain in one static order. If I need to provide more detailed information, please let me know. Any help is appreciated. Thanks.
I was having similar issues, but when clicking an item in the custom list, the items on the screen would reverse in sequence. If I clicked again, they'd reverse back to where they were originally.
After reading this, I checked my code where I overload the getView method. I was getting the view from the convertedView, and if it was null, that's when I'd build my stuff. However, after placing a breakpoint, I found that it was calling this method on every click and on subsequent clicks, the convertedView was not null therefore the items weren't being set.
Here is an example of what it was:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
}
return view;
}
The subtle change is moving the close brace for the null check on the view to just after inflating:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
}
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
return view;
}
I hope this helps others who experience this same problem.
To further clarify the answer of farcats below in more general way, here is my explanation:
The vi.inflate operation (needed here for parsing of the layout of a row from XML and creating the appropriate View object) is wrapped by an if (view == null) statement for efficiency, so the inflation of the same object will not happen again and again every time it pops into view.
HOWEVER, the other parts of the getView method are used to set other parameters and therefore should NOT be included within the if (view == null) statement.
Similarily, in other common implementation of this method, some textView, ImageView or ImageButton elements need to be populated by values from the list[position], using findViewById and after that .setText or .setImageBitmap operations.
These operations must come after both creating a view from scratch by inflation and getting an existing view if not null.
Another good example where this solution is applied for BaseAdapter appears in BaseAdapter causing ListView to go out of order when scrolled
The ListView reuses view objects when you scroll. Are you overriding the getView method? You need to make sure you set each property for every view, don't assume that it will remember what you had before. If you post that method, someone can probably point you at the part that is incorrect.
I have a ListView, AdapterView and a View (search_options) that contains EditText and 3 Spinners. ListView items are multiple copies of (search_options) layout, where user can add more options in ListView then click search to send sql query built according to users options.
I found that convertView mixing indecies so I added a global list (myViews) in activity and passed it to ArrayAdapter. Then in ArrayAdapter (getView) I add every newly added view to it (myViews).
Also on getView instead of checking if convertView is null, I check if the global list (myViews) has a view on the selected (position).. It totally solved problems after losing 3 days reading the internet!!
1- on Activity add this:
Map<Integer, View> myViews = new HashMap<>();
and then pass it to ArrayAdapter using adapter constructor.
mSOAdapter = new SearchOptionsAdapter(getActivity(), resultStrs, myViews);
2- on getView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (!myViews.containsKey(position)) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.search_options, parent, false);
/// ...... YOUR CODE
myViews.put(position, view);
FontUtils.setCustomFontsIn(view, getContext().getAssets());
}else {
view = myViews.get(position);
}
return view;
}
Finally no more mixing items...