Custom rows in ListView with custom Adapter - android

I made the following adapter which I used before to create rows that are the same all the time. I removed the creation of the textviews and imageviews.
What I want to achieve is to create different rows depending on the key.
A row could contain text and an image while another row only has text. How would I be able to do that?
public class DetailsListAdapter extends ArrayAdapter<ArrayList<String>> {
private Context context;
private ArrayList<String> keys;
public DetailsListAdapter(Context context, ArrayList<String> keys) {
super(context,R.layout.details);
this.context = context;
this.keys = keys;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.details, null);
return v;
}
#Override
public int getCount(){
return keys.size();
}
}

To inflate different layout for rows you need to override getViewItemType and getViewTypeCount.
You should have a look at the video in the link.
http://www.youtube.com/watch?v=wDBM6wVEO70
private static final int TYPE_ITEM1 = 0;
private static final int TYPE_ITEM2 = 1;
private static final int TYPE_ITEM3 = 2;
Then
int type;
#Override
public int getItemViewType(int position) {
if (position== 0){
type = TYPE_ITEM1;
} else if (position == 1){
type = TYPE_ITEM2;
}
else
{
type= TYPE_ITEM3 ;
}
return type;
}
#Override
public int getViewTypeCount() {
return 3;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
LayoutInflater inflater = null;
int type = getItemViewType(position);
// instead of if else you can use a case
if (convertView == null) {
if (type == TYPE_ITEM1 {
//infalte layout of type1
convertView = mInflater.inflate(R.layout.layouttype1,
parent, false);
}
if (type == TYPE_ITEM2) {
//infalte layout of type2
convertView = mInflater.inflate(R.layout.layouttype2,
parent, false);
} else {
//infalte layout of normaltype
convertView = mInflater.inflate(R.layout.layouttype3,
parent, false);
}
...// rest of the code
return convertView;
}

override getViewTypeCount inside this function return the number of types views you want to use inside listview.
Override getItemViewType(int position) inside this write your logic to get the type of view.
#Override
public int getViewTypeCount() {
return 2; //return 2, in case you have two types of view
}
#Override
public int getItemViewType(int position) {
return (contition) ? R.layout.layout1 : R.layout.layout2; //return 2, in case you have two types of view
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = (convertView == null) ?View.inflate(context, getItemViewType(position), null) : convertView;
return convertView;
}

Related

convertView received in Adapter is of the wrong type

I have custom adapter for spinner:
public class AreaArrayAdapter extends ArrayAdapter<__KeyValueObject[]> {
private static final int ITEM_TYPE_AREA = 0;
private static final int ITEM_TYPE_REGION = 1;
private static final int NO_OF_TYPES = 2;
private LayoutInflater mLayoutInflater;
private __KeyValueObject[] mItems;
public AreaArrayAdapter(Context context, int resource, __KeyValueObject[] items) {
super(context, resource);
mItems = items;
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getItemViewType(int position) {
int type = -1;
if (mItems[position].bool_val == true) {
type = ITEM_TYPE_REGION;
} else {
type = ITEM_TYPE_AREA;
}
return type;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent);
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
if (mItems[position].bool_val == true) {
return true;
} else {
return true;
}
}
#Override
public int getCount() {
return mItems.length;
}
#Override
public int getViewTypeCount() {
return NO_OF_TYPES;
}
public View getCustomView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
if (type == ITEM_TYPE_AREA) {
ViewHolderArea holderArea = null;
if (convertView == null) {
holderArea = new ViewHolderArea();
convertView = mLayoutInflater.inflate(R.layout.row_spinner_area_item, null);
holderArea.tvAreaItem = (TextView) convertView.findViewById(R.id.tvAreaItem);
convertView.setTag(holderArea);
} else {
holderArea = (ViewHolderArea) convertView.getTag();
}
holderArea.tvAreaItem.setText(mItems[position].value);
} else // type == ITEM_TYPE_REGION
{
ViewHolderRegion holderRegion = null;
if (convertView == null) {
holderRegion = new ViewHolderRegion();
convertView = mLayoutInflater.inflate(R.layout.row_spinner_region_item, null);
holderRegion.tvRegionItem = (TextView) convertView.findViewById(R.id.tvRegionItem);
convertView.setTag(holderRegion);
} else {
holderRegion = (ViewHolderRegion) convertView.getTag();
}
holderRegion.tvRegionItem.setText(mItems[position].value);
}
return convertView;
}
public static class ViewHolderArea {
public TextView tvAreaItem;
}
public static class ViewHolderRegion {
public TextView tvRegionItem;
}
}
here is my data:
public static __KeyValueObject[] AREA = {
new __KeyValueObject(50, "Region 1", true),
new __KeyValueObject(100, "area 1-1"),
new __KeyValueObject(200, "area 1-2"),
new __KeyValueObject(300, "area 1-3"),
new __KeyValueObject(800, "Region 2", true),
new __KeyValueObject(900, "area 2-1"),
new __KeyValueObject(1000, "area 2-2"),
new __KeyValueObject(1100, "area 2-3"),
new __KeyValueObject(1200, "area 2-4")
}
When I press the Spinner for some reason in getCustomView the first call goes right. The second call to getCustomView the item I get is of type ITEM_TYPE_AREA but the convertView I get is of type ViewHolderRegion so trying to cast : holderArea = (ViewHolderArea) convertView.getTag(); crashes.
Any idea why convertView received is of the wrong type?
If you receive convertView and getItemViewType(position) == ITEM_TYPE_REGION it doesn't mean that this view contain in Tag ViewHolderRegion, because views are reuse. so you need each time inflate new view or create common ViewHolder
public static class ViewHolder {
public TextView tvAreaItem;
public TextView tvRegionItem;
}
Update:
Look at this tutorial

In GridView,item width and height is code control,but ImageView for first item no response OnClickListener

This is my use adapter,when I click first item‘s headImageView, I don't skip OtherUserActivity, I click second item’s headImageView, open many OtherUserActivity。I remove LayoutParams,click normal
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LoopDetailsDataList loopDetailsDataList = list.get(position);
ViewHolder holder = ViewHolder.get(context, convertView, parent, R.layout.item_loop_details, position);
convertView = holder.getConvertView();
int itemParams = screenWidth / 2 - Utils.newInstance(context).dip2px(10);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(itemParams, itemParams);
convertView.setLayoutParams(params);
ImageView mainImageView = holder.getView(R.id.item_loop_details_image);
CircleImageView headImageView = holder.getView(R.id.item_loop_details_head_image);
TextView userName = holder.getView(R.id.item_loop_details_user_name);
TextView title = holder.getView(R.id.item_loop_details_title);
TextView praise = holder.getView(R.id.item_loop_details_praise);
TextView comments = holder.getView(R.id.item_loop_details_comments);
headImageView.setTag(position);
headImageView.setOnClickListener(new MyClickListener(position));
return convertView;
}
private class MyClickListener implements View.OnClickListener {
private int index;
private MyClickListener(int index) {
this.index = index;
}
#Override
public void onClick(View v) {
String userSign = list.get(index).getaCreateUserSign();
Log.i(TAG, "CreateUserSign:" + userSign);
Intent intent = new Intent(context, OtherUserActivity.class);
intent.putExtra("userSign", userSign);
context.startActivity(intent);
}
}
this is my ViewHolder
private ViewHolder(Context context, ViewGroup parent, int layoutId,
int position) {
this.mPosition = position;
this.mViews = new SparseArray<>();
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
// setTag
mConvertView.setTag(this);
}
public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder(context, parent, layoutId, position);
} else {
holder = (ViewHolder) convertView.getTag();
holder.mPosition = position;
}
return holder;
}
public View getConvertView() {
return mConvertView;
}
#SuppressWarnings("unchecked")
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public ViewHolder setText(int viewId, String text) {
TextView view = getView(viewId);
view.setText(text);
return this;
}
public ViewHolder setImageResource(int viewId, int drawableId) {
ImageView view = getView(viewId);
view.setImageResource(drawableId);
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bm) {
ImageView view = getView(viewId);
view.setImageBitmap(bm);
return this;
}
public ViewHolder setImageByUrl(int viewId, String url) {
ImageLoaderUtils.getInstance(3, ImageLoaderUtils.Type.LIFO).loadImage(url,
(ImageView) getView(viewId));
return this;
}
public int getPosition() {
return mPosition;
}
Well you shouldn't change the width of your view to half the screen because if you are using a gridview it will take care of that for you if you specify the the number of columns or it will set it to its default 2 columns. I think that there are some overlapping views due to resizing, so hopefully that will solve it.
Also you should do two other things to your code:
First you need to put holder = (ViewHolder) convertView.getTag();out side the if else because you do it no matter what, thats for convention sake.
Second you are not setting the mConvertView in your holder get method in the else block.

Listview: Only one list item with multiple textviews

I need to show multiple text views in only one list item and the rest of the list view items will have just one textview.
How do I achieve this? Any samples or tutorials you can point me at?
Thank you.
Make your own adapter:
BaseAdapter already provieds methods for working with different View types (different layout for list item cells) and to effektivly recycle views:
getViewTypeCount(): The count of how many different Views (layouts) are present.
getItemViewType(): Returns a integer to identify the view type of the item in the cell. Note the internally BaseAdapter implementation uses an array. Therefore your values returned here must be from 0 to n. You are not allowed to skip indexes.
public class MyAdapter extends BaseAdapter {
private final int VIEW_TYPE_NORMAL = 0;
private final int VIEW_TYPE_4_TEXTS = 1;
/**
* The inflater for
*/
protected LayoutInflater inflater;
protected Context context;
public MyAdapter(Context context) {
this.context = context;
this.inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getViewTypeCount() {
// There are two view types
return 2;
}
#Override
public int getItemViewType(int position) {
if (position == 0)
return VIEW_TYPE_4_TEXTS;
else
return VIEW_TYPE_NORMAL;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
if (convertView == null) {
convertView = newView(type, parent);
}
bindView(position, type, convertView);
return convertView;
}
#Override
public long getItemId(int position) {
return 0; // replace it with your id, if you have stable ids in your
// adapter
}
/** Inflates the correct view accorind the view type **/
public View newView(int type, ViewGroup parent) {
if (VIEW_TYPE_4_TEXTS == type) {
View view = inflater.inflate(R.layout.your_layout_with_4_textviews,
parent, false);
view.setTag(new ViewHolder4TextViews(view)); // ViewHolder pattern
return view;
}
// Otherwise its a normal item with VIEW_TYPE_NORMAL
View view = inflater
.inflate(R.layout.your_normal_layout, parent, false);
view.setTag(new NormalViewHolder(view)); // ViewHolder pattern
return view;
}
/** Bind the data for the specified {#code position} to the {#code view}. */
public void bindView(int position, int type, View view) {
if (VIEW_TYPE_4_TEXTS == type) {
// set the 4 text view values
ViewHolder4TextViews holder = (ViewHolder4TextViews) view.getTag();
holder.textview1.setText("...");
holder.textview2.setText("...");
holder.textview3.setText("...");
holder.textview4.setText("...");
} else {
// VIEW_TYPE_NORMAL
NormalViewHolder holder = (NormalViewHolder) view.getTag();
holder.textview.setText("...");
}
}
}
Use a custom adapter for you list and edit the getView function:
public class MyListAdapter extends BaseAdapter {
[...]
#Override
public View getView(int position, View view, ViewGroup parentView) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(position < 1) {
/* Inflate a layout with 4 textview */
view = inflater.inflate(R.layout.your_4_textview_layout, parentView, false);
} else {
/* Inflate a layout with 1 textview */
view = inflater.inflate(R.layout.your_1_textview_layout, parentView, false);
}
return view;
}
[...]
}
Take care about pass Context context to MyListAdapter in constructor like this answer
You can make changes in your adapter...some thing like this.
#position 1: "blank" view will be diplayed
#rest positions: "triplistrow" will bw displayed
#Override
public View getView(final int position, View convertView, final ViewGroup parent)
{
View rowView = convertView;
if(position !=1)
{
if (rowView == null)
{
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.triplistrow, null);
ViewHolder viewHolder = new ViewHolder();
viewHolder.no = (TextView) rowView.findViewById(R.id.TVTripNo);
viewHolder.name = (TextView) rowView.findViewById(R.id.TVTripName);
viewHolder.date = (TextView) rowView.findViewById(R.id.TVTripDate);
rowView.setTag(viewHolder);
}
}
else
{
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.triplistrow, null);
ViewHolder viewHolder = new ViewHolder();
rowView.setTag(viewHolder);
}
if(position!=1)
{
final ViewHolder holder = (ViewHolder) rowView.getTag();
holder.no.setText(String.valueOf(position+1));
holder.name.setText(values.get(position).name);
holder.date.setText(values.get(position).fromdate + " to " + values.get(position).tilldate);
}else
{
}
return rowView;
}

How can i inflate three layouts in ListView?

I have a custom listView in which i want to inflate three different layouts.
I have seen many questions but they are all for odd and even position but in my listView the layout that should be inflated to the listView depends on other condition and its dynamic like
if (i == 0) i get the first Layout to be inflated and if (i==1) second one and so on,
The variable "i" is equal to the value I would be getting from my main Activity.
public class SocialListAdapter extends ArrayAdapter<Item> {
private Activity activity;
private List<Item> items;
private Item objBean;
private int row;
private int i;
public SocialListAdapter(Activity act, int resource, List<Item> arrayList) {
super(act, resource, arrayList);
// TODO Auto-generated constructor stub
this.activity= act;
this.items = arrayList;
this.row = resource;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getCount() {
return items.size();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
objBean = items.get(position);
i = objBean.getI();
if (view == null) {
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(i == 0){
view = inflater.inflate(R.layout.list, null);
} else if (i == 1){
view = inflater.inflate(R.layout.row1, null);
}
holder = new ViewHolder();
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
I tried this code but dint work...
I was getting an error when i scroll through the listView arrayoutofbondexception
getItemViewType has to return a number from 0 to getViewTypeCount.
Try this :
public class CustomAdapter extends BaseAdapter {
private ArrayList<String> comments;
Context mContext;
public CustomAdapter(Context context, ArrayList<String> comments) {
this.mContext = context;
this.comments = comments;
}
public View getView(final int position, View convertView, ViewGroup parent){
String item = comments.get(position);
if (getItemViewType(position) == 0) {
View v = convertView;
if (v == null) {
//GET View 1
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment2, null);
v = viewGroup;
}
//Fill Data for Ist view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
} else if (getItemViewType(position) == 1) {
View v = convertView;
if (v == null) {
//GET View 2
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment1, null);
v = viewGroup;
}
//Fill Data for IInd view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
} else {
//GET View 3
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup viewGroup = (ViewGroup)inflater.inflate(R.layout.item_comment3, null);
v = viewGroup;
}
//Fill Data for IInd view
TextView comm = (TextView) v.findViewById(R.id.comment);
comm.setText(item);
return v;
}
}
#Override
public int getCount() {
return comments.size();
}
#Override
public Object getItem(int position) {
return comments.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
if(position == 0)
return 0;
else if (position == 1)
return 1;
else
return 2;
}
}
You can optimize and make your scrolling smooth by defining view holder :
Try this also
in getItemViewType you can set your condition where do you want to show which type of view.
use "if(position==0)" inside getView method
You can check the condition and inflate the layout that you want.
Here is the example. I have three category depending on it I inflate different layouts.
public class ApplicationAdapter extends BaseAdapter
{
private Context cntx;
/**
* 0 - featured 1- Top rated 2- other
*/
private int whichCategory;
/**
* Constructor
*
* #param context
* #param iwhichCategory
* 0 - featured 1- Top rated 2- other
*/
public ApplicationAdapter(Context context, int iwhichCategory)
{
cntx = context;
whichCategory = iwhichCategory;
}
#Override
public int getCount()
{
return entApplications.size();
}
#Override
public Object getItem(int position)
{
return null;
}
#Override
public long getItemId(int position)
{
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
// This a new view we inflate the new layout
LayoutInflater inflater = (LayoutInflater) cntx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (whichCategory == 0)
convertView = inflater.inflate(R.layout.featured_app_grid_item, parent, false);
if (whichCategory == 1)
convertView = inflater.inflate(R.layout.other_app_grid_item, parent, false);
if (whichCategory == 2)
convertView = inflater.inflate(R.layout.other_app_grid_item1, parent, false);
}
}
}

Listview with or without convertview == null

I have a dilemma using
if (convertview==null){
(my code)
}
or not. Without this piece of code, my listview is not very fast, it locks for a few ms sometimes and you can easy notice this in use. It just doesn't work how its meant to work.
But when this piece of code, my listitems will start recounting after a while (10 or so) and i have a few returning listitems in my list (with the header i used). I used this tutorial for getting my listview with sections link. The length of the list is good.
Ofcourse my list is totally useless with a view repeating items (nicely sectioned by the way) but i also dont want it to be slow.
Does anyone know what to do?
Below is my adapter:
public class DelftAdapter extends BaseAdapter {
private Activity activity;
private List<ListItem> listItems;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
private final int[] bgColors = new int[] { R.color.list_odd, R.color.list_even };
public DelftAdapter(Activity a, ArrayList<ListItem> li) {
activity = a;
listItems = li;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return listItems.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
final ListItem li = listItems.get(position);
if (li != null) {
if(li.isSection()){ // is sectionheader
SectionItem si = (SectionItem)li;
vi = inflater.inflate(R.layout.sectionedlistitem, null);
vi.setOnClickListener(null);
vi.setOnLongClickListener(null);
vi.setLongClickable(false);
final TextView sectionView = (TextView) vi.findViewById(R.id.list_header_title);
sectionView.setText(si.getTitle());
}else{ // no sectionheader
ListData ld = (ListData)li;
vi = inflater.inflate(R.layout.singlelistitem, null);
TextView tvNames=(TextView)vi.findViewById(R.id.tvname);
TextView tvTip=(TextView)vi.findViewById(R.id.tvtip);
ImageView image=(ImageView)vi.findViewById(R.id.image);
tvNames.setText(ld.name);
tvTip.setText(ld.tip);
if (listItems.get(position) != null ){
imageLoader.DisplayImage(ld.photoUrl, image);
}
else{
image.setImageURI(Uri.fromFile(new File("//assets/eten.png")));
}
// alternating colors
int colorPos = position % bgColors.length;
vi.setBackgroundResource(bgColors[colorPos]);
}
}
return vi;
}
}
Consider using getItemViewType() and getViewTypeCount() with recycling convertView. Those are used when you have list items with various layouts. You definitely should recycle convertView.
See also http://android.amberfog.com/?p=296
In your case:
private static final int TYPE_ITEM = 0;
private static final int TYPE_SECTION = 1;
#Override
public int getItemViewType(int position) {
return listItems.get(position).isSection() ? TYPE_SECTION : TYPE_ITEM
}
#Override
public int getViewTypeCount() {
return 2; // sectionheader and regular item
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
if (convertView == null) {
switch (type) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.singlelistitem, null);
...
break;
case TYPE_SECTION:
convertView = mInflater.inflate(R.layout.sectionedlistitem, null);
...
break;
}
} else {
...
}
return convertView;
}
Also use ViewHolder pattern to achieve better performance.

Categories

Resources