動機
Xamarin.Forms に TextCell という UI があります。大きなフォントで1行の Text ラベルと、小さなフォントで1行の Detail ラベルとからなるセルです。ListView や TableView の要素になります。
Detail に複数行の文字列を表示したいことがありました。試しに Detail に複数行の文字列を入れると、1行目だけが表示されました。これではダメですね。(あとで分かったのですが、1行目だけが表示されるのは iOS の場合で、Android では改行されずに続けて表示されます。)
こういうとき、まずは ViewCell を使うのだと思います。しかし、ViewCell の中に Label を並べただけだと、マージンやフォントサイズなどのスタイルが周囲の TextCell と合いません。合わせるにしても、iOS と Android とではスタイルが微妙に違うこともあって、スタイルを作りこむのは少々面倒です。
そこで、Detail に複数行の文字列を表示できる TextCell をカスタム レンダラーで作ろうと思い立ちました。そちらの方が面倒ではないかという意見もあるやもしれませんが、あるいは、既にどこかに似たようなものがあるかもしれませんが、ご容赦ください。
準備
Xamarin.Forms のソース の TextCellRenderer の実装などを見て作戦を練ります。
- iOS
- TextCellRenderer の DetailTextLabel に該当するプロパティがありそう。
- iOS: UITableView cells with multiple lines? - stackoverflow
- Android
-
BaseCellView に実装の中に
_DetailText.SetSingleLine(true)
とあるので、この値を false に変更すれば良さそう。 - TextView SetSingleLine - Android Developers
-
BaseCellView に実装の中に
Android の方ですが、 _DetailText が private であるため外から触ることができません。ググったところ、GetChildAt()
を使ってアクセスできることが分かりました。次の記事を参考にしました。
- Xamarin.Forms CustomRenderer for a TextCell - stackoverflow
成果物
共通部分は空っぽです。
using Xamarin.Forms;
namespace App1
{
public class MultilineTextCell : TextCell
{
}
}
iOS のカスタムレンダラー:
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(App1.MultilineTextCell), typeof(App1.iOS.MultilineTextCellRenderer))]
namespace App1.iOS
{
public class MultilineTextCellRenderer : TextCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
cell.DetailTextLabel.Lines = 0;
cell.DetailTextLabel.LineBreakMode = UILineBreakMode.WordWrap;
return cell;
}
}
}
Android のカスタムレンダラー:
using Android.Content;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(App1.MultilineTextCell), typeof(App1.Droid.MultilineTextCellRenderer))]
namespace App1.Droid
{
public class MultilineTextCellRenderer : TextCellRenderer
{
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
{
var cell = (LinearLayout)base.GetCellCore(item, convertView, parent, context);
var detailText = (TextView)(cell.GetChildAt(1) as LinearLayout).GetChildAt(1);
detailText.SetSingleLine(false);
return cell;
}
}
}
XAML で利用します。
xmlns:local="clr-namespace:App1"
...
<TextCell Text="通常のTextCell" Detail="{Binding MultilineText}"/>
<local:MultilineTextCell Text="複数行テキスト" Detail="{Binding MultilineText}" />
以上、ツイートしながら コーディングしていました。