Getting wrong position in custom ListView while scrolling - xamarin

I am getting wrong position in custom ListView while scrolling.
I have tried the ViewHolder pattern and an ArrayAdapter but both giving the same problem.
If I reproduce the code using Java then I am getting the proper position while scrolling.
So is it Xamarin architecture bug ?
Below is my sample code:
Activity Class
namespace ArrayAdapterDemoApp
{
[Activity(Label = "ArrayAdapterDemoApp", MainLauncher = true,
Icon ="#drawable/icon")]
public class MainActivity : Activity
{
private static List<DataBean> _ItemsList = new List<DataBean>();
private static CustomAdapter _adapter;
private ListView _listview;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
_listview = FindViewById<ListView>(Resource.Id.mylist);
DataBean obj1 = new DataBean();
obj1.Name = "AA";
obj1.City = "11";
_ItemsList.Add(obj1);
DataBean obj2 = new DataBean();
obj2.Name = "BB";
obj2.City = "22";
_ItemsList.Add(obj2);
DataBean obj3 = new DataBean();
obj3.Name = "CC";
obj3.City = "33";
_ItemsList.Add(obj3);
...
DataBean obj15 = new DataBean();
obj15.Name = "OO";
obj15.City = "1010";
_ItemsList.Add(obj15);
_adapter = new CustomAdapter(this, _ItemsList);
_listview.Adapter = _adapter;
}
}
}
Custom Adapter
namespace ArrayAdapterDemoApp
{
public class CustomAdapter : ArrayAdapter<DataBean>
{
private class TaskViewHolder : Java.Lang.Object
{
public TextView tvName;
public TextView tvCity;
}
List<DataBean> listData;
Activity _context;
int _position;
public CustomAdapter(Activity context, List<DataBean> dataList)
: base(context, Resource.Layout.adapter_row, dataList)
{
this._context = context;
this.listData = dataList;
}
public override long GetItemId(int position)
{
return position;
}
public override int Count
{
get { return listData.Count; }
}
//With View Holder
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataBean data = listData[position];
TaskViewHolder viewHolder= null; // view lookup cache stored in tag
if (convertView == null)
{
viewHolder = new TaskViewHolder();
LayoutInflater inflater = LayoutInflater.From(_context);
convertView = inflater.Inflate(Resource.Layout.adapter_row, parent, false);
viewHolder.tvName = convertView.FindViewById<TextView>(Resource.Id.text1);
viewHolder.tvCity = convertView.FindViewById<TextView>(Resource.Id.text2);
convertView.Tag = viewHolder;
}
if(viewHolder==null)
{
viewHolder = (TaskViewHolder)convertView.Tag;
}
viewHolder.tvName.Text = data.Name;
viewHolder.tvCity.Text = data.City;
return convertView;
}
}
}
DataBean Class
namespace ArrayAdapterDemoApp
{
public class DataBean
{
public string Name { get; set; }
public string City { get; set; }
}
}

I had also same issue so I resolved it by just Tag the position to the view.for example.
//iv_delete is a imageview
holder.iv_delete.Tag = position;
and get the position from the Tag
For me it was like that
int finalPosition = (Integer)holder.iv_delete.Tag.IntValue();
Enjoy!!!!!

Add in your adapter class this method:
public DataBean GetItemAtPosition(int position)
{
return this.listData[position];
}

You can tag the position of each row to the control to get the correct position or
Another method is by using action event binding to each view row. This also solves duplicate method call issue.
this might be helpful: http://appliedcodelog.blogspot.in/2015/07/working-on-issues-with-listview-in.html#WrongPosition

Related

Setting imageview using picasso in listview android java

Im setting my imageview using picasso, because I just have url for my image. I have using listview to display. In custom adapter I'm setting imageview using picasso. But image is not displayed in listview. I doesn't know where the problem is... Anybody help out to solve this.... Thanks in advance
public class GetReportAdapter extends ArrayAdapter<NewsReport> {
private ArrayList<NewsReport> newsReports;
public GetReportAdapter(Context context, ArrayList<NewsReport> newsReportArrayList)
{
super(context, 0,newsReportArrayList);
this.newsReports=newsReportArrayList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItemView = convertView;
if (listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
}
NewsReport currentReport=getItem(position);
TextView titleTextView=(TextView) listItemView.findViewById(R.id.title_text_view);
titleTextView.setText(currentReport.getTitle());
TextView channelTextView=(TextView) listItemView.findViewById(R.id.news_channel_text_view);
channelTextView.setText(currentReport.getChannel());
ImageView pictureImageView=(ImageView) listItemView.findViewById(R.id.news_pic_image_view);
Log.e("image urlString",currentReport.getImage());
Picasso.with(listItemView.getContext()).load(currentReport.getImage()).into(pictureImageView);
return listItemView;
}
}
Here Im setting listview
public class MainActivity extends AppCompatActivity {
static final Uri CONTENT_URL = Uri.parse("content://com.example.newsreport/newsfeed");
ContentResolver resolver;
ArrayList<NewsReport> newsReportArrayList = new ArrayList<NewsReport>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
resolver = getContentResolver();
Log.e("resolver", "" + resolver);
getNewsFeed();
setAdapter();
}
public void getNewsFeed() {
String id = null;
String title = null;
String content = null;
String channel = null;
String image = null;
String[] projection = new String[]{BaseColumns._ID, "title", "channel", "image", "content"};
Cursor cursor = getContentResolver().query(CONTENT_URL, projection, null, null, null);
String newsFeed = "";
if (cursor.moveToNext()) {
do {
id = cursor.getString(cursor.getColumnIndex(BaseColumns._ID));
title = cursor.getString(cursor.getColumnIndex("title"));
Log.e("title", title);
content = cursor.getString(cursor.getColumnIndex("content"));
Log.e("content", "" + content);
channel = cursor.getString(cursor.getColumnIndex("channel"));
Log.e("channel", "" + channel);
image = cursor.getString(cursor.getColumnIndex("image"));
Log.e("image", "" + image);
newsReportArrayList.add( new NewsReport(channel,title,image));
} while (cursor.moveToNext());
}
}
public void setAdapter()
{
ListView list=(ListView) findViewById(R.id.list);
GetReportAdapter adapter= new GetReportAdapter(this,newsReportArrayList);
list.setAdapter(adapter);
}
}

Why can't I access the Assets folder of my Xamarin.Android project this way?

I want to set the Typeface of the TextView to a font in the Assets folder. The problem-code is "var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");," not the first use, but the second one towards the end of my code (the red squiggly line appears under "Assets").
namespace UndergroundSports.Android
{
[Activity]
public class CityPage : Activity
{
Sport[] sports = Sport.Sports;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
this.SetContentView(Resource.Layout.CityPage);
var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");
Button bttJoin = FindViewById<Button>(Resource.Id.bttJoin);
bttJoin.Click += (sender, e) =>
{
gotoJoinPage();
};
bttJoin.Typeface = font;
ListView lstSports = FindViewById<ListView>(Resource.Id.lstSport);
lstSports.Adapter = new SportsAdapter(this, sports);
lstSports.ItemClick += (object sender, AdapterView.ItemClickEventArgs e) =>
{
Sport selectedFromList = sports[e.Position];
Global.Instance.CurrentSport = selectedFromList;
gotoMembersPage();
};
}
private void gotoJoinPage()
{
var intent = new Intent(this, typeof(JoinPage));
StartActivity(intent);
}
private void gotoMembersPage()
{
var intent = new Intent(this, typeof(MembersPage));
StartActivity(intent);
}
public class SportsAdapter : BaseAdapter<Sport>
{
Sport[] items;
Activity context;
public SportsAdapter(Activity context, Sport[] items) : base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override Sport this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Length; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(global::Android.Resource.Layout.SimpleListItem1, null);
TextView txtView = view.FindViewById<TextView>(global::Android.Resource.Id.Text1);
var font = Typeface.CreateFromAsset(Assets, "Enter-The-Grid.ttf");
txtView.Text = items[position].Name;
txtView.Gravity = GravityFlags.Center;
txtView.Typeface = font;
return view;
}
}
}
}
But when I tried to create a variable containing the font I got an error telling me:
Cannot access a nonstatic member of outer type Android.Content.Context' via nested typeUndergroundSports.Android.CityPage.SportsAdapter' (CS0038) (UndergroundSportsAndroid)"
From looking at related questions I think I need to either create an instance of the Assets object or make it static.
I'm pretty new to C# and don't really understand what's going on. I would appreciate it if someone could explain why I'm unable to access Assets in this part of my code. The part that confuses me the most is that I use the exact same line of code to access the font earlier within the same file without getting that error.
var font = Typeface.CreateFromAsset(context.Assets, "Enter-The-Grid.ttf");
Pass your activity's instance to your adapter via constructor, and use it to access Assests
public class SportsAdapter : BaseAdapter<Sport>
{
Sport[] items;
Activity context;
public SportsAdapter(Activity context, Sport[] items) : base()
{
this.context = context;
this.items = items;
}
....
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(global::Android.Resource.Layout.SimpleListItem1, null);
TextView txtView = view.FindViewById<TextView>(global::Android.Resource.Id.Text1);
var font = Typeface.CreateFromAsset(context.Assets, "Enter-The-Grid.ttf");
txtView.Text = items[position].Name;
txtView.Gravity = GravityFlags.Center;
txtView.Typeface = font;
return view;
}
}
Also, make sure your .ttf file's build action is set to AndroidAssests. Right the .tff file > Build Action > AndroidAsset

Error null exception in "for" construct xamarin android. Where is incorrect?

This method returns the number of types of Views that will be created by getView method.
public class CustomAdapter : BaseAdapter{
private const int TYPE_ITEM = 0;
private const int TYPE_SEPARATOR = 1;
private List<string> mData;
private TreeSet sectionHeader;
private LayoutInflater mInflater;
public CustomAdapter(Context context, List<string> Data) {
mInflater = (LayoutInflater) context
.GetSystemService(Context.LayoutInflaterService);
this.mData=Data;
}
public void addItem(string item) {
mData.Add(item);
NotifyDataSetChanged();
}
public void addSectionHeaderItem(string item) {
mData.Add(item);
//sectionHeader.Add(mData.Count - 1);
NotifyDataSetChanged();
}
public int getItemViewType(int position) {
return sectionHeader.Contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
public int getViewTypeCount {
get{ return 2; }
}
public override int Count {
get {return mData.Count;}
}
public override Java.Lang.Object GetItem(int position) {
return mData[position];
}
public override long GetItemId(int position) {
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int rowType = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.Inflate(Resource.Layout.textViewItemsSeparator, parent);
holder.textView = (TextView) convertView.FindViewById(Resource.Id.textviewHeaderItems);
break;
case TYPE_SEPARATOR:
convertView = mInflater.Inflate(Resource.Layout.textViewHeaderItems, parent);
holder.textView = (TextView) convertView.FindViewById(Resource.Id.textviewItemsSeparator);
break;
}
convertView.Tag=holder;
} else {
holder = (ViewHolder)convertView.Tag as ViewHolder;
}
holder.textView.Text=mData[position];
return convertView;
}
public class ViewHolder:Java.Lang.Object {
public TextView textView;
}
}
ListView lst;
string[] items = new string[] { "Alternative Rock","Classical",...........};
List<string> listItems;
private CustomAdapter mAdapter;
public override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
// Create your fragment here
}
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
listItems = new List<string> (items);
return inflater.Inflate (Resource.Layout.GenerFragment, container, false);
}
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
lst = View.FindViewById<ListView> (Resource.Id.lstGenres);
//lst.Adapter = new ArrayAdapter<string>(Activity, Resource.Layout.textViewHeaderItems,Resource.Id.textviewHeaderItems, items);
//lst = View.FindViewById<ListView> (Resource.Id.lst_genre);
//lst.SetAdapter(new ArrayAdapter<String>(this.Activity, Resource.Layout.GenerFragment, items));
//mAdapter=new CustomAdapter();
for (int i = 0; i < listItems.Count(); i++) {
mAdapter.addItem (listItems[i]);
if (i == 0) {
mAdapter.addSectionHeaderItem ("Music");
} else if(i==13) {
mAdapter.addSectionHeaderItem ("Audio");
}
}
lst.Adapter = new CustomAdapter (Activity, listItems);
I spent much time for looking for errors but I have no idea why It was null. although It got a data from list
mAdapter.addItem (listItems[i]); -> null exception when I debug on device. Where is incorrect?
in OnActivityCreated you are referencing listItems
for (int i = 0; i < listItems.Count(); i++) {
however, listItems is null. You initialize it in OnCreateView, which has not been executed yet. You need to be sure that listItems is initialized before you attempt to reference it.
Additionally, you are attempting to add items to mAdapter, but it's never been initialized (as far as I can see)
you declare it here, but it will be NULL until you initalize it
private CustomAdapter mAdapter;
here is the initialization, which is commented out
//mAdapter=new CustomAdapter();
when you attempt to reference it here, it is still null, and will throw a Null Reference Exception
mAdapter.addItem (listItems[i]);

How to create header xamarin listview?

lst.Adapter=new CustomAdapter(Android.App.Application.Context,items);
public class CustomAdapter : BaseAdapter
{
private const int TYPE_ITEM = 0;
private const int TYPE_SEPARATOR = 1;
string[] mnData;
//private TreeSet sectionHeader;
LayoutInflater mInflater;
TreeSet sectionHeader;
public CustomAdapter(Context context, string[] Data) {
mInflater = LayoutInflater.FromContext (context);
mnData=Data;
}
public override int GetItemViewType (int position)
{
return sectionHeader.Contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
public override int ViewTypeCount
{
get { return 2; }
}
public override int Count {
get {return mnData.Length;}
}
public override Java.Lang.Object GetItem(int position) {
return mnData[position];
}
public override long GetItemId(int position) {
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//var data = mData [position];
var item=mnData[position];
int row = GetItemViewType (position);
if (convertView == null) {
holder = new ViewHolder ();
switch (row) {
case TYPE_ITEM:
convertView = mInflater.Inflate (Resource.Layout.textGenreItems, parent, false);
holder.textView = (TextView)convertView.FindViewById (Resource.Id.textTitleGenre);
break;
case TYPE_SEPARATOR:
convertView = mInflater.Inflate (Resource.Layout.textGenreHeaderItems, parent, false);
holder.textView = (TextView)convertView.FindViewById (Resource.Id.textHeaderGenre);
break;
}
convertView.Tag = holder;
} else {
holder = (ViewHolder)convertView.Tag as ViewHolder;
}
return convertView;
}
}
public class ViewHolder:Java.Lang.Object {
public TextView textView{ get; set; }
//public TextView textViewSeparator{ get; set; }
I want to create a listview as following picture. How to do that?
Here's how I do it, the keys are getItemViewType and getViewTypeCount in the Adapter class. getViewTypeCount returns how many types of items we have in the list, in this case we have a header item and an event item, so two. getItemViewType should return what type of View we have at the input position.
Android will then take care of passing you the right type of View in convertView automatically.

Xamarin, Both ListView and RecyclerView, click one item, another one selected

I'm having a trouble with both ListView and RecyclerView
Initially, I created a ListView, everything is fine. Then I set onClick event for it so that every time I click an item, it changes its color to yellow. The OnClick function I wrote in the MainActivity. Problem is that when I test, not only that item changes its color but 2 items change. I read that it's because I reuse the view.
So I switch my tactics, using RecyclerView instead but same problem occurs. When I click one item to change its color, another below also changes. I guess it's because both ListView and RecyclerView reuse those Item so they confuse when I click one.
I don't know how to solve this problem, I found a solution is to add an array of boolean which marks which item is clicked but it doesn't work. Any idea guys?
So here is the code
MainActivity
class MainActivity : Activity
{
public RecyclerView recyclerView;
public RecyclerView.LayoutManager manager;
public RecyclerView.Adapter adapter;
List<Row> lst;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
init();
recyclerView = (RecyclerView)FindViewById(Resource.Id.recyclerView);
manager = new LinearLayoutManager(this);
recyclerView.SetLayoutManager(manager);
CustomAdapter adapter = new CustomAdapter(lst, this);
adapter.ItemClick += onItemClick;
recyclerView.SetAdapter(adapter);
}
public void init()
{
lst = new List<Row>();
for (int i = 0; i < 15; i++)
{
Row row = new Row() { field1="1:43:00", field2="09-Apr-16", field3="KPI/Overflow", field4="Kevin Bacon", field5="Unowned", field6= "People Counting # IPCAM-ID-C-1-1" };
lst.Add(row);
}
}
public void onItemClick(object sender, int position)
{
int itemPos = position + 1;
//Toast.MakeText(this, "this is " + itemPos, ToastLength.Short).Show();
recyclerView.GetChildAt(position).SetBackgroundColor(Android.Graphics.Color.Green);
}
}
Custom adapter
public class CustomAdapter : RecyclerView.Adapter
{
public Activity _activity;
public List<Row> lst;
public event EventHandler<int> ItemClick;
public CustomAdapter(List<Row> lst, Activity activity)
{
this.lst = lst;
this._activity = activity;
}
public override int ItemCount
{
get
{
return lst.Count;
}
}
public void OnClick(int position)
{
if (ItemClick!=null)
{
ItemClick(this, position);
}
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder myholder = holder as MyViewHolder;
myholder.textView1.Text = lst[position].field1;
myholder.textView2.Text = lst[position].field2;
myholder.textView3.Text = lst[position].field3;
myholder.textView4.Text = lst[position].field4;
myholder.textView5.Text = lst[position].field5;
myholder.textView6.Text = lst[position].field6;
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View v = this._activity.LayoutInflater.Inflate(Resource.Layout.item, parent, false);
TextView tv1 = (TextView)v.FindViewById(Resource.Id.textView1);
TextView tv2 = (TextView)v.FindViewById(Resource.Id.textView2);
TextView tv3 = (TextView)v.FindViewById(Resource.Id.textView3);
TextView tv4 = (TextView)v.FindViewById(Resource.Id.textView4);
TextView tv5 = (TextView)v.FindViewById(Resource.Id.textView5);
TextView tv6 = (TextView)v.FindViewById(Resource.Id.textView6);
MyViewHolder holder = new MyViewHolder(v, OnClick) { textView1 = tv1, textView2 = tv2, textView3 = tv3, textView4 = tv4, textView5 = tv5, textView6 = tv6 };
return holder;
}
}
class MyViewHolder : RecyclerView.ViewHolder
{
public TextView textView1, textView2, textView3, textView4, textView5, textView6;
public View mainView;
public MyViewHolder(View view, Action<int> listener) : base(view)
{
mainView = view;
mainView.Click += (sender, e) => listener(base.Position);
}
}
I followed the example for the OnClick handler on Xamarin site
https://developer.xamarin.com/guides/android/user_interface/recyclerview/
Your issue is with your code. You send the correct position to your event handler, but then you increment it by one in the Activity. Both ends should be using the 0-based index of the item position. There is no need to increment by one.
For changing the background color of the selected item, you can use a selector in XML so you wouldn't even need to do this in code.
Here is an example.
row_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="#android:color/green" />
<item android:state_selected="false" android:color="#android:color/transparent"/>
</selector>
row_content.axml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/row_layout_parent"
android:background="#drawable/row_selector">
<!-- your row content -->
</LinearLayout>
Then your view holder would be updated to this...
class MyViewHolder : RecyclerView.ViewHolder
{
public TextView textView1, textView2, textView3, textView4, textView5, textView6;
public View mainView;
private LinearLayout _layoutParent;
public MyViewHolder(View view, Action<int> listener) : base(view)
{
mainView = view;
_layoutParent = mainView.FindViewById<LinearLayout>(Resource.Id.row_layout_parent);
_layoutParent.Click += (sender, e) => _layoutParent.Selected = true;
}
}
I removed the other click event. If you still need it for other reasons, then you can add it back, but it's not necessary for just setting the item background color when selected.
For Listview you should set choiceMode as below.
listView.ChoiceMode = ChoiceMode.Single;
Hope it help you :)-
Create a reusable recycleview adapter GENERIC
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Support.V7.Widget;
using Android.Text;
using Android.Text.Style;
using Android.Util;
using Android.Views;
using Android.Widget;
using Java.Util.Zip;
using ActionMenuView = Android.Support.V7.Widget.ActionMenuView;
namespace Android.Basic.Core
{
public class GenericRecyclerViewAdapter<T> : RecyclerView.Adapter
{
/// <summary>
/// You can set this for different custom cardview
/// </summary>
private int CardViewResourceLayout { get; set; }
public ObservableCollection<T> Items { get; private set; }
public event EventHandler<RecyclerViewViewHolder> ItemViewTemplated;
public RecyclerView.LayoutManager layoutManager;
public GenericRecyclerViewAdapter(RecyclerView recyclerView, IEnumerable<T> items, int cardViewResourceLayout, bool isList = true, bool isVertical = true) : base()
{
if(isList)
{
var vertical = isVertical ? LinearLayoutManager.Vertical : LinearLayoutManager.Horizontal;
layoutManager = new LinearLayoutManager(recyclerView.Context, vertical, false);
}
else
{
var vertical = isVertical ? GridLayoutManager.Vertical : GridLayoutManager.Horizontal;
layoutManager = new GridLayoutManager(recyclerView.Context, 3, vertical, false);
}
recyclerView.SetLayoutManager(layoutManager);
this.Items = new ObservableCollection<T>(items);
this.CardViewResourceLayout = cardViewResourceLayout;
this.Items.CollectionChanged += delegate
{
this.NotifyDataSetChanged();
};
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var itemView = LayoutInflater.From(parent.Context).Inflate(CardViewResourceLayout, parent, false);
#if DEBUG
Log.Info("GenericRecyclerViewAdapter - ", CardViewResourceLayout.ToString());
#endif
RecyclerViewViewHolder vh = new RecyclerViewViewHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
RecyclerViewViewHolder vh = holder as RecyclerViewViewHolder;
vh.ItemPosition = position;
vh.TemplateView.Tag = position;
vh.TemplateView.Click -= TemplateView_Click;
vh.TemplateView.Click += TemplateView_Click;
ItemViewTemplated?.Invoke(this, vh);
}
public event EventHandler<T> ItemClicked;
private void TemplateView_Click(object sender, EventArgs e)
{
var position = (int)((View)sender).Tag;
this.ItemClicked?.Invoke(sender, this.Items[position]);
}
public override int ItemCount
{
get { return this.Items.Count; }
}
public override long GetItemId(int position)
{
return base.GetItemId(position);
}
}
public class RecyclerViewViewHolder : RecyclerView.ViewHolder, View.IOnCreateContextMenuListener,
IMenuItemOnMenuItemClickListener
{
public View TemplateView { get; private set; }
public int ItemPosition { get; set; }
public event EventHandler<MenuInfo> ContextMenuCreated;
public event EventHandler<object> MenuItemClicked;
public MenuInfo MenuInfo { get; private set; }
public object Data { get; set; }
public RecyclerViewViewHolder(View itemView) : base(itemView)
{
// Locate and cache view references:
this.TemplateView = itemView;
this.TemplateView.SetOnCreateContextMenuListener(this);
}
public void OnCreateContextMenu(IContextMenu menu, View v, IContextMenuContextMenuInfo menuInfo)
{
MenuInfo = new MenuInfo(menu, v, menuInfo);
ContextMenuCreated?.Invoke(this, MenuInfo);
}
private Android.Views.MenuInflater menuInflater = null;
/// <summary>
/// After ContextMenuCreated
/// </summary>
/// <param name="resourcemenu"></param>
public void InflateMenu(int resourcemenu, SpannableString titleColor = null, object dta = null)
{
if (dta != null)
this.Data = dta;
if (this.TemplateView.Context is AppCompatActivity activity)
{
menuInflater = activity.MenuInflater;
}
else if (this.TemplateView.Context is Activity activity2)
{
menuInflater = activity2.MenuInflater;
}
var contextMenu = this.MenuInfo.ContextMenu;
contextMenu.Clear();
menuInflater.Inflate(resourcemenu, contextMenu);
var num = contextMenu.Size() - 1;
for (int i = 0; i <= num; i++)
{
var men = contextMenu.GetItem(i);
if(titleColor != null)
{
if (i == 0)
{
men.SetTitle(titleColor);
men.SetChecked(true);
}
}
if (i != 0)
{
men.SetOnMenuItemClickListener(this);
}
}
}
public bool OnMenuItemClick(IMenuItem item)
{
this.MenuItemClicked?.Invoke(item, this.Data);
return true;
}
public float PosX;
public float PosY;
}
public class MenuInfo
{
public IContextMenu ContextMenu { get; }
public View View { get; }
public IContextMenuContextMenuInfo ContextMenuInfo { get; }
public MenuInfo(IContextMenu contextMenu, View view, IContextMenuContextMenuInfo menuInfo)
{
this.ContextMenu = contextMenu;
this.View = view;
this.ContextMenuInfo = menuInfo;
}
}
}
Usage
RecyclerView recyclerView = new RecyclerView(this);
var viewAdapter = new Android.Basic.Core.GenericRecyclerViewAdapter<Java.IO.File>(recyclerView, files, Resource.Layout.directory_item);
var indiColor = ThemeHelper.IsDark ? ColorHelper.GetRandomLightColor() : ColorHelper.GetRandomDarkColor();
viewAdapter.ItemViewTemplated += (dd, holder) =>
{
var file = files[holder.ItemPosition];
var view = holder.ItemView;
var expanded = view.FindViewById<ExpandedView>(Resource.Id.expandedView);
expanded.SetToggleColor(indiColor);
expanded.SetTitle(file.Name);
GenerateRecycler(expanded, file);
};
recyclerView.SetAdapter(viewAdapter);
expandedView.AddExpandedView(recyclerView);

Resources