在xamarin android中使用SearchView到您的ActionBar for Recyclerview?(SearchView to your ActionBar for Recycle

编程入门 行业动态 更新时间:2024-10-15 06:18:07
在xamarin android中使用SearchView到您的ActionBar for Recyclerview?(SearchView to your ActionBar for Recyclerview in xamarin android?)

如何在Xamarin android中的Recyclerview listitem的ActionBar Search菜单图标中实现SearchView?

How To Implement SearchView to your ActionBar Search menu icon for Recyclerview listitem in xamarin android?

最满意答案

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

设置SearchView

在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,然后你可以查找包含SearchView的MenuItem ,并在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; } } 使用Java类型包装.NET类型

您必须实现自己的Filter ,因为自定义适配器的性质是您提供自定义的东西。 因此,默认的Filter实现无法知道如何过滤它。

正如Cheesebaron所说, FilterResult用于临时存储过滤值,期望存储的对象是Java类型。 因此,您填充Adapte r的模型必须实现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 holder对象在转换为.NET对象时立即被丢弃。 此外,原始源中的比较,值是否为null是不安全的,因为它不考虑值类型。 对象处理对于将GREF参考保持在最低限度非常有用。 我真的不希望应用程序在内存上运行得很高。

在自定义适配器中过滤

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

I wrote up a simple demo about how to implement this feature, effect like this. You can see it in this GitHub Repository.

Setting up the SearchView

In the folder res/menu create a new file called main.xml. In it add an item and set the actionViewClass to android.support.v7.widget.SearchView. Since you are using the support library you have to use the namespace of the support library to set the actionViewClass attribute. Your main.xml file should look something like this:

<?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>

In your Activity you have to inflate this menu xml like usual, then you can look for the MenuItem which contains the SearchView and add a delegate on QueryTextChange which we are going to use to listen for changes to the text entered into the 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; } } To wrap up .NET types with a Java type

You have to implement your own Filter, as the nature of a custom Adapter is that you present custom stuff. Hence the default Filter implementation cannot know how to filter that.

As Cheesebaron said, FilterResult which is used to store the filtered values temporarily, expects that the object stored is a Java type. So either your model which you populate your Adapter with has to implement Java.Lang.Object or you will have to wrap your values. I will show you the latter, as it will apply to the more use cases, as you probably cannot and probably do not want to implement Java.Lang.Object in your contracts or whatever you are using to store data in, especially when you are communicating and sharing code between platforms.

To wrap up .NET types with a Java type I am using modified code from this monodroid mailing list thread, which looks like this:

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

The main difference is that the Java holder object is disposed of immediately when it is converted to a .NET object. Also the comparison in the original source, whether the value was null is unsafe, as it does not take value types into consideration. The object disposal is very useful to keep GREF references down to a minimum. I really don't want the app to run high on memory.

Filtering in custom Adapters

The Adapter looks like this:

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

更多推荐

本文发布于:2023-08-07 01:41:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1459406.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:您的   android   xamarin   SearchView   Recyclerview

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!