SearchView 到您的 ActionBar for Recyclerview 在 xamarin android 中? [英] SearchView to your ActionBar for Recyclerview in xamarin android?

查看:29
本文介绍了SearchView 到您的 ActionBar for Recyclerview 在 xamarin android 中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

推荐答案

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

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

  1. 设置 SearchView

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

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>

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

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

  1. 使用 Java 类型包装 .NET 类型

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

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.

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

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.

为了用 Java 类型包装 .NET 类型,我使用了来自 monodroid 邮件列表线程,如下所示:

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

主要区别在于 Java 持有者对象在转换为 .NET 对象时会立即被处理掉.同样在原始源中的比较,值是否为空是不安全的,因为它没有考虑值类型.对象处理对于将 GREF 引用保持在最低限度非常有用.我真的不希望应用程序占用大量内存.

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.

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

这篇关于SearchView 到您的 ActionBar for Recyclerview 在 xamarin android 中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆