Xamarin表单android按钮自定义渲染器

时间:2018-02-13 10:21:34

标签: c# xamarin.forms xamarin.android

有没有人有Xamarin Forms android按钮自定义渲染器的完整示例?或者甚至可能吗?

我基本上想要制作圆角按钮。

我尝试从这个例子开始: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/entry/

使用http://taoffi.isosoft.org/post/2016/03/26/Xamarin-forms-so-you-lost-your-rounded-buttons

但是我在编译时遇到了很多错误:

using System;
using System.Collections.Generic;
using System.Linq;
//using UXDivers.Artina.Shared;
using Xamarin.Forms;
using Android.Graphics.Drawables;
using System.ComponentModel;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using Android.Views;
using System.Runtime.Remoting.Contexts;
using CustomRenderer.Android;

[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonCompatRenderer))]

namespace CustomRenderer.Android
{
    public class CustomButtonCompatRenderer : ButtonRenderer
    {
        public CustomButtonCompatRenderer(Context context) : base(context)
        {
            SetWillNotDraw(false);
        }


        private GradientDrawable _normal,
                                        _pressed;


        // resolves: button text alignment lost after click or IsEnabled change
        //public override void ChildDrawableStateChanged(Android.Views.View child)
        //{
        //  base.ChildDrawableStateChanged(child);
        //  Control.Text = Control.Text; 
        //}

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

            if (Control != null)
            {
                SetAlignment();

                var density = Math.Max(1, Resources.DisplayMetrics.Density);
                var button = e.NewElement;
                var mode = MeasureSpec.GetMode((int)button.BorderRadius);
                var borderRadius = button.BorderRadius * density;
                var borderWidth = button.BorderWidth * density;

                // Create a drawable for the button's normal state
                _normal = new Android.Graphics.Drawables.GradientDrawable();

                if (button.BackgroundColor.R == -1.0 && button.BackgroundColor.G == -1.0 && button.BackgroundColor.B == -1.0)
                    _normal.SetColor(Android.Graphics.Color.ParseColor("#ff2c2e2f"));
                else
                    _normal.SetColor(button.BackgroundColor.ToAndroid());

                _normal.SetStroke((int)borderWidth, button.BorderColor.ToAndroid());
                _normal.SetCornerRadius(borderRadius);

                // Create a drawable for the button's pressed state
                _pressed = new Android.Graphics.Drawables.GradientDrawable();
                var highlight = Context.ObtainStyledAttributes(new int[]
                                    {
                                        Android.Resource.Attribute.ColorAccent  //  .ColorActivatedHighlight
                                    }).GetColor(0, Android.Graphics.Color.Gray);

                _pressed.SetColor(highlight);
                _pressed.SetStroke((int)borderWidth, button.BorderColor.ToAndroid());
                _pressed.SetCornerRadius(borderRadius);

                // Add the drawables to a state list and assign the state list to the button
                var sld = new StateListDrawable();
                sld.AddState(new int[] { Android.Resource.Attribute.StatePressed }, _pressed);
                sld.AddState(new int[] { }, _normal);
                Control.SetBackground(sld);     //.SetBackgroundDrawable(sld); // deprecated
            }
        }

        private void SetAlignment()
        {
            var element = this.Element as Button;

            if (element == null || this.Control == null)
            {
                return;
            }

            this.Control.Gravity = GravityFlags.CenterHorizontal | GravityFlags.CenterVertical;
            //element.VerticalAlignment.ToDroidVerticalGravity() |  
            //element.HorizontalAlignment.ToDroidHorizontalGravity();  
        }

        void DrawCustom(Button targetButton)
        {
            if (Control == null || targetButton == null)
                return;

        }
    }
}

错误:

CustomButtonCompatRenderer.cs(20,62,20,66): error CS1729: 'ButtonRenderer' does not contain a constructor that takes 1 arguments
CustomButtonCompatRenderer.cs(52,39,52,47): error CS0234: The type or namespace name 'Graphics' does not exist in the namespace 'CustomRenderer.Android' (are you missing an assembly reference?)
CustomButtonCompatRenderer.cs(55,38,55,54): error CS0234: The type or namespace name 'Graphics' does not exist in the namespace 'CustomRenderer.Android' (are you missing an assembly reference?)
CustomButtonCompatRenderer.cs(63,40,63,48): error CS0234: The type or namespace name 'Graphics' does not exist in the namespace 'CustomRenderer.Android' (are you missing an assembly reference?)
CustomButtonCompatRenderer.cs(66,68,66,79): error CS0117: 'Resource.Attribute' does not contain a definition for 'ColorAccent'
CustomButtonCompatRenderer.cs(67,52,67,68): error CS0234: The type or namespace name 'Graphics' does not exist in the namespace 'CustomRenderer.Android' (are you missing an assembly reference?)
CustomButtonCompatRenderer.cs(75,69,75,81): error CS0117: 'Resource.Attribute' does not contain a definition for 'StatePressed'

我的环境:

  • Visual Studio for mac:7.3.3(build 12)

  • Android SDK工具26.1.1

  • Android SDK构建工具26.0.3

  • Android SDK构建工具25.0.3

  • Android目标版:Android 7.1(API 25)

  • 最低Android版本:Android 4.0.3(API 15)

PS:我是Xamarin和C#的新手

1 个答案:

答案 0 :(得分:2)

在这里。带有CornerRadius和Padding的启用波纹的按钮。

您必须添加使用,命名空间和ExportRenderer属性。

MainActivity.Activity是我在应用初始化时设置的Context类型的属性。只需在Forms.Init。

之前的OnCreate方法中添加以下行:Activity = this;
public class ButtonRenderer : Xamarin.Forms.Platform.Android.ButtonRenderer
{
    // Your Button class
    Button button;

    float radius = 0;
    //--------------------------------------------------------------------------------//
    public ButtonRenderer() : base(MainActivity.Activity) { }

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

        button = Element as Button;

        if (e.OldElement == null)
        {
            if (IsPostLollipopAndroid) Control.Elevation = 2;

            radius = (float)button.CornerRadius;

            if (button.Padding != null)
            {
                Control.SetPadding(ToPx((int)button.Padding.Left), ToPx((int)button.Padding.Top), ToPx((int)button.Padding.Right), ToPx((int)button.Padding.Bottom));
            }

            SetColors();

            Control.SetMinHeight(0);
            Control.SetMinimumHeight(0);
            Control.SetMinWidth(0);
            Control.SetMinimumWidth(0);
        }
    }
    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        switch (e.PropertyName)
        {
            case nameof(VisualElement.BackgroundColor):
            case nameof(Button.PressedColor):
                {
                    SetColors();

                    break;
                }
            case nameof(Button.Padding):
                {
                    if (button.Padding != null)
                    {
                        Control.SetPadding((int)button.Padding.Left, (int)button.Padding.Top, (int)button.Padding.Right, (int)button.Padding.Bottom);
                    }

                    break;
                }
        }
    }

    public override bool DispatchTouchEvent(MotionEvent e)
    {
        if (!Element.InputTransparent) return base.DispatchTouchEvent(e);
        else return true;
    }

    void SetColors()
    {
        radius = (float)button.CornerRadius;

        StateListDrawable list = new StateListDrawable();

        if (IsPreLollipopAndroid)
        {
            GradientDrawable pressedDrawable;

            if (Element.BackgroundColor != Color.Transparent)
            {
                Android.Graphics.Color pressedColor = ChangeColorBrightness(Element.BackgroundColor, (button.PressedColor == ButtonPressedColor.Dark) ? 1f - (float).15 : 1f + (float).15).ToAndroid();
                pressedDrawable = new GradientDrawable(GradientDrawable.Orientation.LeftRight, new int[] { pressedColor, pressedColor });

                pressedDrawable.SetCornerRadius(ToPxFloat(radius));
            }
            else
            {
                Android.Graphics.Color pressedColor = (button.PressedColor == ButtonPressedColor.Dark) ? Color.Black.MultiplyAlpha(.15).ToAndroid() : Color.White.MultiplyAlpha(.15).ToAndroid();
                pressedDrawable = new GradientDrawable(GradientDrawable.Orientation.LeftRight, new int[] { pressedColor, pressedColor });

                pressedDrawable.SetCornerRadius(ToPxFloat(radius));
            }

            list.AddState(new int[] { Android.Resource.Attribute.StatePressed }, pressedDrawable);

            //--------------------------------------------------------------------------------//

            Android.Graphics.Color color = Element.BackgroundColor.ToAndroid();
            var normalDrawable = new GradientDrawable(GradientDrawable.Orientation.LeftRight, new int[] { color, color });

            normalDrawable.SetCornerRadius(ToPxFloat(radius));

            list.AddState(new int[] { -Android.Resource.Attribute.StatePressed }, normalDrawable);

            Control.Background = list;
        }
        else
        {
            GradientDrawable normalDrawable = new GradientDrawable();
            normalDrawable.SetCornerRadius(ToPxFloat(radius));
            normalDrawable.SetColor(Element.BackgroundColor.ToAndroid());

            float[] outerRadii = Enumerable.Repeat(ToPxFloat(radius), 8).ToArray();

            RoundRectShape r = new RoundRectShape(outerRadii, null, null);

            ShapeDrawable shapeDrawable = new ShapeDrawable(r);
            shapeDrawable.Paint.Color = Color.White.ToAndroid();

            var pressedColor = (button.PressedColor == ButtonPressedColor.Dark) ? Color.Black.MultiplyAlpha(.15).ToAndroid() : Color.White.MultiplyAlpha(.15).ToAndroid();
            var ripple = new RippleDrawable(ColorStateList.ValueOf(pressedColor), normalDrawable, shapeDrawable);

            Control.Background = ripple;
        }
    }

    public static int ToPx(double dp)
    {
        return (int)Android.Util.TypedValue.ApplyDimension(Android.Util.ComplexUnitType.Dip, (float)dp, Droid.MainActivity.Activity.Resources.DisplayMetrics);
    }

    public static float ToPxFloat(double dp)
    {
        return Android.Util.TypedValue.ApplyDimension(Android.Util.ComplexUnitType.Dip, (float)dp, Droid.MainActivity.Activity.Resources.DisplayMetrics);
    }

    public static bool IsPreLollipopAndroid => Android.OS.Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop;
    public static bool IsPostLollipopAndroid => Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop;

    public static Color ChangeColorBrightness(Color color, float factor)
    {
        return Color.FromRgba(color.R * factor, color.G * factor, color.B * factor, color.A);
    }
}

Button类有Padding,CornerRadius和PressedColor(包含DarkLight字段的枚举。)

希望它有所帮助!