Multi Select Spinner Xamarin Android - xamarin.android

I am using a spinner and an arrayAdapter to populate items in spinner. I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
My code is as below,
adapterList= new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemMultipleChoice);
spnMultiTest.Adapter = adapterList;

I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
Basically I just translate the codes from Android Spinner with multiple choice
to Xamarin codes. I have tested it and it works fine:
MultiSpinner.cs:
public interface MultiSpinnerListener
{
void onItemsSelected(bool[] selected);
}
public class MultiSpinner : Spinner, IDialogInterfaceOnMultiChoiceClickListener, IDialogInterfaceOnCancelListener
{
Context _context;
private List<String> items;
private bool[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) : base(context)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1) : base(context, arg1)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1, int arg2) : base(context, arg1, arg2)
{
_context = context;
}
public void OnClick(IDialogInterface dialog, int which, bool isChecked)
{
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
public override void OnClick(IDialogInterface dialog, int which)
{
dialog.Cancel();
}
public override bool PerformClick()
{
AlertDialog.Builder builder = new AlertDialog.Builder(_context);
builder.SetMultiChoiceItems(
items.ToArray(), selected, this);
builder.SetPositiveButton("OK",this);
builder.SetOnCancelListener(this);
builder.Show();
return true;
}
public void SetItems(List<String> items, String allText,
MultiSpinnerListener listener)
{
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new bool[items.Count];
for (int i = 0; i < selected.Length; i++)
selected[i] = true;
ArrayAdapter<string> adapter = new ArrayAdapter<string>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { allText });
// all text on the spinner
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item, new String[] { allText });
Adapter = adapter;
}
public void OnCancel(IDialogInterface dialog)
{
Java.Lang.StringBuffer spinnerBuffer = new Java.Lang.StringBuffer();
bool someUnselected = false;
for (int i = 0; i < items.Count; i++)
{
if (selected[i] == true)
{
spinnerBuffer.Append(items[i]);
spinnerBuffer.Append(", ");
}
else
{
someUnselected = true;
}
}
String spinnerText;
if (someUnselected)
{
spinnerText = spinnerBuffer.ToString();
if (spinnerText.Length > 2)
spinnerText = spinnerText.Substring(0, spinnerText.Length - 2);
}
else
{
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { spinnerText });
Adapter = adapter;
if (listener != null)
{
listener.onItemsSelected(selected);
}
}
}
simple_spinner_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Update:
Here is the codes for using this MultiSpinner:
Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<MultiSpinner.MultiSpinner
android:id="#+id/mSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity.cs:
public class MainActivity : Activity
{
MultiSpinner mSpinner;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
mSpinner = FindViewById<MultiSpinner>(Resource.Id.mSpinner);
List<string> items = new List<string> {
"Android",
"iOS",
"UWP"
};
mSpinner.SetItems(items, "AllText", null);
}
}

Related

How to remove item from array in image adapter. Xamarin gallery

public class ImageAdapter : BaseAdapter
{
Context context;
public ImageAdapter (Context conn)
{
context = conn;
}
public override int Count { get { return thumbIds.Length; } }
public override Java.Lang.Object GetItem (int position)
{
return null;
}
public override long GetItemId (int position)
{
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public override View GetView (int position, View convertView, ViewGroup parent)
{
ImageView pic = new ImageView (context);
pic.SetImageResource (thumbIds[position]);
pic.LayoutParameters = new Gallery.LayoutParams (500, 500);
pic.SetScaleType (ImageView.ScaleType.FitXy);
return pic;
}
// references to our images
int[] thumbIds = {
Resource.Drawable.image_1,
Resource.Drawable.image_2,
Resource.Drawable.image_3,
Resource.Drawable.image_4,
Resource.Drawable.image_5,
Resource.Drawable.image_6,
Resource.Drawable.image_7
};
}
How to remove item from array
https://learn.microsoft.com/en-us/xamarin/android/user-interface/controls/gallery
Gallery
Do you want to achieve the result like following GIF?
I advice your to use List<int> to replace of int[], then you can add a method called removeItem in the ImageAdapter like following code.
public void removeItem(int numToRemove)
{
if (numToRemove < thumbIds.Count)
{
thumbIds.RemoveAt(numToRemove);
this.NotifyDataSetChanged();
}
}
Here is code about ImageAdapter.cs
using Android.Views;
using Android.Widget;
using Java.Lang;
using System.Collections.Generic;
using System.Linq;
namespace App25
{
internal class ImageAdapter : BaseAdapter
{
private MainActivity mainActivity;
public ImageAdapter(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public override int Count { get { return thumbIds.Count; } }
public override Java.Lang.Object GetItem(int position)
{
return thumbIds[position];
}
public override long GetItemId(int position)
{
return position;
}
// create a new ImageView for each item referenced by the Adapter
public override View GetView(int position, View convertView, ViewGroup parent)
{
ImageView pic = new ImageView(mainActivity);
pic.SetImageResource(thumbIds[position]);
pic.LayoutParameters = new Gallery.LayoutParams(500, 500);
pic.SetScaleType(ImageView.ScaleType.FitXy);
return pic;
}
public void removeItem(int numToRemove)
{
if (numToRemove < thumbIds.Count)
{
thumbIds.RemoveAt(numToRemove);
this.NotifyDataSetChanged();
}
}
// references to our images
List<int> thumbIds = new List<int> {
Resource.Drawable.faded_div,
Resource.Drawable.icon,
Resource.Drawable.faded_div1,
Resource.Drawable.icon1,
Resource.Drawable.faded_div,
Resource.Drawable.icon2,
Resource.Drawable.faded_div
};
}
}
Here is MainActivity.cs
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
ImageAdapter imageAdapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
Gallery gallery = (Gallery)FindViewById<Gallery>(Resource.Id.gallery1);
Button button1 = FindViewById<Button>(Resource.Id.button1);
imageAdapter = new ImageAdapter(this);
gallery.Adapter = imageAdapter;
button1.Click += Button1_Click;
}
int removeItem = 0;
private void Button1_Click(object sender, System.EventArgs e)
{
imageAdapter.removeItem(removeItem);
}
}
Here is my activity_main.xml.
<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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove"
android:id="#+id/button1"
/>
<Gallery
android:id="#+id/gallery1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
=============Update==================
If you want to achieve the long click ,push alert, then delete the item, you can refer to achieve a longclick event for your gallery like following code.
gallery.ItemLongClick += Gallery_ItemLongClick;
private void Gallery_ItemLongClick(object sender, AdapterView.ItemLongClickEventArgs e)
{
// throw new System.NotImplementedException();
Android.App.AlertDialog.Builder dialog = new Android.App.AlertDialog.Builder(this);
Android.App.AlertDialog alert = dialog.Create();
alert.SetTitle("Title");
alert.SetMessage("Do you want to remove this select item");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
imageAdapter.removeItem(e.Position);
});
alert.Show();
}
Here is my running GIF.

Adding Searchview in Listview

I made an array to transfer the required data consisting of 3 elements(Heading,SubHeading, int ImageResourceId).
The data shows up well in listview but I couldn't do the research inside listview.
I want to do the research using searchview on specific SubHeading or both (Heading,SubHeading)
//on creat
listView.Adapter = new HomeScreenAdapter(this, HomeScreenAdapter.tableItems);
search.QueryTextChange += SearchV_QueryTextChange;
//filter listview
private void SearchV_QueryTextChange(object sender, SearchView.QueryTextChangeEventArgs e)
{
}
//
// 3-class listview item id
public class TableItem
{
public string Heading;
public string SubHeading;
public int ImageResourceId;
public TableItem(string Heading, string SubHeading, int ImageResourceId)
{
this.Heading = Heading;
this.SubHeading = SubHeading;
this.ImageResourceId = ImageResourceId;
}
}
}
//
//class adapter & array item
class HomeScreenAdapter : BaseAdapter<TableItem>
{
public static List<TableItem> tableItems = new List<TableItem>();
List<TableItem> items;
Activity context;
public void filldata()
{
tableItems.Add(new TableItem("hussein", "devepoer", Resource.Drawable.imaga));
tableItems.Add(new TableItem("ahmed", "admin", Resource.Drawable.imagb));
tableItems.Add(new TableItem("jasim", " manager", Resource.Drawable.imagc));
tableItems.Add(new TableItem("ahmed", "admin", Resource.Drawable.imagd));
tableItems.Add(new TableItem("jasim", " manager", Resource.Drawable.image));
tableItems.Add(new TableItem("jasim", " manager", Resource.Drawable.imagf));
}
}
According to your code, I do one sample by SubHeading to filter ListView data.
Firstly, create TableItem class:
public class TableItem
{
public string Heading { get; set; }
public string SubHeading { get; set; }
public int ImageResourceId { get; set; }
}
Then creating UI for ListView item:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ImageView android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
And create Adapter and filter for data:
public class TableItemAdapter : BaseAdapter<TableItem>, IFilterable
{
private List<TableItem> _originalData;
private List<TableItem> _items;
private readonly Activity _context;
public TableItemAdapter(Activity activity, IEnumerable<TableItem> tableitems)
{
_items = tableitems.OrderBy(s => s.SubHeading).ToList();
_context = activity;
Filter = new TableItemFilter(this);
}
public override TableItem this[int position]
{
get { return _items[position]; }
}
public Filter Filter { get; private set; }
public override int Count
{
get { return _items.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = convertView ?? _context.LayoutInflater.Inflate(Resource.Layout.TableItem, null);
var tableitem = _items[position];
var HeadingView = view.FindViewById<TextView>(Resource.Id.textView1);
var SubHeadingView = view.FindViewById<TextView>(Resource.Id.textView2);
var imageView = view.FindViewById<ImageView>(Resource.Id.imageView1);
HeadingView.Text = tableitem.Heading;
SubHeadingView.Text = tableitem.SubHeading;
imageView.SetImageResource(tableitem.ImageResourceId);
return view;
}
public override void NotifyDataSetChanged()
{
// If you are using cool stuff like sections
// remember to update the indices here!
base.NotifyDataSetChanged();
}
private class TableItemFilter : Filter
{
private readonly TableItemAdapter _adapter;
public TableItemFilter(TableItemAdapter adapter)
{
_adapter = adapter;
}
protected override FilterResults PerformFiltering(ICharSequence constraint)
{
var returnObj = new FilterResults();
var results = new List<TableItem>();
if (_adapter._originalData == null)
_adapter._originalData = _adapter._items;
if (constraint == null) return returnObj;
if (_adapter._originalData != null && _adapter._originalData.Any())
{
// Compare constraint to all names lowercased.
// It they are contained they are added to results.
results.AddRange(
_adapter._originalData.Where(
item => item.SubHeading.ToLower().Contains(constraint.ToString())));
}
// Nasty piece of .NET to Java wrapping, be careful with this!
returnObj.Values = FromArray(results.Select(r => r.ToJavaObject()).ToArray());
returnObj.Count = results.Count;
constraint.Dispose();
return returnObj;
}
protected override void PublishResults(ICharSequence constraint, FilterResults results)
{
using (var values = results.Values)
_adapter._items = values.ToArray<Java.Lang.Object>().Select(r =>r.ToNetObject<TableItem>()).ToList();
_adapter.NotifyDataSetChanged();
// Don't do this and see GREF counts rising
constraint.Dispose();
results.Dispose();
}
}
}
ObjectExtensions to implement filter:
public class JavaHolder : Java.Lang.Object
{
public readonly object Instance;
public JavaHolder(object instance)
{
Instance = instance;
}
}
public static class ObjectExtensions
{
public static TObject ToNetObject<TObject>(this Java.Lang.Object value)
{
if (value == null)
return default(TObject);
if (!(value is JavaHolder))
throw new InvalidOperationException("Unable to convert to .NET object. Only Java.Lang.Object created with .ToJavaObject() can be converted.");
TObject returnVal;
try { returnVal = (TObject)((JavaHolder)value).Instance; }
finally { value.Dispose(); }
return returnVal;
}
public static Java.Lang.Object ToJavaObject<TObject>(this TObject value)
{
if (Equals(value, default(TObject)) && !typeof(TObject).IsValueType)
return null;
var holder = new JavaHolder(value);
return holder;
}
}
Mainactivity.cs:
public class MainActivity : AppCompatActivity
{
private SearchView searchview1;
private ListView listview1;
private TableItemAdapter tableitemadapter;
private List<TableItem> tableitems;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
searchview1 = FindViewById<SearchView>(Resource.Id.searchView1);
listview1 = FindViewById<ListView>(Resource.Id.listView1);
tableitems = new List<TableItem>()
{
new TableItem(){Heading= "hussein", SubHeading="devepoer",ImageResourceId= Resource.Drawable.plu3 },
new TableItem(){Heading= "ahmed", SubHeading="admin",ImageResourceId= Resource.Drawable.plu3 },
new TableItem(){Heading= "jasim", SubHeading="manager",ImageResourceId= Resource.Drawable.plu3 },
new TableItem(){Heading= "cherry", SubHeading="admin",ImageResourceId= Resource.Drawable.plu3 },
new TableItem(){Heading= "barry", SubHeading="manager",ImageResourceId= Resource.Drawable.plu3 },
new TableItem(){Heading= "annine", SubHeading="manager",ImageResourceId= Resource.Drawable.plu3 }
};
tableitemadapter = new TableItemAdapter(this,tableitems);
listview1.Adapter = tableitemadapter;
searchview1.QueryTextChange += Searchview1_QueryTextChange;
}
private void Searchview1_QueryTextChange(object sender, SearchView.QueryTextChangeEventArgs e)
{
tableitemadapter.Filter.InvokeFilter(e.NewText);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
The sample in github:
https://github.com/CherryBu/Searchviewsample
The screenshot:

How to make expandable listview children scrollable?

I've an expandable listview. I want to put different data in child views and make them scrollable. Currently my expandable listview is scrolling rather than child scroll. How can I restrict that and meet the requirement?
gridcustom layout:
?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/item_Image"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="#color/accent"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="13sp"
android:text="Image"
android:gravity="center"
android:layout_below="#+id/item_Image"
android:layout_alignParentBottom="true" />
</LinearLayout>
my grid:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_scroll_view"
android:layout_width="match_parent"
android:layout_height="250dp">
<GridView
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnWidth="120dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:gravity="center"/>
</LinearLayout>
Expandable listview adapter:
using System;
using Android.Widget;
using System.Collections.Generic;
using Android.App;
using Android.Views;
namespace Sasken
{
public class ExpandableListAdapter :BaseExpandableListAdapter
{
Activity _context;
List<GridDataClass> _lstGrdData;
ImageAdapter imageAdaptor;
LayoutInflater layoutInflater;
public ExpandableListAdapter (Activity context, List<GridDataClass> lstGridData)
{
this._context = context;
this.layoutInflater = layoutInflater;
this._lstGrdData = lstGridData;
}
public override Java.Lang.Object GetChild (int groupPosition, int childPosition)
{
return true;
}
public override long GetChildId (int groupPosition, int childPosition)
{
return childPosition;
}
public override View GetChildView (int groupPosition, int childPosition, bool isLastChild, View convertView, ViewGroup parent)
{
// string childText = (string) GetChild(groupPosition, childPosition);
// if (convertView == null) {
// convertView = _context.LayoutInflater.Inflate (Resource.Layout.ListItemCustomLayout, null);
// }
// TextView txtListChild = (TextView) convertView.FindViewById(Resource.Id.lblListItem);
// txtListChild.Text=childText;
View v = null;
if (groupPosition == 0) {
v = View.Inflate (_context, Resource.Layout.expandable_Child_Layout, null);
GridView grid = (GridView)v.FindViewById (Resource.Id.gridview);
grid.SetAdapter (new ImageAdapter (this._context, _lstGrdData));
}
if (groupPosition == 1) {
v = View.Inflate (_context, Resource.Layout.expandable_child_1_layout, null);
TextView txtView = (TextView)v.FindViewById (Resource.Id.lblListItem);
txtView.Text = "Test";
txtView.TextSize = 15f;
}
if (groupPosition == 2) {
v = View.Inflate (_context, Resource.Layout.expandable_child_2_layout, null);
TextView txtView = (TextView)v.FindViewById (Resource.Id.lblListItem);
txtView.Text = "Test";
txtView.TextSize = 15f;
}
v.Invalidate ();
return v;
}
public override int GetChildrenCount (int groupPosition)
{
return 1;
}
public override Java.Lang.Object GetGroup (int groupPosition)
{
return null;
}
public override int GroupCount {
get {
return 3;
}
}
public override long GetGroupId (int groupPosition)
{
return 0;
}
public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent)
{
View v = _context.LayoutInflater.Inflate (Resource.Layout.HeaderCustomLayout, null); //infalInflater.inflate(R.layout.list_group, null);
TextView lblListHeader = (TextView)v.FindViewById (Resource.Id.lblListHeader);
if (groupPosition == 0) {
lblListHeader.Text = "QUICK ITEMS";
lblListHeader.SetTypeface (null, Android.Graphics.TypefaceStyle.Bold);
}
if (groupPosition == 1) {
lblListHeader.Text = "PRODUCT LOOKUP";
lblListHeader.SetTypeface (null, Android.Graphics.TypefaceStyle.Bold);
}
if (groupPosition == 2) {
lblListHeader.Text = "NON MARCHANDISE";
lblListHeader.SetTypeface (null, Android.Graphics.TypefaceStyle.Bold);
}
if (groupPosition == 3) {
lblListHeader.Text = "QUICK ITEMS";
lblListHeader.SetTypeface (null, Android.Graphics.TypefaceStyle.Bold);
}
v.Invalidate ();
return v;
}
public override bool HasStableIds {
get {
return false;
}
}
public override bool IsChildSelectable (int groupPosition, int childPosition)
{
return true;
}
class ViewHolderItem :Java.Lang.Object
{
}
}
}
Image custom adaptor::
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;
namespace Sasken
{
[Activity (Label = "ImageAdaptor")]
public class ImageAdapter : BaseAdapter
{
Activity _context;
List<GridDataClass> _lstGridData;
ImageView imageView;
TextView textView;
public ImageAdapter (Activity c, List<GridDataClass> lstGridData)
{
_context = c;
_lstGridData = lstGridData;
}
public override int Count {
get { return _lstGridData.Count; }
}
public override Java.Lang.Object GetItem (int position)
{
return null;
}
public override long GetItemId (int position)
{
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public override View GetView (int position, View convertView, ViewGroup parent)
{
if (convertView == null) { // if it's not recycled, initialize some attributes
convertView = _context.LayoutInflater.Inflate (Resource.Layout.GridCustomLayout, parent, false);
imageView = (ImageView)convertView.FindViewById (Resource.Id.item_Image);
textView = (TextView)convertView.FindViewById (Resource.Id.item_name);
// convertView.LayoutParameters = new GridView.LayoutParams (150,150);
// //convertView.SetScale (ImageView.ScaleType.CenterCrop);
// convertView.SetPadding (2, 2, 2, 2);
}
imageView.SetImageResource (_lstGridData [position].intImgSource);
textView.Text = (_lstGridData [position].strImgName);
return convertView;
}
}
public class GridDataClass
{
public GridDataClass (int item_value = 1, string item_name = "Temporary Id")
{
strImgName = item_name;
intImgSource = item_value;
}
public string strImgName{ get; set; }
public int intImgSource{ get; set; }
}
}

populate images with firebase recycler adapter through databinding

I am populating a firebase data with firebase recycler adapter through databinding and want to populate images with the data either from Picasso or Glide, but unable to do. So can any one help me what to write and where to write the code of either Picasso or Glide.
public class ShopByCategoryFragment extends Fragment {
FirebaseRecyclerAdapter adapter;
Firebase mFirebaseRef = new Firebase("https://abc.firebaseio.com/").child("subCategories");
public ShopByCategoryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.recycler_view, container, false);
final RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
adapter = new FirebaseRecyclerAdapter<SubCategories, ViewHolder>(SubCategories.class, R.layout.fragment_shop_by_category,
ViewHolder.class, mFirebaseRef) {
#Override
protected void populateViewHolder(ViewHolder viewHolder, SubCategories subCategories, int i) {
FragmentShopByCategoryBinding binding = viewHolder.getBinding();
binding.setSubCategories(subCategories);
}
};
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
return rootView;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public FragmentShopByCategoryBinding binding;
public ViewHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
public FragmentShopByCategoryBinding getBinding() {
return binding;
}
}
#Override
public void onDestroy() {
super.onDestroy();
adapter.cleanup();
}
}
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="SubCategories"
type="com.abc.www.shopping.model.SubCategories"/>
</data>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:id="#+id/sub_category_image"
android:scaleType="centerCrop"
android:src="#{SubCategories.image}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:text="#{SubCategories.title}"
android:id="#+id/sub_category_title"
android:padding="16dp"
android:textColor="#color/colorSubCategoryTitle"
android:layout_gravity="center"
android:textAppearance="#android:style/TextAppearance.Material.Title"
tools:targetApi="lollipop" />
</FrameLayout>
</android.support.v7.widget.CardView>
public class SubCategories extends BaseObservable {
private String title;
private String image;
public SubCategories() {
// empty constructor
}
public SubCategories(String title, String image) {
this.title = title;
this.image = image;
}
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
}
I have made something similar to what you need. I'll share my code and do a little explanation.
I have a class that returns the FirebaseRecyclerAdapter like this:
public RecyclerView.Adapter postAdapter(String userKey, final Context context, MyClickListener clickListener) {
Query userPostRef = postRef.child(userKey);
myClickListener = clickListener;
return new FirebaseRecyclerAdapter<PostModel, DataObjectHolder>(PostModel.class, R.layout.stream_layout, DataObjectHolder.class, userPostRef) {
#Override
public void populateViewHolder(final DataObjectHolder dataviewHolder, final PostModel postModel, int position) {
StorageReference mStorageRef = FirebaseStorage.getInstance().getReference();
mStorageRef.child("uploaded_captures/" + postModel.getImageFilename() + ".jpg").getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
dataviewHolder.setImage(uri.toString(), context);
dataviewHolder.setComment(postModel.getPostComment());
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
Log.e("FirebaseRecyclerAdapter", exception.getMessage());
}
});
}
};
}
and a ViewHolder that loads the image into the ImageView used by the layout:
private static class DataObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View mView;
public DataObjectHolder(View itemView) {
super(itemView);
mView = itemView;
}
void setImage(final String imageURL, final Context context) {
final ImageView postImage = (ImageView) mView.findViewById(R.id.imageView);
if (BuildConfig.DEBUG) {
Picasso.with(context).setIndicatorsEnabled(true);
}
Picasso.with(context)
.load(imageURL)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(context)
.load(imageURL)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage);
}
});
}
void setComment(String text) {
TextView comment = (TextView) mView.findViewById(R.id.comment);
comment.setText(text);
}
#Override
public void onClick(View v) {
myClickListener.onItemClick(getAdapterPosition(), v);
}
}
On Picasso I'm using the NerworkPolicy.OFFLINE to check if the image is on the disk cache before trying to get it from the network. It's not perfect and I'm still having some issues with the delay of the images once the ViewHolder it's been recycled but I think it's a start for you to go on.
In my ViewHolder I do this by:
Glide.with(context)
.load(chat.getImageUrl())
.into(imageView);
I haven't tried using data binding for this yet, but the code is simple enough.

Create a custom Spinner

I am trying to customize the MvxSpinner to add some additional controls, here is my code:
public class ChamSpinner : LinearLayout
{
public Spinner Spinner{ get; private set; }
public EventHandler<AdapterView.ItemSelectedEventArgs> ItemSelected;
public ChamSpinner (Context context, IAttributeSet attrs) : this (context, attrs, new ChamSpinnerAdapter (context))
{
}
public ChamSpinner (Context context, IAttributeSet attrs, IMvxAdapter adapter) : base (context, attrs)
{
((Activity)Context).LayoutInflater.Inflate (Resource.Layout.ChamSpinnerLayout, this);
Spinner = FindViewById<Spinner> (Resource.Id.ChamSpinnerSpinner);
int itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId (context, attrs);
int dropDownItemTemplateId = MvxAttributeHelpers.ReadDropDownListItemTemplateId (context, attrs);
adapter.ItemTemplateId = itemTemplateId;
adapter.DropDownItemTemplateId = dropDownItemTemplateId;
Adapter = adapter;
SetupHandleItemSelected ();
}
public new IMvxAdapter Adapter
{
get { return Spinner.Adapter as IMvxAdapter; }
set
{
var existing = Adapter;
if (existing == value)
return;
if (existing != null && value != null)
{
value.ItemsSource = existing.ItemsSource;
value.ItemTemplateId = existing.ItemTemplateId;
}
Spinner.Adapter = value;
}
}
[MvxSetToNullAfterBinding]
public IEnumerable ItemsSource
{
get
{
return Adapter.ItemsSource;
}
set
{
Adapter.ItemsSource = value;
}
}
public int ItemTemplateId
{
get { return Adapter.ItemTemplateId; }
set { Adapter.ItemTemplateId = value; }
}
public int DropDownItemTemplateId
{
get { return Adapter.DropDownItemTemplateId; }
set { Adapter.DropDownItemTemplateId = value; }
}
public ICommand HandleItemSelected { get; set; }
private void SetupHandleItemSelected ()
{
Spinner.ItemSelected += (sender, args) =>
{
var position = args.Position;
HandleSelected (position);
if (ItemSelected != null)
ItemSelected (sender, args);
};
}
protected virtual void HandleSelected (int position)
{
var item = Adapter.GetRawItem (position);
if (this.HandleItemSelected == null
|| item == null
|| !this.HandleItemSelected.CanExecute (item))
return;
this.HandleItemSelected.Execute (item);
}
}
And I am using it like this:
<cross.android.ChamSpinner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxDropDownItemTemplate="#layout/myspinneritemdropdown"
local:MvxItemTemplate="#layout/myspinneritem"
local:MvxBind="ItemsSource MyItemsSource; SelectedItem MyItem; Mode TwoWay" />
The spinner is always empty, I tried to add a custom binding on ItemsSource property but the result stilll the same.
How can I do to show my items in my spinner?
Thank you in advance.
I think using BindingInflate instead of Inflate should fix it or even points you in the right direction. https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Droid/BindingContext/IMvxAndroidBindingContext.cs
((IMvxBindingContextOwner)Context).BindingInflate(Resource.Layout.ChamSpinnerLayout, this);
I found this error in my log
MvxBind:Error: 32,12 View type not found - cross.android.ChamSpinner
My custom control is in another assembly so I added it to MvvmCross View assemblies is my Setup class like this
protected override IList<Assembly> AndroidViewAssemblies
{
get
{
var assemblies = base.AndroidViewAssemblies;
assemblies.Add(typeof(ChamSpinner).Assembly);
return assemblies;
}
}
Thank you Stuart for your advices and for your great Framework.

Resources