I want to emulate this type of Listview in a SlideMenu. I have the SlideMenu working fine. It is a ListFragment. I want to copy this pattern like the YouTube app on Android:
I essentially have a couple of list items I need to add to the top of the list of categories. And I want a Header to separate.
I want this:
Home
Profile
Top Items
Header that says Categories
And List of Categories
I already have the Categories listed out fine on my SlideMenu. They come from an adapter that populate from a table in MySQL. But the three top items do not come from that same table (or ANY table). Is the top portion a header to a ListView? Is it its OWN ListView? or..?
Keep in mind, I want ability to sort the list (which I already have via a spinner). So Categories must be dynamic. But how to I add a couple of static items above AND make a header?
I don't really need code sample, I just want to know method to implement this.
EDIT: Here is Code in progress
This show the separator like the Channels line in the Youtube example. Need to also figure out how to add those two or three static lines up top.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
holder = new ViewHolder();
View rowView = convertView;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
int type = getItemViewType(position);
if (rowView == null) {
switch (type) {
case TYPE_ITEM:
rowView = inflater.inflate(R.layout.mastercat_layout, null,
true);
holder.textView = (TextView) rowView.findViewById(R.id.label);
holder.textView.setTypeface(tf);
holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
break;
case TYPE_SEPARATOR:
rowView = inflater.inflate(R.layout.mastercat_layout_separate, null);
break;
}
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
holder.textView.setText(getItem(position));
holder.imageView.setImageResource(R.drawable.ic_launcher);
return rowView;
}
Maybe you need a couple pools of convertView in adapter?
BaseAdapter contains methods
public int getItemViewType (int position)
and
public int getViewTypeCount ()
You can override it to implement 2 pools of views - one for Headers and another one for Items of listView. Also in this case you need to change you getView method according to itemViewType, returned by getItemViewType().
Related
I'm new to android programming, and I've been reading a lot about it lately. One of the features of ListView, if I understood it right, is that it recycle views and just replaces it with new data when an item is off the screen.
And just a few minutes ago, I was reading up about endless scrolling, and RecyclerView has been one of the popular choices to implement such a feature. So I looked up RecyclerView, and in this video, it is mentioned that RecyclerView recycles a view automatically to reuse it for new data (as a way to contrast its difference with ListView).
Did I misunderstand ListView about its recycling mechanism? Or if it does recycle, how do you actually implement (or how do you know you are implementing) it?
RecyclerView does recycling automatically. In order to make ListView recycle items you will need to do this modification inside of adapter class.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
//brand new
convertView = LayoutInflater.from(mContext).inflate(R.layout.days_list_item, null);
holder = new ViewHolder();
// below is variables that will be different in your case
holder.numberOfDays = (TextView) convertView.findViewById(R.id.eventDays);
holder.sinceOrUntil = (TextView) convertView.findViewById(R.id.eventType);
holder.eventTitle = (TextView) convertView.findViewById(R.id.eventTitle);
holder.daysText = (TextView) convertView.findViewById(R.id.DaysText);
convertView.setTag(holder);
}
else {
//reusing item
holder = (ViewHolder) convertView.getTag();
}
// rest of the code
}
For more details refer to this link.
I just started learning android and I'm at a point where I want to do the question described below but I'm not sure how to start.
I have an array of data with the following data,
1, text1, image1.png
2, text2, image2.png
3, text3, null
4, null, image3.png
I know how to create a ListView with ArrayAdapter along with their xml layout following some tutorial.
As you see in the array above sometimes it doesn't contain an image, sometimes it doesn't contain text and sometimes it has both.
My question is how to make that work with layout so that it dynamically changes based on the array values?
In other words how can I start thinking about building a listview+ArrayAdapter+layout where I can view an imageiew only where the array record has an image only, viewing a textview when there is a text only and viewing both of them when both are available.
A link to a tutorial will be extremely helpful
You could create a type MyCustomType that represents one array element (In your case it holds a number, a text and an image). Furthermore you need to implement your custom array adapter. This adapter uses an ArrayList of your MyCustomType.
public class CustomAdapter extends ArrayAdapter<MyCustomType> {
//...
private ArrayList<MyCustomType> foo;
public CustomAdapter(Context context, Activity bar, ArrayList<MyCustomType> foo) {
super(bar, R.layout.row, foo);
this.foo = foo;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
Override getViewTypeCount() to determine how many different kinds of rows you have. getItemViewType returns the kind of row that has to be displayed.
Your getView method could be similiar to his one:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
int type = getItemViewType(position); // decide what row type has to be displayed
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row, parent, false);
viewHolder = new ViewHolder();
viewHolder.number = //...
viewHolder.text = //...
viewHolder.image = //...
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag(); // avoided to call findViewById
}
switch(type) {
case TYPE1:
//... you could just change the visibility of the corresponding view
break;
case TYPE 2:
// ...
break;
}
return convertView;
}
My advice is to use a custom array adapter. There is a good tutorial here.
The official documentation can be found here.
Essentially you will create a class that extends the ArrayAdapter class. Implement the overrides and put your handling to show or not show particular views in the getView method. This method will fire for each item in the list passed it.
I'm implementing custom adapter which handles multiple type of lines in a listview based on this (very useful) tutorial: http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
Now, I thought I understood everything but one thing puzzles me.
In the getView method we receive the convertView which suppose to be the view (group) with specific layout to display in the specific line in the listview.
public View getView(int position, View convertView, ViewGroup parent) {
//first get the animal from our data model
Animal animal = animals.get(position);
//if we have an image so we setup an the view for an image row
if (animal.getImageId() != null) {
ImageRowViewHolder holder;
View view;
//don't have a convert view so we're going to have to create a new one
if (convertView == null) {
ViewGroup viewGroup = (ViewGroup)LayoutInflater.from(AnimalHome.this)
.inflate(R.layout.image_row, null);
//using the ViewHolder pattern to reduce lookups
holder = new ImageRowViewHolder((ImageView)viewGroup.findViewById(R.id.image),
(TextView)viewGroup.findViewById(R.id.title));
viewGroup.setTag(holder);
view = viewGroup;
}
//we have a convertView so we're just going to use it's content
else {
//get the holder so we can set the image
holder = (ImageRowViewHolder)convertView.getTag();
view = convertView;
}
//actually set the contents based on our animal
holder.imageView.setImageResource(animal.getImageId());
holder.titleView.setText(animal.getName());
return view;
}
//basically the same as above but for a layout with title and description
else {
DescriptionRowViewHolder holder;
View view;
if (convertView == null) {
ViewGroup viewGroup = (ViewGroup)LayoutInflater.from(AnimalHome.this)
.inflate(R.layout.text_row, null);
holder = new DescriptionRowViewHolder((TextView)viewGroup.findViewById(R.id.title),
(TextView)viewGroup.findViewById(R.id.description));
viewGroup.setTag(holder);
view = viewGroup;
} else {
view = convertView;
holder = (DescriptionRowViewHolder)convertView.getTag();
}
holder.descriptionView.setText(animal.getDescription());
holder.titleView.setText(animal.getName());
return view;
}
}
However, in the case of multiple types of lines in the listview (for example, list of animals with separators lines with titles like 'mamals','fish','birds') how does the listview know what convertView to send? it can be one of two completely different types. something is very unclear to me. can someone explain please?
From the tutorial you provided :)
The two additional methods android Adapters provide for managing different row types are:
getItemViewType(int position) and getViewTypeCount().
The list view uses these methods create different pools of views to reuse for different types of rows.
Good Luck :)
We are working with list views in college at the moment. My lecturer gave us a simple application that displays mail messages in a list and when the user selects one it displays the content of the message in a new activity. I understand pretty much all of what is going on but there are a few grey areas I want to clear up!
Basically I am wondering what this section of code does?
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.inbox_row, null);
}
This method is located within a class that extends ArrayAdapter. Am I right in thinking that it is some form of recycling? for when views go on and off the screen?....
Any help is much appreciated. thanks.
it's exactly what you said, a form of recycling.
Inflating a layout takes a lot of memory and a lot of time, so for the efficiency sake, the system passes to you that just went off the screen and you can simply update its text and images and give them back to the UI.
So for example, if your list view is showing 6 items on its list (due to the height of it), it will only inflate 6 items and during scroll it just keeps recycling them.
there's some extra optimisations tricks that you should use and I'm sure that the video link that the commenter posted will explain them.
edit
that example is an ArrayAdapter of Store items, but you can make it to whatever you need.
the adapter does the match and separation layer between UI and data.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = newView();
// Store is the type of this ArrayAdapter
Store store = getItem(position);
Holder h = (Holder) convertView.getTag();
// And here I get the data and address them to the UI
// as you can see, if the convertView is not null,
// I'm not creating a new one, I'm just changing text, images, etc
h.storeName.setText(store.getStoreName());
h.address.setText(store.getAddressLine1());
h.postcode.setText(store.getPostCode());
h.distance.setText(store.getDistance());
return convertView;
}
// I like to separate in a different method when the convertView is null
// but that's me being organisation obsessive
// but it also makes easy to see which methods are only being called the 1st time
private View newView() {
LayoutInflater inf = LayoutInflater.from(getContext());
View v = inf.inflate(R.layout.map_result_list, null);
Holder h = new Holder();
// here we store that holder inside the view itself
v.setTag(h);
// and only call those findById on this first start
h.storeName = (TextView) v.findViewById(R.id.txtLine1);
h.address = (TextView) v.findViewById(R.id.txtLine2);
h.postcode = (TextView) v.findViewById(R.id.txtLine3);
h.distance = (TextView) v.findViewById(R.id.txtDistance);
return v;
}
// this class is here just to hold reference to the UI elements
// findViewById is a lengthy operation so this is one of the optimisations
private class Holder {
TextView storeName;
TextView address;
TextView postcode;
TextView distance;
}
I have a list of items. Each of them has a set of data displayed with TextViews. This data remains mostly unchanged. But I have a distance field, which I would like to update whenever I get new lock from location provider.
The question is: Should I just update my data and call notifyDataSetChanged() on my Adapter or is there a more efficient way?
Seems very expensive to reload all the lists (I have several of them in a ViewPager) just because one TextView in each list item needs to be updated.
Here is my getView() from my adapter. It might help:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.eventrow, parent, false);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.eventTitle);
holder.distance = (TextView) convertView.findViewById(R.id.eventDistance);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(((EventItem) getItem(position)).getTitle());
holder.distance.setText(String.valueOf(((EventItem) getItem(position)).getDistance()));
return convertView;
}
I also thought about directly referencing the holder.distance but it seems like a bad idea to do it outside getView().
The correct way to modify your data is to change your list item and then call notifyDataSetChanged().
The only alternative that comes to my mind is to set again the adapter on the list view which is way more expensive. There are no other ways.
So the answer is: you HAVE to go through notifyDataSetChanged().