在xamarin android中搜索查看你的ActionBar for Recyclerview



如何在xamarin android中为Recyclerview列表项的ActionBar搜索菜单图标实现SearchView?

我写了一个关于如何实现此功能的简单演示,效果如下。你可以在这个GitHub存储库中看到它。

  1. 设置搜索视图

在文件夹res/menu中创建一个名为main.xml的新文件。在其中添加一个项目,并将actionViewClass设置为android.support.v7.widget.SearchView。由于您使用的是支持库,因此必须使用支持库的名称空间来设置actionViewClass属性。您的main.xml文件应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_search"
android:title="Search"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

在您的Activity中,您必须像往常一样扩展此菜单xml,然后您可以查找包含SearchViewMenuItem,并在QueryTextChange上添加一个委托,我们将使用该委托来侦听输入到SearchView:中的文本的更改

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
SupportActionBar.SetDisplayShowHomeEnabled(true);
var chemicals = new List<Chemical>
{
new Chemical {Name = "Niacin", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Biotin", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Chromichlorid", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Natriumselenit", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Manganosulfate", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Natriummolybdate", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Ergocalciferol", DrawableId = Resource.Drawable.Icon},
new Chemical {Name = "Cyanocobalamin", DrawableId = Resource.Drawable.Icon},
};
_recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
_adapter = new RecyclerViewAdapter(this,chemicals);
_LayoutManager = new LinearLayoutManager(this);
_recyclerView.SetLayoutManager(_LayoutManager);
_recyclerView.SetAdapter(_adapter);//
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.main, menu);
var item = menu.FindItem(Resource.Id.action_search);
var searchView = MenuItemCompat.GetActionView(item);
_searchView = searchView.JavaCast<Android.Support.V7.Widget.SearchView>();
_searchView.QueryTextChange += (s, e) => _adapter.Filter.InvokeFilter(e.NewText);
_searchView.QueryTextSubmit += (s, e) =>
{
// Handle enter/search button on keyboard here
Toast.MakeText(this, "Searched for: " + e.Query, ToastLength.Short).Show();
e.Handled = true;
};
MenuItemCompat.SetOnActionExpandListener(item, new SearchViewExpandListener(_adapter));
return true;
}
private class SearchViewExpandListener : Java.Lang.Object, MenuItemCompat.IOnActionExpandListener
{
private readonly IFilterable _adapter;
public SearchViewExpandListener(IFilterable adapter)
{
_adapter = adapter;
}
public bool OnMenuItemActionCollapse(IMenuItem item)
{
_adapter.Filter.InvokeFilter("");
return true;
}
public bool OnMenuItemActionExpand(IMenuItem item)
{
return true;
}
}
  1. 结束。带有Java类型的NET类型

您必须实现自己的Filter,因为自定义适配器的本质是呈现自定义内容。因此,默认的Filter实现无法知道如何过滤它。

正如Cheesebaron所说,用于临时存储过滤后的值的FilterResult期望存储的对象是Java类型。因此,要么用Adapter填充的模型必须实现Java.Lang.Object,要么必须包装您的值。我将向您展示后者,因为它将适用于更多的用例,因为您可能无法也可能不想在您的合同或用于存储数据的任何东西中实现Java.Lang.Object,尤其是当您在平台之间通信和共享代码时。

为了用Java类型封装.NET类型,我使用了这个monodroid邮件列表线程中修改过的代码,它看起来像这样:

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;
}
}

主要区别在于,Java持有者对象在转换为.NET对象时立即被处理。此外,在原始源中比较值是否为null是不安全的,因为它没有考虑值类型。对象处理对于将GREF引用降至最低非常有用。我真的不希望这个应用程序内存不足。

  1. 自定义适配器中的筛选

Adapter看起来像这样:

public class RecyclerViewAdapter : RecyclerView.Adapter, IFilterable
{
private List<Chemical> _originalData;
private List<Chemical> _items;
private readonly Activity _context;
public Filter Filter { get; private set; }
public RecyclerViewAdapter(Activity activity, IEnumerable<Chemical> chemicals)
{
_items = chemicals.OrderBy(s => s.Name).ToList();
_context = activity;
Filter = new ChemicalFilter(this);
}
public override long GetItemId(int position)
{
return position;
}

public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.Chemical, parent, false);
ChemicalHolder vh = new ChemicalHolder(itemView);
return vh;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
ChemicalHolder vh = holder as ChemicalHolder;
var chemical = _items[position];
vh.Image.SetImageResource(chemical.DrawableId);
vh.Caption.Text = chemical.Name;
}
public override int ItemCount
{
get { return _items.Count; }
}
public class ChemicalHolder : RecyclerView.ViewHolder
{
public ImageView Image { get; private set; }
public TextView Caption { get; private set; }
public ChemicalHolder(View itemView) : base(itemView)
{
Image = itemView.FindViewById<ImageView>(Resource.Id.chemImage);
Caption = itemView.FindViewById<TextView>(Resource.Id.chemName);
}
}
private class ChemicalFilter : Filter
{
private readonly RecyclerViewAdapter _adapter;
public ChemicalFilter(RecyclerViewAdapter adapter)
{
_adapter = adapter;
}
protected override FilterResults PerformFiltering(ICharSequence constraint)
{
var returnObj = new FilterResults();
var results = new List<Chemical>();
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(
chemical => chemical.Name.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<Chemical>()).ToList();
_adapter.NotifyDataSetChanged();
// Don't do this and see GREF counts rising
constraint.Dispose();
results.Dispose();
}
}
}

最新更新