So I have the following view
list_view_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layoutSessionItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/lblGroupDate"
style="#style/CustomText.GrayTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/lblCourseCode"
style="#style/CustomText.GrayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/lblSessionCode"
style="#style/CustomText.GrayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
So basically I get the following data from the database:
COURSE_CODE
SESSION_CODE
SESSION_DATE
I need to group the ListView items by SESSION_DATE, what I do is I keep a variable called "PreviousDate" and I compare it to the current SESSION_DATE, if it's different then I enable the header with the ID: "lblGroupDate" if the dates are the same I Hide "lblGroupDate".
Here's my Adapter:
public class SessionListAdapter extends BaseAdapter {
private Date PreviousDate = new Date();
static class ViewHolder {
TextView lblGroupDate;
TextView lblCourseCode;
TextView lblSessionCode;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_view_item, null);
holder = new ViewHolder();
holder.lblGroupDate = (TextView) convertView.findViewById(R.id.lblGroupDate);
holder.lblCourseCode = (TextView) convertView.findViewById(R.id.lblCourseCode);
holder.lblSessionCode = (TextView) convertView.findViewById(R.id.lblSessionCode);
} else {
holder = (ViewHolder)convertView.getTag();
}
SessionData session = (SessionData) getItem(position);
if(session != null) {
Date sessionDate = session.SESSION_DATE;
if (!mPrevDate.equals(sessionDate)) {
PreviousDate = sessionDate;
// HIDE GROUP HEADER
holder.lblGroupDate.setVisibility(View.GONE);
}
else {
// SHOW GROUP HEADER
holder.lblGroupDate.setVisibility(View.VISIBLE);
}
holder.lblCourseCode.setText(session.COURSE_CODE);
holder.lblSessionCode.setText(session.SESSION_CODE);
}
}
}
Here's the PROBLEM:
Lets say I have 20 records, when I scroll down and then scroll up again the rows that had the Group Header (lblGroupDate) enabled get shifted up without a header, it's the header was shifted to the next 3 rows. Why is this happening?
It's happening because getView() can be called at anytime and out-of-order from your data-set. You need to create a data-set that the adapter can be backed by. Just sorting your adapter out. Will update answer with code in a moment.
SessionListAdapter.java
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class SessionListAdapter extends BaseAdapter
{
private static final int TYPE_GROUP = 0;
private static final int TYPE_SESSION = 1;
private static final int MAX_TYPES = 2;
private final LayoutInflater mLayoutInflater;
private ArrayList<SessionViewData> mData;
public SessionListAdapter(Context context, List<SessionData> sessionData)
{
mLayoutInflater = LayoutInflater.from(context);
updateSessionViewData(sessionData);
}
public void updateSessionViewData(List<SessionData> sessionData)
{
Date previousDate = new Date();
ArrayList<SessionViewData> data = new ArrayList<SessionViewData>();
for(SessionData session: sessionData){
if(!previousDate.equals(session.SESSION_DATE)){
data.add(new SessionViewData(TYPE_GROUP, session));
previousDate = session.SESSION_DATE;
}
data.add(new SessionViewData(TYPE_SESSION, session));
}
mData = data;
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
SessionViewData data = mData.get(position);
if(convertView == null){
final int layoutId;
switch(data.type){
case TYPE_GROUP: layoutId = R.layout.list_session_group; break;
case TYPE_SESSION: layoutId = R.layout.list_session_item; break;
default:
throw new IllegalArgumentException("Bad type for: " + data.session);
}
convertView = mLayoutInflater.inflate(layoutId, parent, false);
}
switch(data.type){
case TYPE_GROUP:
TextView lblGroupDate = ((TextView)convertView.findViewById(R.id.lblGroupDate));
lblGroupDate.setText(DateUtils.formatDateTime(convertView.getContext(),
data.session.SESSION_DATE.getTime(), DateUtils.FORMAT_SHOW_DATE));
break;
case TYPE_SESSION:
TextView lblCourseCode = (TextView)convertView.findViewById(R.id.lblCourseCode);
lblCourseCode.setText(data.session.COURSE_CODE);
TextView lblSessionCode = (TextView)convertView.findViewById(R.id.lblSessionCode);
lblSessionCode.setText(data.session.SESSION_CODE);
break;
}
return convertView;
}
#Override
public Object getItem(int position)
{
return mData.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public int getItemViewType(int position)
{
return mData.get(position).type;
}
#Override
public int getViewTypeCount()
{
return MAX_TYPES;
}
#Override
public int getCount()
{
return mData.size();
}
static class SessionViewData
{
int type;
SessionData session;
public SessionViewData(int type, SessionData session)
{
this.type = type;
this.session = session;
}
}
}
list_session_group.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lblGroupDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/CustomText.GrayTitle"
/>
list_session_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layoutSessionItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/lblCourseCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/CustomText.GrayText"
/>
<TextView
android:id="#+id/lblSessionCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/CustomText.GrayText"
/>
</LinearLayout>
Related
How to use the two View one by one for a RecyclerView. Here's the code I'm using:
Adapter Code To Fetch The Layout:
public tagsfeatures onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_item_layout,parent,false);
return new my_item_layout(view);
}
XML For View:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:background="#drawable/noti_bg_item"
>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="#+id/tag_features_img"
android:layout_margin="10dp"
></ImageView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/tag_features_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:text="Awesome Title"
android:textSize="17sp"
android:layout_marginTop="10dp"
app:fontFamily="#font/roboto"></TextView>
<TextView
android:id="#+id/tag_features_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:layout_marginTop="10dp"
android:text="I'm the description text here"
android:textSize="12sp"
app:fontFamily="#font/roboto"></TextView>
</LinearLayout>
</LinearLayout>
Layout Successfully shows and items are showing in the recycler view. Now I need to display the items differently.
Let's say:
//recyclerview
Item0// Image here, Title, Description //Item0
Item1// Title, Description, Image //Item1
Item2// Image here, Title, Description //Item2
Item3// Title, Description, Image //Item3
//recycler view
I can create Two layouts for the recycler view items to display the content differently. But currently, I'm not able to use one layout one by one to loop the Items.
Update: I've used the following code but it returns the 2nd layout only:
#Override
public int getItemViewType(int position) {
final int dataObj = '0';
if(dataObj == 0 ){
return 0;
}
else{
return 1;
}
}
#Override
public tagsfeatures onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == 0) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.1stlayout, parent, false);
return new tagsfeatures(view);
}
else{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.2ndlayout, parent, false);
return new tagsfeatures(view);
}
}
Entire Adapter:
package com.example.videoseo.adapters;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.videoseo.R;
import com.example.videoseo.activities.MainActivity;
import com.example.videoseo.activities.theact;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class TagFeaturesAdapter extends RecyclerView.Adapter<TagFeaturesAdapter.tagsfeatures> {
List<Integer> imageList = new ArrayList<>();
List<String> titleList = new ArrayList<String>();
List<String> desList = new ArrayList<String>();
public TagFeaturesAdapter(List<Integer> imageList, List<String> titleList, List<String> desList) {
this.imageList = imageList;
this.titleList = titleList;
this.desList = desList;
}
#NonNull
#Override
public int getItemViewType(int position) {
int thepos = imageList.get(position);
switch (thepos){
case 0:
return 0;
default:
return 1;
}
}
#Override
public tagsfeatures onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
int postype = viewType;
switch (postype) {
case 0:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tag_features, parent, false);
return new tagsfeatures(view);
case 1:
View view2 = LayoutInflater.from(parent.getContext()).inflate(R.layout.tag_features0, parent, false);
return new tagsfeatures(view2);
default:
View view3 = LayoutInflater.from(parent.getContext()).inflate(R.layout.tag_features, parent, false);
return new tagsfeatures(view3);
}
}
#Override
public void onBindViewHolder(#NonNull tagsfeatures holder, final int position) {
holder.toolimg.setImageResource(imageList.get(position));
holder.title.setText(titleList.get(position));
holder.description.setText(desList.get(position));
}
#Override
public int getItemCount() {
return imageList.size();
}
public class tagsfeatures extends RecyclerView.ViewHolder{
ImageView toolimg;
TextView title, description;
public tagsfeatures(#NonNull View itemView) {
super(itemView);
toolimg = (ImageView) itemView.findViewById(R.id.tag_features_img);
title = (TextView) itemView.findViewById(R.id.tag_features_title);
description = (TextView) itemView.findViewById(R.id.tag_features_desc);
}
}
}
in your recyclerview adapter override getItemViewType function
override fun getItemViewType(position: Int): Int {
return when(dataList.get(position).type) {
"typeOne" -> 0;
"typeTwo" -> 1;
else -> 2
}
}
the type returned from this function will be passed to onCreateViewHolder function. Based on the viewType you can load different layout files
I am trying to have 2 layout in one RecyclerView
I have a recycler view list which is called Bookmark and it is parsed from an xml and this is all working , but I wanna in this recyclerview to put another layout which contains a button and that can be clickable.
Like in the photo the icons are from recyclerview and the plus button need to be compatible with the list, if the list is larger or smaller the button will be compatible with the space of list.
This is my new code for the Adapter which depends on the answer #LluisFelisart
And this is the error
ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
ArrayList<Bookmark> arrayList = new ArrayList<>();
public MyAdapter(Context context, ArrayList<Bookmark> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
public class ViewHolder0 extends RecyclerView.ViewHolder {
TextView tvName,tvId,tvSearchUrl,tvNativeUrl;
ImageView tvIcon;
public ViewHolder0(#NonNull View itemView) {
super(itemView);
tvName=itemView.findViewById(R.id.textView);
tvIcon = itemView.findViewById(R.id.image_view);
/* tvId=itemView.findViewById(R.id.tvId);
tvSearchUrl=itemView.findViewById(R.id.tvSearchUrl);
tvNativeUrl=itemView.findViewById(R.id.tvNativeUrl);*/
}
}
public class ViewHolder2 extends RecyclerView.ViewHolder {
ImageView tvAddBookmark;
public ViewHolder2(#NonNull View itemView) {
super(itemView);
tvAddBookmark = itemView.findViewById(R.id.image_button_add);
}
}
#Override
public int getItemViewType(int position) {
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
return position % 2 * 2;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.grid_item, viewGroup, false);
switch (i) {
case 0: return new ViewHolder0(viewGroup);
case 2: return new ViewHolder2(viewGroup);
}
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolder0 viewHolder0 = (ViewHolder0) holder;
((ViewHolder0) holder).tvName.setText(arrayList.get(position).getName());
((ViewHolder0) holder).tvIcon.setImageResource(arrayList.get(position).getIcon());
break;
case 2:
ViewHolder2 viewHolder2 = (ViewHolder2) holder;
}
((ViewHolder0) holder).tvIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent;
intent = new Intent(context, BookmarkActivity.class);
v.getContext().startActivity(intent);
}
});
((ViewHolder0) holder).tvIcon.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Intent intent = new Intent(context, ActivityBookmarksFavorites.class);
v.getContext().startActivity(intent);
return false;
}
});
}
#Override
public int getItemCount() {
return arrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName,tvId,tvSearchUrl,tvNativeUrl;
ImageView tvIcon;
public ViewHolder(#NonNull View itemView) {
super(itemView);
tvName=itemView.findViewById(R.id.textView);
tvIcon = itemView.findViewById(R.id.image_view);
/* tvId=itemView.findViewById(R.id.tvId);
tvSearchUrl=itemView.findViewById(R.id.tvSearchUrl);
tvNativeUrl=itemView.findViewById(R.id.tvNativeUrl);*/
}
}
}
This is the grid item layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:visibility="visible"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/image_view"
style="#style/BookmarkIconIv" />
<TextView android:id="#+id/textView"
android:layout_marginTop="1.0dip"
style="#style/BookmarkTextTv" />
</LinearLayout>
This is the layout of button which I want to be in the recycler view
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="#+id/image_button_add"
android:layout_width="45dp"
android:layout_height="45dp"
android:src="#drawable/ic_add_black_24dp"
android:background="#color/transparent" />
</android.support.constraint.ConstraintLayout>
This is the Fragment which the recycler view it is shown
public class FragmentBookmark extends Fragment {
ArrayList<Bookmark> arrayList = new ArrayList<>();
XmlPullParser pullParser;
MyAdapter myAdapter;
View paramView;
RecyclerView myRecyclerView;
private Context mContext;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
#Nullable
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
paramView = inflater.inflate(R.layout.bookmark, container, false);
myRecyclerView = paramView.findViewById(R.id.myRecyclerView);
myRecyclerView.setLayoutManager(new GridLayoutManager(mContext, 4));
myRecyclerView.setHasFixedSize(true);
myAdapter = new MyAdapter(mContext, arrayList);
myRecyclerView.setAdapter(myAdapter);
try {
XmlPullParser xpp = getResources().getXml(R.xml.bookmarks);
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
if (xpp.getEventType() == XmlPullParser.START_TAG) {
if (xpp.getName().equals("Bookmark")) {
Bookmark bookmark = new Bookmark();
bookmark.setName(xpp.getAttributeValue(null, "name"));
int drawableResourceId = getResources().getIdentifier(xpp.getAttributeValue(null, "icon"),"drawable", mContext.getPackageName());
bookmark.setIcon(drawableResourceId);
arrayList.add(bookmark);
}
}
xpp.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
myAdapter.notifyDataSetChanged();
return paramView;
}
}
This is the layout bookmark which contains recyclerview
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/myRecyclerView"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:fillViewport="false">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Here is the answer
Follow this steps
First Create a two layout for your Multiple viewType
SAMPLE CODE
layout.layout_one
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:cardCornerRadius="15dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="Name : " />
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="Icon : " />
<ImageView
android:id="#+id/tvIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:padding="10dp"
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="Id : " />
<TextView
android:id="#+id/tvId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="SearchUrl : " />
<TextView
android:id="#+id/tvSearchUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="NativeUrl : " />
<TextView
android:id="#+id/tvNativeUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
layout.button_two
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imgButton"
android:layout_width="50dp"
android:layout_height="50dp" />
</LinearLayout>
Now you need to create two RecyclerView.ViewHolder for your both viewType
Now you need to Override getItemViewType()
it Return the viewType of the item at position for the purposes of view recycling.
Now in your onCreateViewHolder() method you need to return your instance of your ViewHolder based on your viewType which you will get using getItemViewType() method
Than in your onBindViewHolder() method based your viewType set your view property
here is the sample code of RecyclerView.Adapter with multiple view types
DataAdapter
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
public class DataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
ArrayList<Bookmark> arrayList = new ArrayList<>();
public static final int ITEM_TYPE_ONE = 0;
public static final int ITEM_TYPE_TWO = 1;
public DataAdapter(Context context, ArrayList<Bookmark> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = null;
// check here the viewType and return RecyclerView.ViewHolder based on view type
if (viewType == ITEM_TYPE_ONE) {
view = LayoutInflater.from(context).inflate(R.layout.layout_one, parent, false);
return new ViewHolder(view);
} else if (viewType == ITEM_TYPE_TWO) {
view = LayoutInflater.from(context).inflate(R.layout.button_two, parent, false);
return new ButtonViewHolder(view);
}else {
return null;
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
final int itemType = getItemViewType(position);
// First check here the View Type
// than set data based on View Type to your recyclerview item
if (itemType == ITEM_TYPE_ONE) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.tvName.setText(arrayList.get(position).getName());
viewHolder.tvIcon.setImageResource(arrayList.get(position).getIcon());
viewHolder.tvSearchUrl.setText(arrayList.get(position).getSearchUrl());
viewHolder.tvNativeUrl.setText(arrayList.get(position).getNativeUrl());
} else if (itemType == ITEM_TYPE_TWO) {
ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;
buttonViewHolder.imgButton.setImageResource(arrayList.get(position).getIcon());
}
}
#Override
public int getItemViewType(int position) {
// based on you list you will return the ViewType
if (arrayList.get(position).getViewType() == 0) {
return ITEM_TYPE_ONE;
} else {
return ITEM_TYPE_TWO;
}
}
#Override
public int getItemCount() {
return arrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName, tvId, tvSearchUrl, tvNativeUrl;
ImageView tvIcon;
public ViewHolder(#NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
tvIcon = itemView.findViewById(R.id.tvIcon);
tvId = itemView.findViewById(R.id.tvId);
tvSearchUrl = itemView.findViewById(R.id.tvSearchUrl);
tvNativeUrl = itemView.findViewById(R.id.tvNativeUrl);
}
}
public class ButtonViewHolder extends RecyclerView.ViewHolder {
ImageView imgButton;
public ButtonViewHolder(#NonNull View itemView) {
super(itemView);
imgButton = itemView.findViewById(R.id.imgButton);
}
}
}
When you adding data in your list you need to provide the viewtype in list
Make some changes in your Bookmark POJO class
Bookmark POJO class
public class Bookmark
{
String name,id,nativeUrl,searchUrl;
int icon;
int viewType;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public String getNativeUrl() {
return nativeUrl;
}
public void setNativeUrl(String nativeUrl) {
this.nativeUrl = nativeUrl;
}
public String getSearchUrl() {
return searchUrl;
}
public void setSearchUrl(String searchUrl) {
this.searchUrl = searchUrl;
}
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
this.viewType = viewType;
}
#Override
public String toString() {
return "Bookmark{" +
"name='" + name + '\'' +
", icon='" + icon + '\'' +
", id='" + id + '\'' +
", nativeUrl='" + nativeUrl + '\'' +
", searchUrl='" + searchUrl + '\'' +
'}';
}
}
Sample activity code
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private Context mContext;
ArrayList<Bookmark> arrayList = new ArrayList<>();
RecyclerView myRecyclerView;
DataAdapter dataAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
myRecyclerView = findViewById(R.id.myRecyclerView);
myRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
myRecyclerView.setHasFixedSize(true);
dataAdapter = new DataAdapter(mContext, arrayList);
myRecyclerView.setAdapter(dataAdapter);
try {
XmlPullParser xpp = getResources().getXml(R.xml.bookmarks);
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
if (xpp.getEventType() == XmlPullParser.START_TAG) {
if (xpp.getName().equals("Bookmark")) {
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(0) + " * ");
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(1) + " * ");
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(5) + " * ");
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(2) + " * ");
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(3) + " * ");
Log.e("MY_VALUE", " * " + xpp.getAttributeValue(4) + " * ");
Bookmark bookmark = new Bookmark();
bookmark.setName(xpp.getAttributeValue(0));
int drawableResourceId = this.getResources().getIdentifier(xpp.getAttributeValue(1), "drawable", mContext.getPackageName());
bookmark.setIcon(drawableResourceId);
bookmark.setId(xpp.getAttributeValue(2));
bookmark.setSearchUrl(xpp.getAttributeValue(3));
bookmark.setNativeUrl(xpp.getAttributeValue(4));
// here you need to set view type
bookmark.setViewType(0);
arrayList.add(bookmark);
}
}
xpp.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// here i have added second viewType
// you need to set as per your requirement
Bookmark bookmark = new Bookmark();
bookmark.setViewType(1);
bookmark.setIcon(R.drawable.dishu);
arrayList.add(bookmark);
dataAdapter.notifyDataSetChanged();
}
}
NOTE
In the below code i have set second viewType at the last index of Arraylist
you need to set viewType as per your requirement
For more information you can check below articles
Working with RecyclerView and multiple view types
A RecyclerView with multiple item types
Android RecyclerView with Different Child Layouts
Android Pagination Tutorial—Handling Multiple View Types
Heterogenous Layouts inside RecyclerView
Android RecyclerView Example – Multiple ViewTypes
How to create RecyclerView with multiple view type?
Android Multiple row layout using RecyclerView
You can use different layouts on the same RecyclerView , just override adapter getItemViewType() method and return a different int value for the button layout, in your example you should return for example 1 for the normal item and 2 for the button item.
The view type is passed as argument to onCreateViewHolder() method and depending of the viewType value you inflate the normal layout or the button layout.
It seems that you need also make getItemCount() to return one more than the array size
Hope it will help
Here an example:
How to create RecyclerView with multiple view type?
Do this operations in your Activity.
ArrayList<Bookmark> data = new ArrayList<>();
//data.addAll(your array list bookmark); uncomment this line add your all array list of bookmark
Bookmark d = new Bookmark(0);
data.add(d);
mList.setAdapter(new BookMarkAdapter(activity, data));
Try This adapter
public class BookMarkAdapter extends RecyclerView.Adapter {
private Context context;
private ArrayList<Bookmark> data;
public BookMarkAdapter(Context context, ArrayList<Bookmark> data) {
this.context = context;
this.data = data;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == 1)
return new ViewBookmarkHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_with_normal_image_and_textview, parent, false));
else
return new AddBookmarkHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_with_image, parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Bookmark d = data.get(position);
if (d.getType()==1) {
ViewBookmarkHolder viewBookmarkHolder =(ViewBookmarkHolder) holder;
// do your show image and textview operation here
} else {
AddBookmarkHolder addBookmarkHolder =(AddBookmarkHolder) holder;
// do your on click operation here. Like adding new bookmark and update your arraylist and notify data changed for adapter.
}
}
#Override
public int getItemViewType(int position) {
return data.get(position).getType();
}
#Override
public int getItemCount() {
return data.size();
}
}
Update this methods and variables in your Bookmark Pojo
public class Bookmark {
private Integer type;
public Bookmark(Integer type) {
this.type = type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getType() {
if(type==null)
return 1;
return type;
}
}
I'm having a weird problem with my viewholders. I'm trying to create an activity that allows the user to choose multiple dates from datepicker dialog (this works) and then make a list of the chosen dates and add a timepicker for each of them. E.g 22-02-2014 14:00 (the 14:00 depicting a timepicker for that date). So if 5 dates were chosen, 5 rows would appear with each containing a chosen date and its own timepicker.
Currently creating the list from chosen dates works but timepicker won't appear no matter what I attempt. Only the textview containing the date is shown in the holder.
I've been searching for solution for several hours but I can't seem to find similar issues which indicates something is seriously wrong on my end. Just can't fathom what that might be.
The relevant code (omitting the activity itself since it works as supposed with list and all):
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.TimePicker;
public class RandomListAdapter extends ArrayAdapter<String> {
static Context mContext;
private static class ViewHolder {
private View row;
private TextView date;
private TimePicker tp;
public ViewHolder(View row) {
this.row = row;
}
public void setDate(String date) {
this.date.setText(date);
}
public void initTp(TimePicker tp){
this.tp = tp;
}
public void setHour(int h) {
tp.setCurrentHour(h);
}
public void setMin(int m){
tp.setCurrentMinute(m);
}
public TextView getDate() {
if(date==null) {
date = (TextView) row.findViewById(R.id.rr_date_item);
}
return date;
}
public TimePicker getTp() {
if(tp==null) {
tp = (TimePicker) row.findViewById(R.id.rr_tp);
}
return tp;
}
}
private static RandomListAdapter instance = null;
private ArrayList<String> items;
public RandomListAdapter(Context context) {
super(context, 0);
mContext = context;
items = new ArrayList<String>();
}
public void updateList(ArrayList<String> l){
items = l;
super.notifyDataSetChanged();
}
#Override
public int getCount() {
return items.size();
}
#Override
public String getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String s = (String)items.get(position);
ViewHolder vH = null;
if(convertView == null){
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
convertView = inflater.inflate(R.layout.rr_row_layout, null);
vH = new ViewHolder(convertView);
vH.date = vH.getDate();
vH.tp = vH.getTp();
convertView.setTag(vH);
}
else {
vH = (ViewHolder) convertView.getTag();
}
if(vH != null && s != null) {
vH.setDate(s);
}
return convertView;
}
public static RandomListAdapter getInstance(Context mContext) {
if(instance == null) {
instance = new RandomListAdapter(mContext);
}
return instance;
}
}
The xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:showDividers="middle"
android:divider="?android:attr/dividerHorizontal" >
<TextView
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:id="#+id/rr_date_item"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical|left"
android:ellipsize="end"
android:singleLine="true"
android:layout_weight="0"
android:textAlignment="gravity"
android:textSize="18sp" />
<TimePicker
android:id="#+id/rr_tp"
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
Thanks in advance and my apologies if I'm missing something obvious.
This could be a problem
android:layout_height="0dip"
on your TimePicker. Change the height to wrap_content. Only the width needs to be 0dp when using weight.
I want to do custom listview(one image & one text per item) with custom radio buttons(Single selection item option).
My Code:
listview.setAdapter(new myAdapter(getApplicationContext(),
R.layout.mycontent, text));
my Adapter class:
public class myMessageAdapter extends ArrayAdapter<String> {
private LayoutInflater mInflater;
private String[] mText = null;
long id;
private int mViewResourceId;
public myMessageAdapter(Context ctx, int viewResourceId,
ArrayList<String> textList) {
super(ctx, viewResourceId);
mInflater = (LayoutInflater) ctx
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
String[] message = textList.toArray(new String[textList.size()]);
mText = message;
mViewResourceId = viewResourceId;
}
#Override
public int getCount() {
return mText .length;
}
#Override
public String getItem(int position) {
return mText [position];
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = mInflater.inflate(mViewResourceId, null);
TextView MESSAGE = (TextView) convertView.findViewById(R.id.message);
id = getItemId(position);
MESSAGE.setText(mText [position]);
return convertView;
}
}
My output list item should be:
<radioButton> <Text> <Image>.Single choice selection.How could i do that?
Try this Solution
package com.example.demo;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.TextView;
public class ListViewDemo extends Activity {
ListView LSOne;
int[] _intRadio = new int[20];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_radio);
LSOne = (ListView) findViewById(R.id.LSOne);
LSOne.setAdapter(new LsAdapter());
}
public class LsAdapter extends BaseAdapter {
#Override
public int getCount() {
return 20;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public int getViewTypeCount() {
// TODO Auto-generated method stub
return 20;
}
#Override
public int getItemViewType(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
int _intPosition = getItemViewType(position);
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.listview_radio_raw, null);
holder = new ViewHolder();
holder.code = (TextView) convertView
.findViewById(R.id.textView1);
holder.name = (RadioButton) convertView
.findViewById(R.id.radioButton1);
convertView.setTag(holder);
holder.name.setId(_intPosition);
holder.name.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
for (int i = 0; i < _intRadio.length; i++) {
if (i == v.getId()) {
_intRadio[i] = 1;
} else {
_intRadio[i] = 0;
}
}
notifyDataSetChanged();
}
});
} else {
holder = (ViewHolder) convertView.getTag();
}
if (_intRadio[_intPosition] == 1) {
holder.name.setChecked(true);
} else {
holder.name.setChecked(false);
}
return convertView;
}
private class ViewHolder {
TextView code;
RadioButton name;
Button btn;
}
}
}
listview_radio_raw.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dip"
android:orientation="horizontal" >
<RadioButton
android:id="#+id/radioButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#drawable/button_radio"
android:paddingLeft="60dip"
android:text="RadioButton" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
</LinearLayout>
button_radio.xml
//this xml add in your drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/radio_on" android:state_checked="true" android:state_pressed="false"/>
<item android:drawable="#drawable/radio_off" android:state_checked="false" android:state_pressed="false"/>
<item android:drawable="#drawable/radio_on_pressed" android:state_checked="true" android:state_pressed="true"/>
<item android:drawable="#drawable/radio_off_pressed" android:state_checked="false" android:state_pressed="true"/>
</selector>
Download all image for thislink
In your getview inflate custom layout
public View getView(int arg0, View arg1, ViewGroup arg2) {
ViewHolder vh;
vh= new ViewHolder();
if(arg1==null )
{
arg1=mInflater.inflate(R.layout.customlistview, arg2,false);
vh.iv1= (ImageView)arg1.findViewById(R.id.ivs);
vh.rb= (RadioButton) arg1.findViewById(R.id.radioButton1);
vh.tv= (TextView)arg1.findViewById(R.id.textView1);
}
else
{
arg1.setTag(vh);
}
//set data to textview, radiobutton and imageview.
return arg1;
}
static class ViewHolder
{
TextView tv;
RadioButton rb;
ImageView iv1;
}
customlistview.xml. Custom layout for each row.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:cacheColorHint="#000000"
>
<RadioButton
android:id="#+id/radioButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RadioButton" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<ImageView
android:id="#+id/ivs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
I'm struggling with assigning my listview row more than one textview.
I want my row to have one textview for a person's name, one for address and one for age - but I'm not succeeding in doing so.
If someone could provide me with a simple example, that would be great.
Thanks!
Here's my custom ArrayAdapter
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater = null;
private ArrayList<Invoice> peopleList;
private final class ViewHolder {
TextView kidLabel;
TextView restLabel;
TextView fristLabel;
TextView amountLabel;
}
private ViewHolder mHolder = null;
public MyAdapter(Context context) {
Context mContext = context;
mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return peopleList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
mHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.row, null);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder) convertView.getTag();
}
mHolder.kidLabel = (TextView) convertView.findViewById(R.id.kidLabel);
mHolder.kidLabel.setText(peopleList.get(position).getKID());
mHolder.fristLabel = (TextView) convertView
.findViewById(R.id.fristLabel);
mHolder.fristLabel.setText(peopleList.get(position).getDueDate());
mHolder.restLabel = (TextView) convertView.findViewById(R.id.restLabel);
mHolder.restLabel.setText(peopleList.get(position).getDueAmount());
mHolder.amountLabel = (TextView) convertView
.findViewById(R.id.amountLabel);
mHolder.amountLabel.setText(peopleList.get(position).getDueAmount());
return convertView;
}
}
And Here's my custom row xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:src="#drawable/cellback" android:scaleType="fitXY"/>
<TextView
android:id="#+id/kidLabel"
android:layout_width="160dip"
android:layout_height="25dip"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:textSize="13dip" android:textColor="#333"/>
<TextView
android:id="#+id/fristLabel"
android:layout_width="160dip"
android:layout_height="20dip"
android:layout_marginTop="25dip"
android:textSize="11dip" android:textColor="#999"/>
<TextView
android:id="#+id/amountLabel"
android:layout_width="160dip"
android:layout_height="25dip"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:textSize="13dip" android:layout_marginLeft="160dip" android:gravity="right" android:textColor="#333"/>
<TextView
android:id="#+id/restLabel"
android:layout_width="160dip"
android:layout_height="20dip"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:textSize="11dip" android:layout_marginLeft="160dip" android:layout_marginTop="25dip" android:gravity="right" android:textColor="#999"/>
</RelativeLayout>
First,you should create a xml to describe what your list cell likes,called cell.xml,for example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" android:background="#color/spink">
<TextView
android:id="#+id/name_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/address_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Address"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Second,create a adapter.It helps your listview to show data:
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater = null;
private ArrayList<People> peopleList;
private final class ViewHolder {
TextView nameTextView;
TextView addressTextView;
}
private ViewHolder mHolder = null;
public MyAdapter(Context context) {
mContext = context;
mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return peopleList.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
mHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.cell, null);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder)convertView.getTag();
}
mHolder.nameTextView (TextView)convertView.findViewById(R.id.name_textView);
mHolder.nameTextView.setText(peopleList.get(position).getName());
mHolder.addressTextView = (TextView)convertView.findViewById(R.id.address_textView);
mHolder.addressTextView.setText(peopleList.get(position).getAddress());
return convertView;
}
}
Finally,when you want to show the data,do this in your activity:
listView.setAdapter(new MyAdapter());
hope it helps you.
You need to create a custom listview layout and a custom array adapter (I have also created a custom class to work with the layout).
An example of this is:
Custom class that works with the custom listview layout:
package id10778734.sceresini.week4.exercise3.views;
import id10778734.sceresini.week4.exercise3.R;
import id10778734.sceresini.week4.exercise3.Constants;
import id10778734.sceresini.week4.exercise3.tasks.Task;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class TaskListItem extends LinearLayout {
private Task mTask;
private ImageView mEmailImageView;
private ImageView mPriorityImageView;
private TextView mTaskNameTextView;
private TextView mTaskResponsibleTextView;
private ImageView mDeleteImageView;
public TaskListItem (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
mEmailImageView = (ImageView) findViewById(R.id.list_row_layout_email_button);
mPriorityImageView = (ImageView) findViewById(R.id.list_row_layout_priority_icon);
mTaskNameTextView = (TextView) findViewById(R.id.list_row_layout_task_name);
mTaskResponsibleTextView = (TextView) findViewById(R.id.list_row_layout_responsible);
mDeleteImageView = (ImageView) findViewById(R.id.list_row_layout_delete_button);
}
public void setTask (Task task) {
mTask = task;
mEmailImageView.setTag(task);
switch (task.getPriority()) {
case Constants.LOW:
mPriorityImageView.setImageResource(R.drawable.low);
break;
case Constants.MEDIUM:
mPriorityImageView.setImageResource(R.drawable.medium);
break;
case Constants.HIGH:
mPriorityImageView.setImageResource(R.drawable.high);
break;
}
mTaskNameTextView.setText(task.getName());
mTaskResponsibleTextView.setText(task.getResponsible());
mDeleteImageView.setTag(task);
}
public Task getTask () {
return mTask;
}
public ImageView getDeleteImageView () {
return mDeleteImageView;
}
public ImageView getEmailImageView () {
return mEmailImageView;
}
}
the custom layout xml file:
<?xml version="1.0" encoding="UTF-8"?>
<id10778734.sceresini.week4.exercise3.views.TaskListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dip" >
<ImageView
android:id="#+id/list_row_layout_email_button"
android:layout_width="36dip"
android:layout_height="36dip"
android:layout_gravity="center_vertical"
android:layout_marginLeft="4dip"
android:layout_marginRight="5dip"
android:contentDescription="#string/list_row_email_icon_description"
android:src="#android:drawable/ic_dialog_email" />
<ImageView
android:id="#+id/list_row_layout_priority_icon"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_gravity="center_vertical"
android:layout_marginLeft="4dip"
android:layout_marginRight="10dip"
android:contentDescription="#string/view_task_priority_icon_description" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" >
<TextView
android:id="#+id/list_row_layout_task_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/list_row_layout_responsible"
android:paddingRight="5dip"
android:textAppearance="#style/list_row_task_name" />
<TextView
android:id="#id/list_row_layout_responsible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/list_row_layout_delete_button"
android:maxWidth="100dip"
android:textAppearance="#style/list_row_task_responsible" />
<ImageView
android:id="#id/list_row_layout_delete_button"
android:layout_width="36dip"
android:layout_height="36dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:contentDescription="#string/delete_task_icon_description"
android:src="#drawable/task_delete" />
</RelativeLayout>
</id10778734.sceresini.week4.exercise3.views.TaskListItem>
And the custom array adapter:
package id10778734.sceresini.week4.exercise3;
import id10778734.sceresini.week4.exercise3.R;
import id10778734.sceresini.week4.exercise3.tasks.Task;
import id10778734.sceresini.week4.exercise3.views.TaskListItem;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class TaskListAdapter extends BaseAdapter
{
private ArrayList<Task> mTasks;
private Context mContext;
private AlertDialog unsavedChangesDialog;
private TaskManagerApplication mApp;
public TaskListAdapter(Context context, ArrayList<Task> tasks) {
super();
mApp = (TaskManagerApplication) context.getApplicationContext();
mContext = context;
mTasks = tasks;
}
#Override
public int getCount()
{
return mTasks.size();
}
#Override
public Task getItem(int position)
{
return (null == mTasks) ? null : mTasks.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
TaskListItem tli;
if (null == convertView)
{
tli = (TaskListItem) View.inflate(mContext, R.layout.list_row_layout, null);
} else
{
tli = (TaskListItem) convertView;
}
tli.setTask(mTasks.get(position));
tli.getEmailImageView().setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
emailTask(v);
}
});
tli.getDeleteImageView().setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
deleteTask(v);
}
});
return tli;
}
/**
* deleteTask() prompts the user with an alert dialog and presents them with
* the option to continue with the delete request or to cancel the request.
* Upon confirmation, the selected Task object which is retrieved from the
* deleteTaskIcon tag, will be removed from the list of currentTasks.
*
* #param v
*/
protected void deleteTask(View v)
{
final Task t = (Task) v.getTag();
String alertMessage = String.format(mContext.getString(R.string.delete_task_message), t.getName());
unsavedChangesDialog = new AlertDialog.Builder(mContext).setTitle(R.string.delete_task_title).setMessage(alertMessage)
// Delete the task and refresh the adapter.
.setPositiveButton(R.string.delete_task_delete, new AlertDialog.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1)
{
// Remove task from array list
mTasks.remove(t);
// Remove task from Database
mApp.deleteTask(t);
// Display toast message stating the task was deleted
mApp.displayToast(Constants.TASK_DELETED);
// Update listView with modified adapter
forceReload();
}
})
// Cancel the delete request and do nothing.
.setNegativeButton(R.string.delete_task_cancel, new AlertDialog.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1)
{
unsavedChangesDialog.cancel();
}
}).create();
unsavedChangesDialog.show();
}
/**
* emailTask() populates the selected task into an intent which allows the
* user to select a client to send the task. This is formatted for email
* clients.
*
* #param v
*/
protected void emailTask(View v)
{
// Retrieve the task that is allocated to this RowView
final Task t = (Task) v.getTag();
// Instantiate the intent that will be used to call the email client
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
// Variables/Arrays to hold email attributes
String[] emailRecipients = { mContext.getString(R.string.email_task_to_email) };
String emailType = mContext.getString(R.string.email_task_type);
String emailTitle = mContext.getString(R.string.email_task_title);
String emailSubject = String.format(mContext.getString(R.string.email_task_subject), t.getName());
String emailMessage = String.format(mContext.getString(R.string.email_task_message), t.getResponsible(), t.getName(), mContext.getString(t.getPriorityStringId()));
// Add the email attributes to the intent
emailIntent.setType(emailType);
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, emailRecipients);
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, emailSubject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, emailMessage);
// Start intent
mContext.startActivity(Intent.createChooser(emailIntent, emailTitle));
}
/**
* Refreshes the ListView with the modified dataset.
*/
public void forceReload()
{
notifyDataSetChanged();
}
}