クリック効果をXamarin.Formsに追加します

Xamarin.Formsは勢いを増しており、残念ながら、すぐに使用できるオプションはほとんどありません。すべてを依存関係サービスまたはレンダラーを介して追加する必要があります。 この波には、多くの異なるライブラリがあり、多くの場合、基本的な機能が追加されました。

私の決定も例外ではありません。







私の仕事は、iOSとAndroidのほとんどすべての要素をクリックする効果を追加できる小さな拡張機能を作成することでした。







最初は、クリックの効果を持つコンテナを作成し、必要な要素を既に追加することを考えていました。 追加のネストと選択の不正確さを考慮して、このアイデアを放棄しなければなりませんでした。 つまり、このコンテナにCircleImageまたはFrameタイプの非長方形要素を配置すると、丸みのある領域の外側に選択範囲ができます。







すべてのコントロールを書き換えて展開するのは馬鹿げているので、静的拡張を追加することにしました。







どのように見えるべきか



Android 5以降では、明らかに、リップル効果を使用する必要があります。 ただし、iOSおよびAndroid <5の場合、このソリューションは場違いに見えます。 これらのプラットフォームでは、タッチしたときに機能するカラーアニメーション選択を実装することにしました。







実装



PCL



まず、静的なTouchEffectクラスが、エフェクトの色を担当するBindablePropertyを使用してPCLプロジェクトに作成されました。







Android



Androidのバージョンに応じて、リップル効果を使用するかどうかを識別する変数を定義する必要があります。







public bool EnableRipple => Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop;
      
      





Androidの標準ウェーブの実装は非常に簡単です。







 private void AddRipple() { if (Element is Layout) { _rippleOverlay = new FrameLayout(Container.Context) { LayoutParameters = new ViewGroup.LayoutParams(-1, -1) }; _rippleListener = new ContainerOnLayoutChangeListener(_rippleOverlay); _view.AddOnLayoutChangeListener(_rippleListener); ((ViewGroup)_view).AddView(_rippleOverlay); _rippleOverlay.BringToFront(); _rippleOverlay.Foreground = CreateRipple(Color.Accent.ToAndroid()); } else { _orgDrawable = _view.Background; _view.Background = CreateRipple(Color.Accent.ToAndroid()); } _ripple.SetColor(GetPressedColorSelector(_color)); } private void RemoveRipple() { if (Element is Layout) { var viewgrp = (ViewGroup)_view; viewgrp?.RemoveOnLayoutChangeListener(_rippleListener); viewgrp?.RemoveView(_rippleOverlay); _rippleListener?.Dispose(); _rippleListener = null; _rippleOverlay?.Dispose(); _rippleOverlay = null; } else { _view.Background = _orgDrawable; _orgDrawable?.Dispose(); _orgDrawable = null; } _ripple?.Dispose(); _ripple = null; } private RippleDrawable CreateRipple(Android.Graphics.Color color) { if (Element is Layout) { var mask = new ColorDrawable(Android.Graphics.Color.White); return _ripple = new RippleDrawable(GetPressedColorSelector(color), null, mask); } var back = _view.Background; if (back == null) { var mask = new ColorDrawable(Android.Graphics.Color.White); return _ripple = new RippleDrawable(GetPressedColorSelector(color), null, mask); } else if (back is RippleDrawable) { _ripple = (RippleDrawable) back.GetConstantState().NewDrawable(); _ripple.SetColor(GetPressedColorSelector(color)); return _ripple; } else { return _ripple = new RippleDrawable(GetPressedColorSelector(color), back, null); } }
      
      





背景はコントロールから取得され、エフェクトが追加されます。







古いバージョンのAndroidの場合、要素の上にFrameLayoutを追加し、背景チャンネルのアルファアニメーションを追加することにしました。 このメソッドは、要素のTouchイベントにサブスクライブします。







 private void OnTouch(object sender, View.TouchEventArgs args) { switch (args.Event.Action) { case MotionEventActions.Down: Container.RemoveView(_layer); Container.AddView(_layer); _layer.Top = 0; _layer.Left = 0; _layer.Right = _view.Width; _layer.Bottom = _view.Height; _layer.BringToFront(); TapAnimation(250, 0, 65, false); break; case MotionEventActions.Up: case MotionEventActions.Cancel: TapAnimation(250, 65, 0); break; } }
      
      





これを押すと、0から65のAチャンネルアニメーションで新しいレイアウトがコンテナに追加され、リリースされると65から0にアニメーションが戻り、コンテナから削除されます。







次に、 OnAttachedメソッドで、何するか決定し、Rippleエフェクトを作成するか、Touchをサブスクライブします。







 if (EnableRipple) AddRipple(); else _view.Touch += OnTouch;
      
      





iOS



iOSの場合、アプローチは前のステップと同様で、UIViewが押されるとメイン要素の上に追加され、Aチャンネルもアニメーション化されます。 これを行うために、UITapGestureRecognizerとUILongPressGestureRecognizerが作成され、要素に追加されます。







 _tapGesture = new UITapGestureRecognizer(async (obj) => { await TapAnimation(0.3, _alpha, 0); }); _longTapGesture = new UILongPressGestureRecognizer(async (obj) => { switch (obj.State) { case UIGestureRecognizerState.Began: await TapAnimation(0.5, 0, _alpha, false); break; case UIGestureRecognizerState.Ended: case UIGestureRecognizerState.Cancelled: case UIGestureRecognizerState.Failed: await TapAnimation(0.5, _alpha); break; } }); _view.AddGestureRecognizer(_longTapGesture); _view.AddGestureRecognizer(_tapGesture);
      
      





長押しでは、異なるアニメーション時間が設定され、単純な押しとは異なり、指が離された後にのみマスクが削除されます。







実際にはすべて。







使用する



XAML:







 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamEffects.Sample" xmlns:xe="clr-namespace:XamEffects;assembly=XamEffects" x:Class="XamEffects.Sample.MainPage"> <Grid HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="100" WidthRequest="200" BackgroundColor="LightGray" xe:TouchEffect.Color="Red"> <Label Text="Test touch effect" HorizontalOptions="Center" VerticalOptions="Center"/> </Grid> </ContentPage>
      
      





iOS Android API> = 21 Android API <21


まとめ



タッチ効果を実装するという基本的なアイデアをもたらしました。すべてのコードとNugetパッケージがGitHubで利用可能です。







PS:私はネイティブ開発の経験がほとんどないので、何が改善/改善できるかについてアドバイスをうれしく思います。

PPS: Habrastorageはわずかに不器用に変換されたgif。








All Articles