質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

1回答

2957閲覧

XamarinでPickerのDone/OKボタンが押下された際に特定の処理を呼び出す方法

yiwata

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

0グッド

0クリップ

投稿2021/12/06 02:30

前提・実現したいこと

Xamarinを使用してC#でiOS/Android向けのアプリを開発しております。実現したいのは、XAMLのPickerで入力完了を意味するDone/OKボタンが押された際に、特定の処理を走らせることです。(Pickerの入力完了のボタンはiOSではDoneボタン、AndroidではOKボタンです。)

iOS側で実現することはできたため、今回の質問はAndroid側での実現方法についてです。

iOS側の実装

iOS側の実装については、PickerRendererクラスを継承して実現することができました。XAMLにて以下のBasePickerタグを記述することで、Doneボタン押下にて★印のついたコメント行の処理を呼び出すことができています。

BasePicker.cs

C#

1using Xamarin.Forms; 2 3namespace AppName.Controls 4{ 5 public class BasePicker : Picker 6 { 7 public BasePicker() : base() 8 { 9 } 10 11 string _someProperty = null; 12 public string SomeProperty 13 { 14 get { return _someProperty ; } 15 set { _someProperty = value; } 16 } 17 } 18}

MyPickerRenderer.cs(AppName.iOS側に存在)

C#

1using System; 2using Xamarin.Forms; 3using Xamarin.Forms.Platform.iOS; 4using UIKit; 5using AppName.Controls; 6using AppName.iOS.Renderer; 7 8[assembly: ExportRenderer(typeof(BasePicker), typeof(MyPickerRenderer))] 9namespace AppName.iOS.Renderer 10{ 11 public class MyPickerRenderer : PickerRenderer 12 { 13 protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) 14 { 15 base.OnElementChanged(e); 16 17 if (e.OldElement != null) 18 { 19 if (Control != null) 20 { 21 var toolbar = (UIToolbar)Control.InputAccessoryView; 22 var doneBtn = toolbar.Items[1]; 23 24 doneBtn.Clicked -= DoneBtn_Clicked; 25 } 26 } 27 28 if (e.NewElement != null) 29 { 30 var toolbar = (UIToolbar)Control.InputAccessoryView; 31 var doneBtn = toolbar.Items[1]; 32 33 doneBtn.Clicked += DoneBtn_Clicked; 34 } 35 } 36 37 void DoneBtn_Clicked(object sender, EventArgs e) 38 { 39 // ★ここに何か実行したい処理を書く★ 40 } 41 } 42}

Android側の実装(試したこと)

今回質問したいのは、Android側の実装をどうすべきかというという点です。最初に、AppName.iOS側に存在するMyPickerRenderer.csをAppName.Android側にコピーし、usingやnamespaceの「iOS」の部分を「Android」等に書き換えました。

次に、元々のiOS向けのコードにはUIToolbarを取得したのち、(UIBarButtonItem)doneBtnを取得している部分がありますので、この部分をAndroid向けに書き換える必要があると考えています。しかしこのdoneBtnの取得方法について、さまざまなキーワードで検索を試みましたが、情報を見つけることができませんでした。ここで調査が行き詰っています。

Visual Studioの入力補完機能を使用すると、OnElementChanged内で得られるControlにはSetOn○○○Listenerというメソッドが複数存在しているようで、これを利用すればOKボタンを押した際に呼び出されるメソッドを指定できるのかもしれないとも推測しています。しかしこれについても、検索を試みても詳しい使用方法がヒットせず、単なる推測のままです。

Android側でPickerのOKボタンが押された際に特定の処理を呼び出すにはどうすればいいのでしょうか。どうぞよろしくお願いいたします。

その他の試したこと

XAMLのPickerで指定できるSelectedIndexChangedについては、選択項目を変更せずにDone/OKした場合に動作してくれないため、今回の使用目的には合いませんでした。同様にPropertyChanged、Unfocusedについても今回の使用目的には合いませんでした。

補足情報(FW/ツールのバージョンなど)

C#、Xamarin.Forms等のバージョンは最新のものを使用しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

AndroidのPickerは、内部でAlertDialogを作成して表示している形なので、簡単にカスタマイズすることができません。仕方ないので、クリック時に独自のダイアログを表示するようにします。
SetPositiveButtonがOKボタンを押された処理なのでそこに処理を追加することになります。
また、フォーカス時に表示されるピッカーでも動作するように、OnFocusChangeRequestedにも修正を加えています。(OnClickOnFocusChangeRequestedはほぼ元コードのコピペです。)

c#

1public class MyPickerRenderer : PickerRenderer 2{ 3 AlertDialog _dialog; 4 5 public MyPickerRenderer(Context context) : base(context) 6 { 7 } 8 9 IElementController ElementController => Element as IElementController; 10 11 protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) 12 { 13 base.OnElementChanged(e); 14 15 if (e.OldElement != null) 16 { 17 if (Control != null) 18 { 19 Control.Click -= Control_Click; 20 Control.FocusChange -= Control_FocusChange; 21 } 22 } 23 24 if (e.NewElement != null) 25 { 26 Control.Click += Control_Click; 27 Control.FocusChange += Control_FocusChange; 28 } 29 } 30 31 protected override void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e) 32 { 33 if (Control == null) 34 return; 35 36 e.Result = true; 37 38 if (e.Focus) 39 { 40 // Android does the actual focus/unfocus work on the main looper 41 // So in case we're setting the focus in response to another control's un-focusing, 42 // we need to post the handling of it to the main looper so that it happens _after_ all the other focus 43 // work is done; otherwise, a call to ClearFocus on another control will kill the focus we set here 44 Device.BeginInvokeOnMainThread(() => 45 { 46 if (Control == null || Control.Handle == IntPtr.Zero) 47 return; 48 if (Control is IPopupTrigger popupElement) 49 popupElement.ShowPopupOnFocus = true; 50 51 Control?.RequestFocus(); 52 }); 53 } 54 else 55 { 56 Control.ClearFocus(); 57 } 58 } 59 60 protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Android.Graphics.Rect previouslyFocusedRect) 61 { 62 base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect); 63 } 64 65 protected override void Dispose(bool disposing) 66 { 67 if (disposing) 68 { 69 if (Control != null) 70 { 71 Control.Click -= Control_Click; 72 Control.FocusChange -= Control_FocusChange; 73 } 74 } 75 76 base.Dispose(disposing); 77 } 78 79 private void Control_Click(object sender, EventArgs e) 80 { 81 OnClick(); 82 } 83 84 private void OnClick() 85 { 86 Picker model = Element; 87 88 if (_dialog != null) 89 return; 90 91 var picker = new NumberPicker(Context); 92 if (model.Items != null && model.Items.Any()) 93 { 94 picker.MaxValue = model.Items.Count - 1; 95 picker.MinValue = 0; 96 picker.SetDisplayedValues(model.Items.ToArray()); 97 picker.WrapSelectorWheel = false; 98 picker.DescendantFocusability = Android.Views.DescendantFocusability.BlockDescendants; 99 picker.Value = model.SelectedIndex; 100 } 101 102 var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical }; 103 layout.AddView(picker); 104 105 ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true); 106 107 var builder = new AlertDialog.Builder(Context); 108 builder.SetView(layout); 109 110 if (!Element.IsSet(Picker.TitleColorProperty)) 111 { 112 builder.SetTitle(model.Title ?? ""); 113 } 114 else 115 { 116 var title = new SpannableString(model.Title ?? ""); 117 title.SetSpan(new ForegroundColorSpan(model.TitleColor.ToAndroid()), 0, title.Length(), SpanTypes.ExclusiveExclusive); 118 119 builder.SetTitle(title); 120 } 121 122 builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) => 123 { 124 ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); 125 _dialog = null; 126 }); 127 builder.SetPositiveButton(global::Android.Resource.String.Ok, (s, a) => 128 { 129 ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value); 130 // It is possible for the Content of the Page to be changed on SelectedIndexChanged. 131 // In this case, the Element & Control will no longer exist. 132 if (Element != null) 133 { 134 if (model.Items.Count > 0 && Element.SelectedIndex >= 0) 135 Control.Text = model.Items[Element.SelectedIndex]; 136 ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); 137 } 138 _dialog = null; 139 140 // ★ここに何か実行したい処理を書く★ 141 }); 142 143 _dialog = builder.Create(); 144 _dialog.DismissEvent += (sender, args) => 145 { 146 ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); 147 _dialog?.Dispose(); 148 _dialog = null; 149 }; 150 _dialog.Show(); 151 } 152 153 private void Control_FocusChange(object sender, FocusChangeEventArgs e) 154 { 155 if (e.HasFocus) 156 { 157 if (Clickable) 158 CallOnClick(); 159 else 160 OnClick(); 161 } 162 else if (_dialog != null) 163 { 164 _dialog.Hide(); 165 ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); 166 _dialog = null; 167 } 168 } 169}

投稿2021/12/07 16:22

編集2021/12/09 15:55
f-miyu

総合スコア1625

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yiwata

2021/12/09 06:18

ソースコードで詳しくご回答いただきありがとうございます。ご回答いただいたソースで、意図した動作ができることが確認できました。 少し追加の質問をさせてください。XAMLでEntryとPickerを順に並べて、Entryに「ReturnType="Next"」を指定した場合、Entryの入力完了後にソフトキーボードの「→」キー押下でPickerにフォーカスを移すことができると思います。そのような動作でPickerにフォーカスを移した場合、今回実装していただいたダイアログは出てこない動作となっていました。Focus周りの処理も実装していただいたようなのでお訊きしたいのですが、上記したような「直前のEntryからソフトキーボードでPickerにフォーカスが移ったケース」でもダイアログが出てくれるように、ご回答いただいたソースを改変することは可能でしょうか? もしそれほど手がかからないようであれば、改変後のソースをご教示いただけると大変助かります。逆にそう簡単には改変できないという見立てであれば、その旨コメントいただけますでしょうか。その場合は実現方法を別途詳しく調査したいと思いますので、いったんこの質問はベストアンサー指定の上クローズさせていただこうと思います。
f-miyu

2021/12/09 15:59

そもそも元々のPickerでも、Focusメソッド呼び出しではダイアログは表示されますが、キー押下では、フォーカスが移動してもダイアログは表示されないようですね。 一応、フォーカス移動でもダイアログが表示されるように修正しましたので、確認してみてください。 (OnFocusChangeRequestedで行っていたダイアログ表示処理をFocusChangeイベント発火時に行うようにしています。)
yiwata

2021/12/14 03:06

ソースコードのご修正ありがとうございます。「直前のEntryからソフトキーボードでPickerにフォーカスが移ったケース」でもダイアログが表示されることが確認できました。 ソースコードレベルで具体的な実装をご回答いただき感謝しております。Rendererの中身についてはおそらく自力では実装できなかったと思いますので、大変助かりました。どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問