0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Xamarin Android GridViewのカスタムレンダラーの作成

Last updated at Posted at 2020-05-12

前書き

XamarinもAndroidも初めてで、両方の知識が必要なCustomRendererを作成するのに手こずったので書き残しました。
冗長な書き方や間違いがある可能性が高いです。

※CustomRendererはPCL側のXamlで実装できない、できたけど速度が遅い。みたいなときに各プラットフォームごとの固有の書き方をすることで改善することができる機能。(今回はAndroidなのでC#、xmlで実装していきます)

完成形

Screenshot_20200513-005743.png

実装

※私のnamespaceやプロジェクト名は自分のものに置き換えてください

PCL側

PCLとXamarin.Androidを紐づけるためのクラスを用意します。

PCLCustomGridViewRenderer.cs

using System.Runtime.CompilerServices;
using Xamarin.Forms;

[assembly: InternalsVisibleTo("MyProject.Android")]
namespace MyProject.Views.Renderer
{
    public class PCLCustomGridViewRenderer : ItemsView
    {
    }
}


ItemsViewは配列を受け取るための抽象クラス。継承してやることでXamlからPCLCustomGridViewRendererに対して配列を渡すことができる。(配列はそのままAndroid側の実装で扱えます)

XamlからPCLCustomGridViewRendererを呼び出します。

<StackLayout xmlns:renderer="clr-namespace:MyProject.Views.Renderer">
    <renderer:PCLCustomGridViewRenderer ItemsSource="{Binding Items}"/>
</StackLayout>

※PCLCustomGridViewRendererが見つからないエラーが出る場合はプロジェクトをリビルドしてみてください。

ちなみにItems内のデータ構成はObservableCollectionになっています。

ObservableCollection<Item> Items;

class Item
{
    public string Text { get; set; }
}

PCL側の実装は以上です。

Xamarin.Android側

3つほどクラスファイルを用意します。
1.Android側で配列を受け取りGridViewを生成するクラス。
2.Androidの世界で出てくるAdpterクラス。データとxmlを紐づけるためのクラス。
3.GridView内に配置する要素のxmlファイル

1.AndroidCustomGridViewRenderer.cs
エラー線が出る箇所がありますが、進めていく際に治るので、一旦無視してください。

using Android.Content;
using Android.Support.V7.View;
using Android.Widget;
using MyProject.Droid.Renderer;
using MyProject.Droid.Views.Adapter;
using MyProject.Views.Renderer;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

//互いに参照できるよう定義
[assembly: ExportRenderer(typeof(PCLCustomGridViewRenderer), typeof(AndroidCustomGridViewRenderer))]
namespace MyProject.Droid.Renderer
{
    public class AndroidCustomGridViewRenderer : ViewRenderer<PCLCustomGridViewRenderer, GridView>
    {
        private GridViewAdapter adapter;
        public AndroidCustomGridViewRenderer(Context context) : base(context) { }

        // 生成時一度だけ呼ばれるイベント
        protected override void OnElementChanged(ElementChangedEventArgs<PCLCustomGridViewRenderer> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                if (adapter != null)
                    adapter.Element = null;
            }

            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    // Adpterの生成
                    adapter = new GridViewAdapter(Context);
           
                    // GridViewの生成
                    var gridView = new CustomGridView(new ContextThemeWrapper(Context, Resource.Style.VerticalScrollbarRecyclerView));
           // GridViewのパラメータを設定(親サイズに追従する設定)
                    gridView.LayoutParameters = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
                    // カラム数
                    gridView.NumColumns = 3; 
           // Viewに対しAdpterを設定
                    gridView.Adapter = adapter;

                    //多分コントロールを実際に生成
                    SetNativeControl(gridView);
                }

                if (adapter != null)
                    // NewElementにはIList<Person>が入ってきます
                    // ElementはAdpterクラスが私が定義しました。
                    adapter.Element = e.NewElement;
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == ItemsView.ItemsSourceProperty.PropertyName)
            {
                adapter?.UpdateItems();
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            adapter.Dispose();
        }
    }
}

2.AndroidCustomGridViewAdapter.cs

using System.Collections.ObjectModel;
using System.Linq;
using Android.Content;
using Android.Views;
using Android.Widget;
using MyProject.Models;
using MyProject.Views.Renderer;

namespace MyProject.Droid.Views.Adapter
{
    class AndroidCustomGridViewAdapter : BaseAdapter
    {
        private PCLCustomGridViewRenderer element;
        public PCLCustomGridViewRenderer Element
        {
            get => element;
            set
            {
                element = value;
                UpdateItems();
            }
        }

        Context context;

        private ObservableCollection<Item> viewModels = new ObservableCollection<Item>();

        public AndroidCustomGridViewAdapter(Context c)
        {
            context = c;
        }

        public override int Count
        {
            get { return viewModels.Count(); }
        }

        public override Java.Lang.Object GetItem(int position)
        {
            return null;
        }

        public override long GetItemId(int position)
        {
            return 0;
        }
        public void UpdateItems()
        {
            viewModels.Clear();

            if (Element?.ItemsSource != null)
            {
                foreach (var item in Element.ItemsSource)
                {
                    if (item is Item model)
                    {
                        viewModels.Add(model);
                    }
                }
            }

            NotifyDataSetChanged();
        }

        // Countプロパティの数だけ呼ばれるViewを生成する関数
        public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
        {
            var view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.custom_gridview_item, parent, false);
            // 要素の作成
            SetUp(view, viewModels[position]);

            return view;
        }

        private void SetUp(Android.Views.View view, Item model)
        {
            var text = view.FindViewById<TextView>(Resource.Id.text);

            text.Text = model.Text;
        }
    }
}

3.custom_gridview_item.xml
Resources/layoutフォルダに配置して下さい。

<?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">
    <TextView
        android:id="@+id/text"
        android:text="ああああああああああああああああ"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"    
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

まだエラー線が出ている箇所があると思うので定義を追加します。
Resources/values/styles.xmlのresourcesタグの中に以下を追加します

<style name="VerticalScrollbarRecyclerView" parent="android:Widget">
  <item name="android:scrollbars">vertical</item>
</style>

完成

ビルドしてみてください。

もしプロジェクトの実行の際に以下のエラーが出た場合

System.InvalidOperationException: 'The class, property, or method you are attempting to use ('VerifyCollectionViewFlagEnabled') is part of CollectionView; to use it, you must opt-in by calling Forms.SetFlags("CollectionView_Experimental") before calling Forms.Init()

MyProject.AndroidプロジェクトのMainActivity.csに1行追加してください

global::Xamarin.Forms.Forms.SetFlags("CollectionView_Experimental"); ←下の行より前にこの行を追加
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);      

あとがき

今回はただテキストを表示するだけでしたが、Itemクラスを拡張したり、xmlを変えたりすることで自由な表現ができると思います。

Xamarin Android 各要素の高さが定まっていないGridViewのCustomRenderer作成

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?