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.
Related
because I need to display something more than just a list in a Fragment.
So I choose Fragment rather than ListFragment, and my layout is something looks like
<linearlayout...>
<TextView...>...<TextView/>
<Button...>...<Button/>
<ListView android:id = "#"+id/mylist" ...></ListView>
</linearylayout>
And I implemnt "MyAdapter" extend BaseAdapter, which has getView like following
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(position == 0)
{
return categroyView("Team leader");
}
else if (position == 2)
{
return categroyView("Team memebers");
}
else
{
LayoutInflater inflater = context.getLayoutInflater();
View v = inflater.inflate(R.layout.row_group, null, false);
return v;
}
}
protected View categroyView(String text)
{
TextView txtView = new TextView(context);
txtView.setText(text);
return txtView;
}
It turns out that I can receive onItemClick when its position is 0 or 2 (which as you can see I dynamically generate textView.
Meanwhile I can't receive onItemClick when its position is not 0 or 2 (which I return inflate view from XML)
I've seen some discussion about if customized row layout has some clickable item (like button), this situation will happen, but even my row layout has only one textView, it still failed to receive onItemClick.
p.s.
Also, I select Fragment rather than Activity for other other design issue.
I know I can alternatively add v.setOnClickListene in getView to help this issue, but then still the item won't highlight if I pressed on it.
What is in the position two view? If that thing might be able to take focus sit will do it instead of the list item if you don't want the inards to be clickabke then disable that it's click and it will the be passed to the item
Also are these long lists? You will run into trouble if you inflate a lot
I have this ListView:
I'm using a custom adapter.
As you see, each row is made of a checkbox, a big TextView, and a little TextView.
All items have defined the little TextView, even the "Item 2" but it's a void string.
The problem comes when I tap the EditText placed in the header of the list:
The keyboard appears, and the rows are recycled, so the getView method of my adapter is called. In that method I have an if clause where I check if the length of the "optional" text (the little TextView) is greater than 0. In that case I make some room (the space that you can see in the screenshot) and I display it.
The problem is that "Item 2" has the "optional" text initialized but it's void (0-sized). I don't understand why the if clause is executed. But more strangely... the else is also executed! In the else I just show a void string in the little TextView.
Why is this happening? The app is really simple. This is my getView method:
#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);
}
ListItem list_item = items.get(position);
TextView item_title = (TextView) convertView.findViewById(R.id.item_title);
TextView item_optional = (TextView) convertView.findViewById(R.id.item_optional);
item_title.setText(list_item.getTitle());
// If the task has an optional text, make some room and display it
if (list_item.hasOptional()) {
// This portion of code will be executed when you tap the EditText and the keyboard appears, putting the item up in the row
LayoutParams layout_params = (LayoutParams) item_title.getLayoutParams();
layout_params.topMargin = 10;
layout_params.height = -2; // -2: wrap_content
item_title.setLayoutParams(layout_params);
item_optional.setText(list_item.getOptional());
item_optional.setVisibility(0);
} else {
// This portion of code will ALSO be executed when you tap the EditText... why? this should not happen!
item_optional.setText("");
}
return convertView;
}
The source code can be seen here (github).
When you modify a recycled view you have no idea what the state of the view is, with respect to how it might have been customized by previous calls to getView. The view you are recycling is not a fresh-out-the-box inflation of R.layout.list_row. Think of it as a "second hand" or "used" view.
So I can see under if (list_item.hasOptional().. you make some modification to the item_title.getLayoutParams(). As a view created here may later be recycled for a list item that will fail the check if (list_item.hasOptional() under the else code block you must reset the values you modify to the default specified in the layout.
I have a list of custom objects that I am loading into an ActivityList that allows multiple selections and displays the checkbox on the right side. Those custom objects contain a field named "enabled". When I load the data I want to scroll through the list of objects and check the checkbox for each row that represents an object that has the enable field marked true. Currently I have all of the records loading into the ActivityList as I want but I can not make any of the rows "checked" even though objects are marked as "enabled".
This is the code I am using to mark a row as checked
for (int i = 0; i < sourceList.length; i++) {
DataSource d = sourceList[i];
view.getChildAt(i).getClass().toString());
CheckedTextView checkView = (CheckedTextView)view.getChildAt(i);
checkView.setChecked(Boolean.parseBoolean(d.enabled));
}
I have put this code directly after calling setListAdapter and I have put it in the onContentChanged() function. However, in both places the rows are not displayed yet so the view.getChildAt(i) returns null so obviously the row does not get checked.
Can anyone tell me where I can put this code so that it will be executed after all rows have been added and displayed on the screen?
Thank you!
Originally I did not have a custom Adapter I was just using ArrayAdapter. In order to override the getView() method I created a custom Adapter and extended ArrayAdapter. I am still allowing the ArrayAdapter class to do most of the work but I am overriding getView(). Here is my code for getView()
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = super.getView(position, convertView, parent);
if (convertView.getClass() == CheckedTextView.class){
CheckedTextView checkView = (CheckedTextView)convertView;
DataSource d = getItem(position);
checkView.setText(d.getName());
checkView.setChecked(Boolean.parseBoolean(d.enabled));
}
return convertView;
}
Even with this code none of the check boxes are being checked. The DataSource's name field is being set as the text but the setChecked() method does not seem to be working for me. I also tried hard coding that to be
checkView.setChecked(true);
That did not work for me either. Do you have any more ideas on what I might be doing wrong?
Thanks again!
Can you show your adapter code? The place to call setChecked is in your getView code in your adapter. It will be something like:
#Override
getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.my_list_row, parent, false);
}
CheckedTextView checkView = (CheckedTextView)
convertView.findViewById(R.id.check_view);
DataSource d = sourceList[i];
checkView.setChecked(Boolean.parseBoolean(d.enabled));
return convertView;
}
ListView doesn't really expose its children via getChildAt. The intended interface is via constructing and populating rows in getView.
What you need to do is call myList.setItemChecked(position, Boolean.parseBoolean(d.enabled)); in your getView method (where myList is an instance of ListView).
Also make sure you've called myList.setChoiceMode(int choiceMode) before setting your list adapter.
If you have Checkable views inside another view, then the containing view needs to implement Checkable as well for checkable items in lists to work properly (see CheckableRelativeLayout for instance)
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.
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...