Im trying to understand why findViewById always returns null when Im reusing my view in getView() of my custom adapter.
1) Below image shows my ListView. Pretty basic.
2) I have defined another xml file called selected_apps_button.xml which is view that'll be added to the ListView above. Here is the image
3) Here is how im inflating the view in step 2 to add to the ListView in step 1
public View getView(final int pos, View convertView, ViewGroup parent) {
View selectAppsButton = convertView;
if(selectAppsButton == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
selectAppsButton = inflater.inflate(R.layout.selected_apps_button, parent, false);
}
final TileButton appColorButton = (TileButton) selectAppsButton.findViewById(R.id.appColorr);
final TileButton appNameButton = (TileButton) selectAppsButton.findViewById(R.id.appNamee); //appNameButton is always null
4) In the above code 'appNameButton' is always null. Interesting thing is 'appColorButton' from same inflated layout is NOT null.
5) Any idea what's going on?
I have build and rebuild the project like a million times. But it didnt help.
Please let me know if more information is required. Thanks.
Related
As you should know, ListView recycles the view. But i want to work with elements that can be clicked and expanded. Like i already did:
But it was completely messed up, even using:
View checklayout = convertView;
if(checklayout == null){
checklayout = inflater.inflate(R.layout.home_cell, null);
}
When some opened expandable views goes out of the screen, the recycled one, which shouldn't be expandable, receives the vanished's layout. Only view that has "1 AVALIAÇÃO LANÇADA" should open, and show it's content. I add this content by using if(qtdAvaliacoes > 0) that is a property of my Object that comes from ArrayList<>.
I "solved" this disabling the recycler, with:
#Override
public int getViewTypeCount() {
return getCount();
}
#Override
public int getItemViewType(int position) {
return position;
}
Once my listView will only receives 5~10 rows. But i know that isn't a good practice. While i'm writting this question, i found a solution, calling my object before inflate any view, then checking the property:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View checklayout = convertView;
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final Disciplina disciplina = lista.get(position);
if(checklayout == null || disciplina.getQtdAvaliacoes() == 0){
checklayout = inflater.inflate(R.layout.home_cell, null);
}
final View layout = checklayout;
But I don't think this is the best way to do this. I read something about Tags, but was little confused. I think if i could bind these onClick methods to the row position it would be better.
Any ideas ? Or is my solution good at you, developer's, point of view.
Thanks.
The easiest way is to not do subinflates within a list item. Do it via view visibilities instead, making the inflated part GONE if you don't want it to display yet. You'll just have to explicitly set the visibility of that view in every call to getView
I am new to Android. I would like to create an Activity very similar to "Add event" part of Android Calendar app. To me it looks like a ListView with different components in each row. I could be wrong. If I am right, I still don't know how to add different components to each row of a ListView, e.g., EditText in one row, TextView in another row, etc. If this app is not ListView at all, if anybody can tell me how I can create something similar, I'd appreciate that a lot.
hello check this link it be helpful 1
listView with different component
First you have to learn how to implement a custom Adapter (see this tutorial: http://www.vogella.com/tutorials/AndroidListView/article.html#adapterown)
Then in your getView overriden method (you'll learn about it in the tutorial) you have to do something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = null;
if (position == LAYOUT1_POSITION) //In this row you will place the layout named layout1
rowView = inflater.inflate(R.layout.rowLayout1, parent, false);
if (position == LAYOUT2_POSITION)
rowView = inflater.inflate(R.layout.rowLayout2, parent, false);
//Do similar for all your different layouts
return rowView;
}
I'm new to android programming and doing the first steps with Adapters (for a ListView).
Overriding the Adapter.getView I often see things like this:
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = inflater.inflate(R.layout.table_row, null);
} else {
itemView = convertView;
}
// play with itemView
return itemView;
}
My question is what speaks against this:
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = super(position, convertView, parent);
// play with itemView
return itemView;
}
This seems to work for me but I'm sure there's a big point I'm missing :D
Thanks for reading and sorry for my bad english ...
You can use
View itemView = super(position, convertView, parent);
if only you are deriving from "ready to use" adapters (not BaseAdapter), like SimpleAdapter, or ArrayAdapter, as they already have their implementation for the getView().
Have a look at them: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r2.1/android/widget/ArrayAdapter.java#361 for the ArrayAdapter, and
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r2.1/android/widget/SimpleAdapter.java#113 for SimpleAdapter.
If you derive from BaseAdapter, you will have to manualy implement the whole method, as you've described in the first example, because it does not have it out of the box: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r2.1/android/widget/BaseAdapter.java#BaseAdapter
The getView(..)-method of the Adapter can be multiple ways. The only question is, which one is the most efficient?
An interesting article to read and make you understand the ListView more detailed: http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
If you mean that this piece of code:
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = inflater.inflate(R.layout.table_row, null);
} else {
itemView = convertView;
}
seems unnecessary for you: this piece of code allows Android to create a relatively small number of cells (equals to the number of cells that are visible on your screen +-), and then "recycle" these cells - use them over and over again while the user scrolls the list, instead of creating a cell for each object in your array.
This will help you with:
Saving memory - because you don't create view for each element in your array
Saving CPU usage - creating a view object out of xml file ("inflating") is relatively expensive task and doing so for each item in your array might choke your UI thread
I'm trying to give the users of my app the option to change how they want their results displayed.
I've created a different layout item for each view and extended from BaseAdapter like so:
public View getView(int index, View recycledCompatibleView, ViewGroup parent)
{
// Just in case the view can be reused
View toReturn = recycledCompatibleView;
if(toReturn == null)
{
LayoutInflater inflator = LayoutInflater.from(parent.getContext());
toReturn = inflator.inflate(layoutId, null);
}
...
}
public void setResultsListStyle(int layoutId)
{
this.layoutId = layoutId;
}
Calling notifyDataSetChanged() is (observable through debug) refreshing the view because the new view is being inflated and returned from getView() method.
However the view on screen is not changing...
Is there something I'm missing ?
The problem here is that ListView may cache already inflated Views that are of old "format". You need to somehow reset this View "cache". For this you can use next trick:
mListView.setAdapter(mListView.getAdapter());
Instead of calling notifyDataSetChanged().
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...