I have noticed the RecyclerView.Adapter type is not generic in Xamarin.Android. Why is it so? It seems to be defined as generic in native Android according to the documentation. Is there an underlying reason for this? Or is it some kind of backward compatiblity scenario?
You can create generic RecycleView in C# but not in java. In java you need to write code multiple times. That's why java android is worse even just simple event handler, Android Team Engineers are lazy to implements all data event handling because of the nature of java.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
namespace XamarinAndroid.Basic.Core
{
public class GenericRecyclerViewAdapter<T> : RecyclerView.Adapter
{
/// <summary>
/// You can set this for different custom cardview
/// </summary>
public int CardViewResourceLayout { get; set; }
public ObservableCollection<T> Items { get; private set; }
public event EventHandler<RecyclerViewViewHolder> ItemViewTemplated;
public GenericRecyclerViewAdapter(IEnumerable<T> items, int cardViewResourceLayout) : base()
{
this.Items = new ObservableCollection<T>(items);
this.CardViewResourceLayout = cardViewResourceLayout;
this.Items.CollectionChanged += (sender, e) =>
{
try
{
this.NotifyDataSetChanged();
}
catch { }
};
}
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;
this.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(this, 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
{
public View TemplateView { get; private set; }
public int ItemPosition { get; set; }
public RecyclerViewViewHolder(View itemView) : base(itemView)
{
// Locate and cache view references:
this.TemplateView = itemView;
}
}
}
Assigning data to each view
var recyclerViewToday = this.FindViewById<Android.Support.V7.Widget.RecyclerView>(Resource.Id.recyclerViewToday);
var layoutManager = new LinearLayoutManager(this);
recyclerViewToday.SetLayoutManager(layoutManager);
var adapter = new GenericRecyclerViewAdapter<FavoriteModel>(previousList, Resource.Layout.recent_list_item);
adapter.ItemViewTemplated += (d, holder) =>
{
var indicate = holder.ItemView.FindViewById<View>(Resource.Id.indicatorItemmRec);
indicate.SetBackgroundColor(ColorHelper.GetRandomLightColor());
(holder.ItemView.FindViewById<TextView>(Resource.Id.wordTxtRec)).Text = adapter.Items[holder.ItemPosition].Word;
var textView = (holder.ItemView.FindViewById<TextView>(Resource.Id.definitionTxtRec));
if (!string.IsNullOrEmpty(previousList[holder.ItemPosition].Definition))
textView.Text = previousList[holder.ItemPosition].Definition;
else
textView.Visibility = Android.Views.ViewStates.Gone;
};
recyclerViewToday.SetAdapter(adapter);
Result
Clean, Generic and Reusable
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 = context.FindViewById<RecyclerView>(Resource.Id.recycler);
var viewAdapter = new Android.Basic.Core.GenericRecyclerViewAdapter<StorageFile>(recyclerView, files, Resource.Layout.main_item);
viewAdapter.ItemViewTemplated += (dd, holder) =>
{
//CardView
CardView cardView = holder.ItemView.FindViewById<CardView>(Resource.Id.CategorycardView);
cardView.SetCardBackgroundColor(ThemeHelper.IsDark ? Color.ParseColor("#303030") : Color.White);
var indicator = holder.ItemView.FindViewById<LinearLayout>(Resource.Id.indicator);
indicator.SetBackgroundColor(ThemeHelper.IsDark ? ColorHelper.GetRandomLightColor() : ColorHelper.GetRandomDarkColor());
var file = files[holder.ItemPosition];
var itemIcon = holder.ItemView.FindViewById<ImageView>(Resource.Id.itemIcon);
var icon = file.GetFileThumbnail();
if (icon != null)
{
itemIcon.SetImageBitmap(icon);
}
TextView itemName = holder.ItemView.FindViewById<TextView>(Resource.Id.itemName);
itemName.Text = file.DisplayName;
itemName.SetTextColor(textColor);
};
viewAdapter.ItemClicked += (dd, ff) =>
{
var checkBttn = (dd as View).FindViewById<ImageButton>(Resource.Id.check_boxBttn);
};
recyclerView.SetAdapter(viewAdapter);
Related
i have created one custom entry field with floating label using custom renderer like from this uri https://github.com/AlejandroRuiz/FloatingTextEntry
i need to change entry underline color as well placeholder color on button click .
Custom Entry Code :
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Xamarin.Forms;
namespace BloodTrace.CustomControls
{
public class XfxEntry : Entry
{
public static readonly BindableProperty ErrorTextProperty = BindableProperty.Create(nameof(ErrorText),
typeof(string),
typeof(XfxEntry),
default(string), propertyChanged: OnErrorTextChangedInternal);
public static readonly BindableProperty FloatingHintEnabledProperty = BindableProperty.Create(nameof(FloatingHintEnabled),
typeof(bool),
typeof(XfxEntry),
true);
public static readonly BindableProperty ActivePlaceholderColorProperty = BindableProperty.Create(nameof(ActivePlaceholderColor),
typeof(Color),
typeof(XfxEntry),
Color.Accent);
/// <summary>
/// ActivePlaceholderColor summary. This is a bindable property.
/// </summary>
public Color ActivePlaceholderColor
{
get { return (Color)GetValue(ActivePlaceholderColorProperty); }
set { SetValue(ActivePlaceholderColorProperty, value); }
}
public Color ErrorPlaceholderColor
{
get { return (Color)GetValue(ErrorPlaceholderColorProperty); }
set { SetValue(ErrorPlaceholderColorProperty, value); }
}
public static readonly BindableProperty ErrorPlaceholderColorProperty = BindableProperty.Create(nameof(ErrorPlaceholderColor),
typeof(Color),
typeof(XfxEntry),
Color.Green);
/// <summary>
/// <c>true</c> to float the hint into a label, otherwise <c>false</c>. This is a bindable property.
/// </summary>
public bool FloatingHintEnabled
{
get { return (bool)GetValue(FloatingHintEnabledProperty); }
set { SetValue(FloatingHintEnabledProperty, value); }
}
/// <summary>
/// Gets or Sets whether or not the Error Style is 'Underline' or 'None'
/// </summary>
public ErrorDisplay ErrorDisplay { get; set; } = ErrorDisplay.None;
public bool IsErrorPlaceholderColorChange { get; set; } = false;
/// <summary>
/// Error text for the entry. An empty string removes the error. This is a bindable property.
/// </summary>
public string ErrorText
{
get { return (string)GetValue(ErrorTextProperty); }
set { SetValue(ErrorTextProperty, value); }
}
/// <summary>
/// Raised when the value of the error text changes
/// </summary>
public event EventHandler<TextChangedEventArgs> ErrorTextChanged;
private static void OnErrorTextChangedInternal(BindableObject bindable, object oldvalue, object newvalue)
{
var materialEntry = (XfxEntry)bindable;
materialEntry.OnErrorTextChanged(bindable, oldvalue, newvalue);
materialEntry.ErrorTextChanged?.Invoke(materialEntry, new TextChangedEventArgs((string)oldvalue, (string)newvalue));
}
protected virtual void OnErrorTextChanged(BindableObject bindable, object oldvalue, object newvalue) { }
}
public enum ErrorDisplay
{
Underline,
None
}
}
Android Renderer : -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using BloodTrace;
using BloodTrace.Droid.Renderer;
using Application = Android.App.Application;
using Color = Xamarin.Forms.Color;
using AColor = Android.Graphics.Color;
using FormsAppCompat = Xamarin.Forms.Platform.Android.AppCompat;
using BloodTrace.CustomControls;
using Android.Support.Design.Widget;
using Android.Text;
using Xamarin.Forms;
using Android.Content.Res;
using Android.Views.InputMethods;
using Java.Lang;
using Android.Support.V7.Widget;
using Android.Util;
using Xamarin.Forms.Platform.Android;
using System.ComponentModel;
using Android.Text.Method;
using Android.Support.V4.View;
using BloodTrace.Droid.Extensions;
using BloodTraceSharedProject.Extensions;
[assembly: ExportRenderer(typeof(XfxEntry), typeof(XfxEntryRendererDroid))]
namespace BloodTrace.Droid.Renderer
{
public class XfxEntryRendererDroid : FormsAppCompat.ViewRenderer<XfxEntry, TextInputLayout>,
ITextWatcher,
TextView.IOnEditorActionListener
{
private bool _hasFocus;
private ColorStateList _defaultTextColor;
public XfxEntryRendererDroid(Context context) : base(context)
{
AutoPackage = false;
}
protected EditText EditText => Control.EditText;
public bool OnEditorAction(TextView v, ImeAction actionId, KeyEvent e)
{
if ((actionId == ImeAction.Done) || ((actionId == ImeAction.ImeNull) && (e.KeyCode == Keycode.Enter)))
{
Control.ClearFocus();
HideKeyboard();
((IEntryController)Element).SendCompleted();
}
return true;
}
public virtual void AfterTextChanged(IEditable s)
{
}
public virtual void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
}
public virtual void OnTextChanged(ICharSequence s, int start, int before, int count)
{
if (string.IsNullOrWhiteSpace(Element.Text) && (s.Length() == 0)) return;
((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, s.ToString());
}
protected override TextInputLayout CreateNativeControl()
{
var textInputLayout = new TextInputLayout(Context);
var editText = new AppCompatEditText(Context)
{
SupportBackgroundTintList = ColorStateList.ValueOf(GetPlaceholderColor())
};
editText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
textInputLayout.AddView(editText);
return textInputLayout;
}
protected override void OnElementChanged(ElementChangedEventArgs<XfxEntry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
if (Control != null)
EditText.FocusChange -= ControlOnFocusChange;
if (e.NewElement != null)
{
var ctrl = CreateNativeControl();
SetNativeControl(ctrl);
if (!string.IsNullOrWhiteSpace(Element.AutomationId))
EditText.ContentDescription = Element.AutomationId;
_defaultTextColor = EditText.TextColors;
Focusable = true;
EditText.ShowSoftInputOnFocus = true;
// Subscribe
EditText.FocusChange += ControlOnFocusChange;
EditText.AddTextChangedListener(this);
EditText.SetOnEditorActionListener(this);
EditText.ImeOptions = ImeAction.Done;
SetText();
SetHintText();
SetErrorText();
SetFontAttributesSizeAndFamily();
SetInputType();
SetTextColor();
SetHorizontalTextAlignment();
SetFloatingHintEnabled();
SetIsEnabled();
SetErrorDisplay();
SetLabelAndUnderlineColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Entry.PlaceholderProperty.PropertyName)
SetHintText();
else if (e.PropertyName == XfxEntry.ErrorTextProperty.PropertyName)
SetErrorText();
else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName ||
e.PropertyName == InputView.KeyboardProperty.PropertyName)
SetInputType();
else if (e.PropertyName == Entry.TextProperty.PropertyName)
SetText();
else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
SetHorizontalTextAlignment();
else if (e.PropertyName == XfxEntry.FloatingHintEnabledProperty.PropertyName)
SetFloatingHintEnabled();
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
SetIsEnabled();
else if ((e.PropertyName == Entry.FontAttributesProperty.PropertyName) ||
(e.PropertyName == Entry.FontFamilyProperty.PropertyName) ||
(e.PropertyName == Entry.FontSizeProperty.PropertyName))
SetFontAttributesSizeAndFamily();
else if (e.PropertyName == XfxEntry.ActivePlaceholderColorProperty.PropertyName ||
e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
SetLabelAndUnderlineColor();
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
SetTextColor();
}
private void ControlOnFocusChange(object sender, FocusChangeEventArgs args)
{
_hasFocus = args.HasFocus;
if (_hasFocus)
{
var manager = (InputMethodManager)Application.Context.GetSystemService(Context.InputMethodService);
EditText.PostDelayed(() =>
{
EditText.RequestFocus();
manager.ShowSoftInput(EditText, 0);
},
100);
}
var isFocusedPropertyKey = Element.GetInternalField<BindablePropertyKey>("IsFocusedPropertyKey");
((IElementController)Element).SetValueFromRenderer(isFocusedPropertyKey, _hasFocus);
//This will change floating label entry SetUnderlineColor >>>> SetUnderlineColor color basedon focus or non focus
SetUnderlineColor(_hasFocus ? GetActivePlaceholderColor() : GetPlaceholderColor());
}
protected AColor GetPlaceholderColor() => Element.PlaceholderColor.ToAndroid(Color.FromHex("#80000000"));
private AColor GetActivePlaceholderColor() => Element.ActivePlaceholderColor.ToAndroid(global::Android.Resource.Attribute.ColorAccent, Context);
protected virtual void SetLabelAndUnderlineColor()
{
var defaultColor = GetPlaceholderColor();
var activeColor = GetActivePlaceholderColor();
SetHintLabelDefaultColor(defaultColor);
SetHintLabelActiveColor(activeColor);
SetUnderlineColor(_hasFocus ? activeColor : defaultColor);
}
//This will change entry under line color
private void SetUnderlineColor(AColor color)
{
var element = (ITintableBackgroundView)EditText;
element.SupportBackgroundTintList = ColorStateList.ValueOf(color);
}
private void SetHintLabelActiveColor(AColor color)
{
var hintText = Control.Class.GetDeclaredField("mFocusedTextColor");
hintText.Accessible = true;
hintText.Set(Control, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { color }));
}
private void SetHintLabelDefaultColor(AColor color)
{
var hint = Control.Class.GetDeclaredField("mDefaultTextColor");
hint.Accessible = true;
hint.Set(Control, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { color }));
}
private void SetText()
{
if (EditText.Text != Element.Text)
{
EditText.Text = Element.Text;
if (EditText.IsFocused)
EditText.SetSelection(EditText.Text.Length);
}
}
private void SetHintText()
{
Control.Hint = Element.Placeholder;
}
private void SetTextColor()
{
if (Element.TextColor == Color.Default)
{
EditText.SetTextColor(_defaultTextColor);
}
else
{
EditText.SetTextColor(Element.TextColor.ToAndroid());
}
}
private void SetHorizontalTextAlignment()
{
switch (Element.HorizontalTextAlignment)
{
case Xamarin.Forms.TextAlignment.Center:
EditText.Gravity = GravityFlags.CenterHorizontal;
break;
case Xamarin.Forms.TextAlignment.End:
EditText.Gravity = GravityFlags.Right;
break;
default:
EditText.Gravity = GravityFlags.Left;
break;
}
}
public void SetFloatingHintEnabled()
{
Control.HintEnabled = Element.FloatingHintEnabled;
Control.HintAnimationEnabled = Element.FloatingHintEnabled;
SetFontAttributesSizeAndFamily();
}
public void SetErrorDisplay()
{
switch (Element.ErrorDisplay)
{
case ErrorDisplay.None:
Control.ErrorEnabled = false;
break;
case ErrorDisplay.Underline:
Control.ErrorEnabled = true;
break;
}
}
protected void HideKeyboard()
{
var manager = (InputMethodManager)Application.Context.GetSystemService(Context.InputMethodService);
manager.HideSoftInputFromWindow(EditText.WindowToken, 0);
}
//here we can set entry floating label >>> input text size
private void SetFontAttributesSizeAndFamily()
{
EditText.Typeface = Control.Typeface = Element.ToTypeface();
EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
//var a = (float)Element.FontSize; //18
}
private void SetErrorText()
{
Control.Error = !string.IsNullOrEmpty(Element.ErrorText) ? Element.ErrorText : null;
}
private void SetIsEnabled()
{
EditText.Enabled = Element.IsEnabled;
}
private void SetInputType()
{
EditText.InputType = Element.Keyboard.ToInputType();
if (Element.IsPassword && (EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText)
{
EditText.TransformationMethod = new PasswordTransformationMethod();
EditText.InputType = EditText.InputType | InputTypes.TextVariationPassword;
}
if (Element.IsPassword && (EditText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)
{
EditText.TransformationMethod = new PasswordTransformationMethod();
EditText.InputType = EditText.InputType | InputTypes.NumberVariationPassword;
}
}
}
}
XML : -
<xfx:XfxEntry x:Name="EntPassword" Placeholder="Password" IsPassword="True" Focused="EntPassword_Focused"></xfx:XfxEntry>
<Button Text="Sign Up" BackgroundColor="#C62729" TextColor="White" x:Name="BtnSignUp" Clicked="BtnSignUp_Clicked" ></Button>
Please help.
Thanks in advance
How can I provide control (change) of my dynamic images created in xamarin forms by clicking?
Image ggImage = new Image()
ggImage.Source = otoTip.imagex
ggImage.AutomationId = "seat_" +otoTip.num
ggImage.WidthRequest = imageWidth
ggImage.HeightRequest = 50
ggImage.VerticalOptions = LayoutOptions.Start
ggImage.HorizontalOptions = LayoutOptions.Start
ggImage.GestureRecognizers.Add(tapGestureRecognizer)
For example, I just want to change a resume source.
You have to implement a TapGestureRecognizer and then add it to your image...
Image ggImage = new Image();
ggImage.Source = otoTip.imagex
ggImage.AutomationId = "seat_" +otoTip.num
ggImage.WidthRequest = imageWidth
ggImage.HeightRequest = 50;
ggImage.VerticalOptions = LayoutOptions.Start;
ggImage.HorizontalOptions = LayoutOptions.Start;
// your TapGestureRecognizer implementation, this is just a sample...
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += (s, e) => { ggImage.Source = yourNewSource };
ggImage.GestureRecognizers.Add(tapGestureRecognizer);
In order to change every unique image of your dynamically created images you could extend your OtoTip class and
public class OtoTip : INotifyPropertyChanged
{
public int x { get; set; }
public int y { get; set; }
public int num { get; set; }
public int near { get; set; }
public int status { get; set; }
public int yy { get; set; }
public int xx { get; set; }
// xx
private string _imagex;
public string imagex
{
get { return _imagex; }
set
{
_imagex = value;
OnPropertyChanged(nameof(imagex));
}
}
private string _imageActivex;
public string imageActivex
{
get { return _imageActivex; }
set
{
_imageActivex = value;
OnPropertyChanged(nameof(imageActivex));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You have to extend your getVoyagesData() now, to also set the imageActivex like your standard image.
Now you have to adjust the TapGestureRecognizer to use the new property
tapGestureRecognizer.Tapped += (s, e) =>
{
if(ggImage.Source = otoTip.imagex) {
ggImage.Source = otoTip.imageActivex;
}
else
{
ggImage.Source = otoTip.imagex;
}
};
Does this help?
I am working to achieve a recycler view interface that looks like this:
Presently, am only using a recyclerview with LinearLayoutManager using an adapter with two viewholders, i tried gridLayout manager too but i did not achieve the target interface: I need help achieving this, Do i have to create a custom Layout Manager? or What exactly do i have to do? Please, I am really stuck on this.
This is my adapter code
public class SimpleStringRecyclerViewAdapter : RecyclerView.Adapter
{
private List<Data> mValues;
private Context context;
private const int TYPE_FULL = 0;
private const int TYPE_HALF = 1;
private const int TYPE_QUARTER = 2;
public SimpleStringRecyclerViewAdapter(Context context, List<Data> items)
{
this.context = context;
mValues = items;
}
public override int ItemCount
{
get
{
return mValues.Count();
}
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if (holder is SimpleViewHolder)
try
{
Data item = mValues.ElementAt(position);
var simpleHolder = holder as SimpleViewHolder;
simpleHolder.mTxtView.Text = Android.Text.Html.FromHtml(item.article.Title).ToString();
simpleHolder.mTxtView2.Text = item.article.Description;
using (var imageView = simpleHolder.mImageView)
{
string url = Android.Text.Html.FromHtml(item.article.UrlToImage).ToString();
//Download and display image
UrlImageViewHelper.SetUrlDrawable(imageView,
url, Resource.Drawable.cheese_1
);
}
// simpleHolder.mprogressbar.Visibility = ViewStates.Gone;
}
catch (Exception e)
{
//Toast.MakeText(this.context, e.ToString(), ToastLength.Long).Show();
}
else
{
try
{
Data item = mValues.ElementAt(position);
var simpleHolder = holder as SimpleViewHolder2;
simpleHolder.mTxtView.Text = Android.Text.Html.FromHtml(item.youTubeItem.Title).ToString();
// simpleHolder.mTxtView2.Text = item.DescriptionShort;
using (var imageView = simpleHolder.mImageView)
{
string url = Android.Text.Html.FromHtml(item.youTubeItem.MaxResThumbnailUrl).ToString();
//Download and display image
UrlImageViewHelper.SetUrlDrawable(imageView,
url, Resource.Drawable.cheese_1
);
}
}
catch (Exception e)
{
//Toast.MakeText(this.context, e.ToString(), ToastLength.Long).Show();
}
}
}
public override int GetItemViewType(int position)
{
if (mValues.ElementAt(position).type == 1)
{
return Resource.Layout.ItemsList;
}
else
{
return Resource.Layout.VideoList;
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
if (viewType == Resource.Layout.ItemsList)
{
View view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.ItemsList, parent, false);
view.SetBackgroundColor(Color.White);
SimpleViewHolder holder = new SimpleViewHolder(view);
// holder.mprogressbar = view.FindViewById<ProgressBar>(Resource.Id.progressBar);
// holder.mprogressbar.Visibility = ViewStates.Visible;
//Showing loading progressbar
return holder;
}
else
{
View view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.VideoList, parent, false);
view.SetBackgroundColor(Color.White);
SimpleViewHolder2 holder = new SimpleViewHolder2(view);
return holder;
}
}
}
public class SimpleViewHolder : RecyclerView.ViewHolder
{
public string mBoundString;
public readonly View mView;
public readonly ImageView mImageView;
public readonly TextView mTxtView;
public readonly TextView mTxtView2;
// public ProgressBar mprogressbar;
public SimpleViewHolder(View view) : base(view)
{
mView = view;
mImageView = view.FindViewById<ImageView>(Resource.Id.avatar2);
mTxtView = view.FindViewById<TextView>(Resource.Id.Text11);
mTxtView2 = view.FindViewById<TextView>(Resource.Id.Text12);
// mprogressbar = view.FindViewById<ProgressBar>(Resource.Id.progressBar);
}
public override string ToString()
{
return base.ToString() + " '" + mTxtView.Text;
}
}
public class SimpleViewHolder2 : RecyclerView.ViewHolder
{
public string mBoundString;
public readonly View mView;
public readonly ImageView mImageView;
public readonly TextView mTxtView;
public readonly TextView mTxtView2;
public SimpleViewHolder2(View view) : base(view)
{
mView = view;
mImageView = view.FindViewById<ImageView>(Resource.Id.videoavatar);
mTxtView = view.FindViewById<TextView>(Resource.Id.videoText1);
// mprogressbar = view.FindViewById<ProgressBar>(Resource.Id.progressBar);
}
}
SetUpRecyclerView Method:
dataUse = OfflineDeserializer.OfflineData(content, json2);
recyclerView.SetLayoutManager(new LinearLayoutManager(recyclerView.Context));
recyclerView.SetAdapter(new SimpleStringRecyclerViewAdapter(recyclerView.Context, dataUse));
if (vp.IsShown)
{
vp.Visibility = ViewStates.Invisible;
}
This is what I have presently:
you can use recycler view with multiple view type
here is the link you can refer
Recyclerview with multiple view types
Here is a sample code you can modify it according to your need
public class MyFeedsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final int
VIDEO = 1,
IMAGE = 2,
AUDIO = 3;
public MyFeedsAdapter(HomeActivity homeActivity, ArrayList<MyFeedsModel> list, MyFeedsFragment myFeedsFragment) {
this.activity = homeActivity;
this.list = list;
this.myFeedsFragment = myFeedsFragment;
metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case VIDEO:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_myfeeds, parent, false);
return new ViewForVideo(view);
case IMAGE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_myfeeds, parent, false);
return new ViewForImage(view);
case AUDIO:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_myfeeds, parent, false);
return new ViewForAudio(view);
default:
return null;
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
if (list.get(position).getFile_type().equals(IntentString.VIDEO)) {
setViewForVideo((ViewForVideo) holder, position);
} else if (list.get(position).getFile_type().equals(IntentString.IMAGE)) {
setViewForImage((ViewForImage) holder, position);
} else if (list.get(position).getFile_type().equals(IntentString.AUDIO)) {
setViewForAudio((ViewForAudio) holder, position);
}
}
#Override
public int getItemCount() {
return list.size();
}
#Override
public int getItemViewType(int position) {
if (list.get(position).getFile_type().equals(IntentString.VIDEO)) {
return VIDEO;
} else if (list.get(position).getFile_type().equals(IntentString.IMAGE)) {
return IMAGE;
} else if (list.get(position).getFile_type().equals(IntentString.AUDIO)) {
return AUDIO;
} else {
return 0;
}
}
public void setViewForVideo(final ViewForVideo holder, final int position) {
}
public void setViewForImage(final ViewForImage holder, final int position) {
}
public void setViewForAudio(final ViewForAudio holder, int position) {
}
public class ViewForVideo extends RecyclerView.ViewHolder {
public ViewForVideo(View itemView) {
super(itemView);
}
}
public class ViewForImage extends RecyclerView.ViewHolder {
public ViewForImage(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
public class ViewForAudio extends RecyclerView.ViewHolder {
public ViewForAudio(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
I am getting following exception when implementing the code below the exception.
Java.lang.IllegalStateException: The specific child already has a parent. you must call the removeView() on the child's parent first.
using System;
using Android.Support.V7.Widget;
using Android.Views;
using Android.Widget;
using Android.App;
namespace Recycle.Droid
{
internal class albumadapter1 : RecyclerView.Adapter
{
// private int[] imageid;
// private Photo_album palbum;
// private _Recycler _Recycler;
private int[] imageid;
private Activity mainActivity;
public albumadapter1(Activity context, int[] imageid)
{
this.mainActivity = context;
this.imageid = imageid;
}
public override int ItemCount
{
get
{
return imageid.Length;
}
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh1 = holder as PhotoViewHolder;
vh1.img.SetImageResource(imageid[position]);
//vh.Caption.Text = palbum[position].Caption;
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var itemview = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.view_holder, parent);
PhotoViewHolder vh = new PhotoViewHolder(itemview);
return vh;
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public TextView tv { get; private set; }
public ImageView img { get; private set; }
public PhotoViewHolder(View itemView) : base(itemView)
{
img = itemView.FindViewById<ImageView>(Resource.Id.imageView1);
// var Caption = itemView.FindViewById<TextView>(Resource.Id.textView);
// public PhotoViewHolder(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
}
}
}
please help me with problem.
Add False to the code below your issue will be solved
.......
...
...
..
.
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var itemview = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.view_holder, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemview);
return vh;
}
.....
...
..
.
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);