Eventos

[Xamarin] Implementando un simple ListViewAdapter

Cuando empecé a trabajar con listas en Android, en casi todos los ejemplos vi que se creaban clases que extendían de BaseAdapter y que estas se creaban para cada Adapter (cuando solo cambiaba la clase y no la plantilla de datos o incluso cuando solo se usaba una sola vez). Así que ante esta particular situación sumado el hecho de los pocos ejemplos de filtrado que encontraba eran con ArrayAdapter y sin usar plantillas personalizadas decidí hacer este post.


Empecemos


Entity.cs
namespace CustomListViewAdapter.Models
{
public class Entity
{
public int Id { get; set; }
}
}
view raw Entity.cs hosted with ❤ by GitHub

User.cs
namespace CustomListViewAdapter.Models
{
public class User : Entity
{
public string ListName
{
get { return string.Format("{0} ({1})", Name, Age); }
}
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
}
}
view raw User.cs hosted with ❤ by GitHub

UserDataTemplate.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/UserNameTextView"
android:textColor="#fff" />
<TextView
android:text="Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/UserAddressTextView"
android:textColor="#aaa" />
</LinearLayout>

ListViewAdapter.cs
using Android.App;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CustomListViewAdapter.Adapters
{
public class ListViewAdapter<T> : BaseAdapter<T>, IFilterable
{
protected List<T> Items;
protected List<T> OriginalItems;
private ListViewFilter<T> _listViewfilter;
private int _resource;
private readonly Func<T, View, ViewGroup, View> _viewFunc;
private readonly Func<T, View, View> _simpleViewFunc;
protected Func<List<T>, List<T>> PerformFiltering;
public ListViewAdapter(List<T> items, Func<T, View, ViewGroup, View> getView, Func<List<T>, List<T>> performFiltering = null)
{
this.Items = items;
this._viewFunc = getView;
this.PerformFiltering = performFiltering;
}
public ListViewAdapter(List<T> items, int resource, Func<T, View, View> simpleViewFunc, Func<List<T>, List<T>> performFiltering = null)
{
this.Items = items;
this._resource = resource;
this._simpleViewFunc = simpleViewFunc;
this.PerformFiltering = performFiltering;
}
#region BaseAdapter
public override T this[int position]
{
get { return Items[position]; }
}
public override int Count
{
get { return Items.Count; }
}
public override long GetItemId(int position)
{
var item = Items[position] as Entity;
return null != item ? item.Id : 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
if (null != _simpleViewFunc)
return _simpleViewFunc(this.Items[position], convertView ?? LayoutInflater.FromContext(Application.Context).Inflate(_resource, parent, false));
else
return _viewFunc(this.Items[position], convertView, parent);
}
#endregion BaseAdapter
#region Methods
public void Add(T item)
{
if (null != OriginalItems)
this.OriginalItems.Add(item);
else
this.Items.Add(item);
this.NotifyDataSetChanged();
}
public void Remove(T item)
{
if (null != OriginalItems)
this.OriginalItems.Remove(item);
else
this.Items.Remove(item);
this.NotifyDataSetChanged();
}
public void AddRange(IEnumerable<T> items)
{
if (null != OriginalItems)
this.OriginalItems.AddRange(items);
else
this.Items.AddRange(items);
this.NotifyDataSetChanged();
}
#endregion Methods
#region IFilterable
public Filter Filter
{
get
{
if (null == this._listViewfilter)
this._listViewfilter = new ListViewFilter<T>(this);
return this._listViewfilter;
}
}
#endregion IFilterable
private class ListViewFilter<T> : Filter
{
private readonly ListViewAdapter<T> _listViewAdapter;
public ListViewFilter(ListViewAdapter<T> listViewAdapter)
{
this._listViewAdapter = listViewAdapter;
}
protected override Filter.FilterResults PerformFiltering(Java.Lang.ICharSequence constraint)
{
FilterResults results = new FilterResults();
if (null == _listViewAdapter.OriginalItems)
_listViewAdapter.OriginalItems = new List<T>(_listViewAdapter.Items);
var filteringResults = _listViewAdapter.PerformFiltering(_listViewAdapter.OriginalItems);
results.Values = new JavaObject<List<T>>(filteringResults);
results.Count = filteringResults.Count;
return results;
}
protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
{
this._listViewAdapter.Items = ((JavaObject<List<T>>)results.Values).Value;
if (results.Count == 0)
_listViewAdapter.NotifyDataSetInvalidated();
else
_listViewAdapter.NotifyDataSetChanged();
}
}
private class JavaObject<T> : Java.Lang.Object
{
public JavaObject(T obj)
{
this.Value = obj;
}
public T Value { get; private set; }
}
}
}

Main.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="25px"
android:minHeight="25px">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/NameSearchEditText" />
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/UsersListView" />
</LinearLayout>
view raw Main.axml hosted with ❤ by GitHub

MainActivity.cs -> GetUsers()
private List<User> GetUsers()
{
var random = new Random();
return new List<User>{
new User { Id = 1, Age = random.Next(9,81), Name= "Shoshana L. Robinson", Address= "Ap #691-1861 Fringilla St."},
new User { Id = 2, Age = random.Next(9,81), Name= "Hadassah T. Lawrence", Address = "270-9592 Dui Street"},
new User { Id = 3, Age = random.Next(9,81), Name= "Caleb R. Rios", Address = "375-1704 Feugiat Street"},
new User { Id = 4, Age = random.Next(9,81), Name= "Keaton K. Booth", Address = "Ap #717-1834 Erat. Rd."},
new User { Id = 5, Age = random.Next(9,81), Name= "Laura C. Maynard", Address = "Ap #893-343 Interdum Road"},
new User { Id = 6, Age = random.Next(9,81), Name= "Cailin A. Mcleod", Address = "Ap #484-5309 Augue Road"},
new User { Id = 7, Age = random.Next(9,81), Name= "Chava P. Wallace", Address = "693-5603 Donec St."},
new User { Id = 8, Age = random.Next(9,81), Name= "Isabella I. Bush", Address = "Ap #100-9976 Donec Road"},
new User { Id = 9, Age = random.Next(9,81), Name= "Leah Y. Acevedo", Address = "9389 Pede. Avenue"},
new User { Id = 10, Age = random.Next(9,81), Name= "McKenzie N. Owen", Address = "453-5024 Et Road"},
new User { Id = 11, Age = random.Next(9,81), Name= "Ivor Q. Woodard", Address = "P.O. Box 818, 8492 Eleifend Avenue"},
new User { Id = 12, Age = random.Next(9,81), Name= "Winifred J. Clark", Address = "P.O. Box 760, 2645 Mauris St."},
new User { Id = 13, Age = random.Next(9,81), Name= "Chaney M. Moss", Address = "970-5780 Sagittis Avenue"},
new User { Id = 14, Age = random.Next(9,81), Name= "Justina R. Barr", Address = "P.O. Box 968, 5665 Non, St."},
new User { Id = 15, Age = random.Next(9,81), Name= "Luke E. Orr", Address = "Ap #373-393 Vulputate Street"},
new User { Id = 16, Age = random.Next(9,81), Name= "Thor G. Baxter", Address = "Ap #930-8980 Lacinia St."},
new User { Id = 17, Age = random.Next(9,81), Name= "Moana T. Rodriquez", Address = "Ap #826-8981 Proin St."},
new User { Id = 18, Age = random.Next(9,81), Name= "Aphrodite V. Dominguez", Address = "371-8128 Vulputate Rd."},
new User { Id = 19, Age = random.Next(9,81), Name= "Sade C. Cameron", Address = "8680 Praesent Street"},
new User { Id = 20, Age = random.Next(9,81), Name= "Chaney T. Cooper", Address = "Ap #758-7491 Class St."},
new User { Id = 21, Age = random.Next(9,81), Name= "Lydia W. Cameron", Address = "152-3416 Mi St."},
new User { Id = 22, Age = random.Next(9,81), Name= "Merritt C. Guzman", Address = "Ap #902-2633 Pulvinar Street"},
new User { Id = 23, Age = random.Next(9,81), Name= "Shaeleigh D. Rowland", Address = "851-9396 Praesent Street"},
new User { Id = 24, Age = random.Next(9,81), Name= "Alika Q. Baird", Address = "P.O. Box 982, 6859 Ligula. Av."},
new User { Id = 25, Age = random.Next(9,81), Name= "Bertha M. Burgess", Address = "Ap #297-6268 Aliquam St."},
new User { Id = 26, Age = random.Next(9,81), Name= "Gareth I. Robbins", Address = "468 Taciti St."},
new User { Id = 27, Age = random.Next(9,81), Name= "Hillary H. Johns", Address = "Ap #768-1660 Integer Road"},
new User { Id = 28, Age = random.Next(9,81), Name= "Quincy T. Whitehead", Address = "1325 Vestibulum Rd."}
};
}

Caso 1. Queremos listar usando una única plantilla de datos y sin filtrar


MainActivity.cs
[Activity(Label = "https://reachsos.com", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var users = GetUsers();
var usersListView = FindViewById<ListView>(Resource.Id.UsersListView);
usersListView.Adapter = new ListViewAdapter<User>(users, Resource.Layout.UserDataTemplate, GetUserView);
}
private View GetUserView(User user, View view)
{
view.FindViewById<TextView>(Resource.Id.UserNameTextView).Text = user.Name;
view.FindViewById<TextView>(Resource.Id.UserAddressTextView).Text = user.Address;
return view;
}
//private List<User> GetUsers()...
}

Caso 2. Queremos listar usando una única plantilla de datos y filtrar


MainActivity.cs
[Activity(Label = "https://reachsos.com", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var nameSearchEditText = FindViewById<EditText>(Resource.Id.NameSearchEditText);
nameSearchEditText.TextChanged += (s, e) => ((IFilterable)FindViewById<ListView>(Resource.Id.UsersListView).Adapter).Filter.InvokeFilter(string.Empty);
var users = GetUsers();
var usersListView = FindViewById<ListView>(Resource.Id.UsersListView);
usersListView.Adapter = new ListViewAdapter<User>(users, Resource.Layout.UserDataTemplate, GetUserView, GetFilteredUsers);
}
private List<User> GetFilteredUsers(List<User> originalItems)
{
var nameSearchEditText = FindViewById<EditText>(Resource.Id.NameSearchEditText);
if (!string.IsNullOrWhiteSpace(nameSearchEditText.Text))
{
return originalItems.Where(x =>
x.Name.StartsWith(nameSearchEditText.Text, StringComparison.InvariantCultureIgnoreCase) &&
x.Age >= 45).ToList();
}
return originalItems;
}
private View GetUserView(User user, View view)
{
view.FindViewById<TextView>(Resource.Id.UserNameTextView).Text = user.ListName;
view.FindViewById<TextView>(Resource.Id.UserAddressTextView).Text = user.Address;
return view;
}
//private List<User> GetUsers()...
}

Caso 3. Queremos listar usando diferentes plantillas de datos c/s filtrar


OldUserDataTemplate.axml 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/UserNameTextView"
android:textColor="#fb3"
android:textSize="18sp" />
<TextView
android:text="Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/UserAddressTextView"
android:textColor="#fff" />
<TextView
android:text="Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/UserBonusTextView"
android:textColor="#aaa" />
</LinearLayout>

MainActivity.cs
[Activity(Label = "https://reachsos.com", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var nameSearchEditText = FindViewById<EditText>(Resource.Id.NameSearchEditText);
nameSearchEditText.TextChanged += (s, e) => ((IFilterable)FindViewById<ListView>(Resource.Id.UsersListView).Adapter).Filter.InvokeFilter(string.Empty);
var users = GetUsers();
var usersListView = FindViewById<ListView>(Resource.Id.UsersListView);
usersListView.Adapter = new ListViewAdapter<User>(users, GetUserView, GetFilteredUsers);
}
private List<User> GetFilteredUsers(List<User> originalItems)
{
var nameSearchEditText = FindViewById<EditText>(Resource.Id.NameSearchEditText);
if (!string.IsNullOrWhiteSpace(nameSearchEditText.Text))
{
return originalItems.Where(x =>
x.Name.StartsWith(nameSearchEditText.Text, StringComparison.InvariantCultureIgnoreCase) &&
x.Age >= 45).ToList();
}
return originalItems;
}
private View GetUserView(User user, View convertView, ViewGroup parent)
{
View view;
if (user.Age >= 65)
{
view = LayoutInflater.Inflate(Resource.Layout.OldUserDataTemplate, parent, false);
view.FindViewById<TextView>(Resource.Id.UserNameTextView).Text = user.ListName;
view.FindViewById<TextView>(Resource.Id.UserAddressTextView).Text = user.Address;
view.FindViewById<TextView>(Resource.Id.UserBonusTextView).Text = ((user.Age - 65) * 1000).ToString();
}
else
{
view = LayoutInflater.Inflate(Resource.Layout.UserDataTemplate, parent, false);
view.FindViewById<TextView>(Resource.Id.UserNameTextView).Text = user.ListName;
view.FindViewById<TextView>(Resource.Id.UserAddressTextView).Text = user.Address;
}
return view;
}
//private List<User> GetUsers()...
}


No hay comentarios.:

Publicar un comentario

Epicalsoft — Superheroic Software Development Blog Designed by Templateism Copyright © 2014

Con tecnología de Blogger.