【问题标题】:Xamarin Forms Android Button Material ShadowXamarin Forms Android 按钮材质阴影
【发布时间】:2018-06-23 10:11:00
【问题描述】:

我正在尝试在 android 中实现一个带阴影的按钮。正如您在image 中看到的那样,我有 3 种类型的按钮。

左侧是默认按钮,使用此按钮:<Button FontSize="14" Text="Button" /> 阴影已定义并在按下时正确动画。

中间是这样的:<Button FontSize="14" Text="Button" BackgroundColor="Red" TextColor="White" />改变背景颜色使按钮更高,并去除阴影。

正确的是我使用了自定义渲染器,我使用了自定义ViewOutlineProvider 并将其分配给Control 的View.OutlineProvider 属性。标记是这样的:<ctrl:MaterialButton FontSize="14" Text="Discard Changes" BackgroundColor="#6200EE" HeightRequest="36" TextColor="White" />

这是自定义渲染器类:

[assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
namespace XF.Material.Droid.Renderers
{

    public class MaterialButtonRenderer : Xamarin.Forms.Platform.Android.ButtonRenderer
    {
        private MaterialButton _materialButton;

        public MaterialButtonRenderer(Context context) : base(context) { }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);

            if (e?.NewElement != null)
            {
                _materialButton = this.Element as MaterialButton;
                this.Control.Text = this.Control.Text.ToUpper();
                this.Control.OutlineProvider = new MaterialButtonOutlineProvider(_materialButton.CornerRadius);
            }
        }

        private class MaterialButtonOutlineProvider : ViewOutlineProvider
        {
            private readonly int _cornerRadius;

            public MaterialButtonOutlineProvider(int cornerRadius)
            {
                _cornerRadius = cornerRadius;
            }

            public override void GetOutline(Android.Views.View view, Outline outline)
            {
                var cornerRadius = MaterialExtensions.ConvertDpToPx(_cornerRadius);
                outline.SetRoundRect(0, 0, view.Width, view.Height, cornerRadius);
            }
        }
    }
}

我还尝试在渲染器中设置View.ElevationView.TranslationZ 属性,但仍然没有阴影。如果您仔细观察右侧按钮的角,您可以看到一个轮廓,但它似乎已被剪掉。

【问题讨论】:

    标签: android xamarin xamarin.forms xamarin.android


    【解决方案1】:

    经过一天的思考如何解决这个问题,我终于能够展示阴影了。

    默认情况下,Android 提供了这个默认的可绘制按钮:

    <inset xmlns:android="http://schemas.android.com/apk/res/android"
       android:insetLeft="@dimen/abc_button_inset_horizontal_material"
       android:insetTop="@dimen/abc_button_inset_vertical_material"
       android:insetRight="@dimen/abc_button_inset_horizontal_material"
       android:insetBottom="@dimen/abc_button_inset_vertical_material">
       <shape android:shape="rectangle">
            <corners android:radius="@dimen/abc_control_corner_material" />
            <solid android:color="@android:color/white" />
            <padding android:left="@dimen/abc_button_padding_horizontal_material"
                 android:top="@dimen/abc_button_padding_vertical_material"
                 android:right="@dimen/abc_button_padding_horizontal_material"
                 android:bottom="@dimen/abc_button_padding_vertical_material" />
        </shape>
    </inset>
    

    在 Xamarin Forms 中设置自定义背景(即&lt;Button BackgroundColor="Red" Text="Ok"/&gt;)时,它会创建一个没有填充和插入的可绘制对象,从而隐藏按钮的任何轮廓或阴影。这就是为什么与没有背景颜色的默认按钮相比,定义了背景颜色的按钮看起来更大的原因。您可以设置按钮的HeightRequest 使其变小,但仍然不会出现阴影。

    作为设备 >= API 21 的解决方法,我在 Resources/drawable-v21 文件夹下创建了两个可绘制的 XML 文件,即 drawable_ripple_darkdrawable_ripple_light。这些将分别用于具有深色或浅色背景颜色的按钮:

    <?xml version="1.0" encoding="utf-8" ?>
    <ripple xmlns:android="http://schemas.android.com/apk/res/android"
            android:color="@color/ripple_material_dark"> <!-- or android:color="@color/ripple_material_light" -->
      <item android:id="@+id/inset_drawable">
        <inset android:insetLeft="@dimen/abc_button_inset_horizontal_material"
               android:insetTop="@dimen/abc_button_inset_vertical_material"
               android:insetRight="@dimen/abc_button_inset_horizontal_material"
               android:insetBottom="@dimen/abc_button_inset_vertical_material">
          <shape android:shape="rectangle">
            <corners android:radius="@dimen/abc_control_corner_material" />
            <solid android:color="@android:colorAccent" />
            <padding android:left="@dimen/abc_button_padding_horizontal_material"
                     android:top="@dimen/abc_button_padding_vertical_material"
                     android:right="@dimen/abc_button_padding_horizontal_material"
                     android:bottom="@dimen/abc_button_padding_vertical_material" />
          </shape>
        </inset>
      </item>
    </ripple>
    

    这将在我的自定义渲染器类中用作可绘制的“模板”:

    [assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
    namespace XF.Material.Droid.Renderers
    {
        public class MaterialButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
        {
            private MaterialButton _materialButton;
    
            public MaterialButtonRenderer(Context context) : base(context) { }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
            {
                base.OnElementChanged(e);
    
                if (e?.NewElement != null)
                {
                    _materialButton = this.Element as MaterialButton;
    
                    this.Control.SetAllCaps(true);
    
                    if (Material.IsLollipop)
                    {
                        this.Control.Background = this.CreateRippleDrawable();
                    }
                }
            }
    
            private Drawable CreateRippleDrawable()
            {
                var normalColor = _materialButton.BackgroundColor.ToAndroid();
                var cornerRadius = _materialButton.CornerRadius.ConvertDpToPx();
                var borderWidth = (int)MaterialExtensions.ConvertDpToPx((int)_materialButton.BorderWidth);
                var borderColor = _materialButton.BorderColor.ToAndroid();
                var rippleDrawable = (normalColor.IsColorDark() ? ContextCompat.GetDrawable(this.Context, Resource.Drawable.drawable_ripple_dark) as RippleDrawable : ContextCompat.GetDrawable(this.Context, Resource.Drawable.drawable_ripple_light) as RippleDrawable).GetConstantState().NewDrawable().Mutate() as RippleDrawable; //Copies the drawable
                var insetDrawable = rippleDrawable.FindDrawableByLayerId(Resource.Id.inset_drawable) as InsetDrawable;
    
                var gradientDrawable = insetDrawable.Drawable as GradientDrawable;
                gradientDrawable.SetCornerRadius(cornerRadius);
                gradientDrawable.SetColor(normalColor);
                gradientDrawable.SetStroke(borderWidth, borderColor);
    
                return rippleDrawable;
            }
        }
    }
    

    现在我可以show影子了。触摸时它也会升高。

    【讨论】:

      【解决方案2】:

      您有任何关于如何集成您的解决方案的示例吗?

      我输入了你的代码,它会生成一些细节,因为这部分中不知道变量 MaterialButton

      [assembly: ExportRenderer (typeof (MaterialButton), typeof (MaterialButtonRenderer))]
      

      这个变量也没有

      private MaterialButton _materialButton;
      

      您是否导入了任何金块或除此之外您还做了什么?

      您好。

      【讨论】:

      猜你喜欢
      • 2016-11-23
      • 1970-01-01
      • 2015-05-25
      • 2016-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-20
      相关资源
      最近更新 更多