用于xamarin表单的段控件自定义渲染器pcl android无法正常工作

时间:2017-04-27 02:01:19

标签: xamarin xamarin.android xamarin.forms

请在下面找到适用于Android的自定义渲染器以进行分段控制的代码。我从堆栈溢出中的一个帖子中复制了这个。它适用于IOS,但对于android失败。任何人有任何想法?我错过了什么。

这是事件

protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
        base.OnElementChanged(e);

        var layoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);

        var g = new RadioGroup(Context);
        g.Orientation = Orientation.Horizontal;
        g.CheckedChange += (sender, eventArgs) =>
        {
            var rg = (RadioGroup)sender;
            if (rg.CheckedRadioButtonId != -1)
            {
                var id = rg.CheckedRadioButtonId;
                var radioButton = rg.FindViewById(id);
                var radioId = rg.IndexOfChild(radioButton);
                var btn = (RadioButton)rg.GetChildAt(radioId);
                var selection = (String)btn.Text;
                e.NewElement.SelectedValue = selection;
            }
        };

        for (var i = 0; i < e.NewElement.Children.Count; i++)
        {
            var o = e.NewElement.Children[i];
            var v = (SegmentedControlButton)layoutInflater.Inflate(Resource.Layout.SegmentedControl, null);
  //Error at this above line

            v.Text = o.Text;
            if (i == 0)
                v.SetBackgroundResource(Resource.Drawable.segmented_control_first_background);
            else if (i == e.NewElement.Children.Count - 1)
                v.SetBackgroundResource(Resource.Drawable.segmented_control_last_background);
            g.AddView(v);
        }

        SetNativeControl(g);
    }
}

它发生在这条线上。

var v = (SegmentedControlButton)layoutInflater.Inflate(Resource.Layout.SegmentedControl, null);
v.Text = o.Text;

错误

 Android.Views.InflateException: Binary XML file line #1: Binary XML file line #1: 
 Error inflating class SegmentedControl.Android.SegmentedControlButton ---> Android.Views.InflateException: 
 Binary XML file line #1: Error inflating class SegmentedControl.Android.SegmentedControlButton ---> Java.Lang.ClassNotFoundException: 
 Didn't find class "SegmentedControl.Android.SegmentedControlButton" on path: DexPathList[[zip file "/data/app/com.mytestapp.myfirstapp-1/base.apk"],
 nativeLibraryDirectories=[/data/app/com.mytestapp.myfirstapp-1/lib/x86, /data/app/com.mytestapp.myfirstapp-1/base.apk!/lib/x86, /vendor/lib, /system/lib]]

2 个答案:

答案 0 :(得分:0)

尝试关注SegmentControlRenderer。我也添加了所需的文件。

在xaml文件中使用以下内容

<StackLayout BackgroundColor="#0A0E3F" Padding="10" Spacing="0" Grid.Row="0">
    <local:CustomSegmentedControl SelectedValue="Offices" x:Name="segmentControl" HorizontalOptions="FillAndExpand">
        <local:CustomSegmentedControl.Children>
            <local:CustomSegmentedControlOption Text="Control1" />
            <local:CustomSegmentedControlOption Text="Control2" />
            <local:CustomSegmentedControlOption Text="Control3" />
        </local:CustomSegmentedControl.Children>
    </local:CustomSegmentedControl>
</StackLayout>

确保将所有这7个文件放在正确的目录中。如果出现任何问题,请告诉我

SegmentedControlRenderer.cs (放置在Droid模块中)

using System;
using Xamarin.Forms.Platform.Android;
using Android.Widget;
using Android.Content;
using Android.Util;
using Android.Graphics;
using Android.Views;
using System.Collections.Generic;

[assembly: Xamarin.Forms.ExportRenderer(typeof(App.CustomSegmentedControl), typeof(App.Droid.SegmentedControlRenderer))]
namespace App.Droid
{
    public class SegmentedControlRenderer : ViewRenderer<CustomSegmentedControl, RadioGroup>
    {
        RadioGroup g = null;
        List<CustomSegmentedControlButton> listSegmentControl = new List<CustomSegmentedControlButton>();

        public SegmentedControlRenderer()
        {

        }

        protected override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig)
        {
            base.OnConfigurationChanged(newConfig);

            if (listSegmentControl == null)
                return;

            foreach (var control in listSegmentControl)
            {
                control.SetWidth(Resources.DisplayMetrics.WidthPixels / listSegmentControl.Count);
            }
        }

        protected override void OnElementChanged(ElementChangedEventArgs<TPSegmentedControl> e)
        {
            base.OnElementChanged(e);

            var layoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);

            g = new RadioGroup(Context);
            g.Orientation = Orientation.Horizontal;

            g.CheckedChange += (sender, eventArgs) =>
            {
                var rg = (RadioGroup)sender;
                if (rg.CheckedRadioButtonId != -1)
                {
                    var id = rg.CheckedRadioButtonId;
                    var radioButton = rg.FindViewById(id);
                    var radioId = rg.IndexOfChild(radioButton);
                    var btn = (RadioButton)rg.GetChildAt(radioId);
                    for (int i = 0; i < g.ChildCount; i++)
                    {
                        g.GetChildAt(i).SetBackgroundResource(Resource.Drawable.segment_control_option_bg);
                    }
                    btn.SetBackgroundResource(btn.Checked ? Resource.Drawable.segment_control_selected_option_bg : Resource.Drawable.segment_control_option_bg);
                    var selection = (String)btn.Text;
                    e.NewElement.SelectedValue = selection;
                }
            };


            for (var i = 0; i < e.NewElement.Children.Count; i++)
            {
                var o = e.NewElement.Children[i];
                var v = (TPSegmentedControlButton)layoutInflater.Inflate(Resource.Layout.SegmentedControl, null);
                v.Text = o.Text;
                int minWidth = Resources.DisplayMetrics.WidthPixels / e.NewElement.Children.Count;
                v.SetWidth(minWidth);
                v.SetBackgroundResource(v.Checked ? Resource.Drawable.segment_control_selected_option_bg : Resource.Drawable.segment_control_option_bg);
                g.AddView(v);
                listSegmentControl.Add(v);
            }

            try
            {
                g.GetChildAt(0).PerformClick();
            }
            catch (Exception ex)
            {

            }
            SetNativeControl(g);

        }
    }

    public class CustomSegmentedControlButton : RadioButton
    {
        private int lineHeightSelected;
        private int lineHeightUnselected;

        private Paint linePaint;

        public CustomSegmentedControlButton(Context context) : this(context, null)
        {
        }

        public CustomSegmentedControlButton(Context context, IAttributeSet attributes) : this(context, attributes, Resource.Attribute.segmentedControlOptionStyle)
        {
        }

        public CustomSegmentedControlButton(Context context, IAttributeSet attributes, int defStyle) : base(context, attributes, defStyle)
        {
            Initialize(attributes, defStyle);
        }

        private void Initialize(IAttributeSet attributes, int defStyle)
        {
            var a = this.Context.ObtainStyledAttributes(attributes, Resource.Styleable.SegmentedControlOption, defStyle, Resource.Style.SegmentedControlOption);

            var lineColor = Color.ParseColor("#4aa3f4");
            linePaint = new Paint();
            linePaint.Color = lineColor;

            lineHeightUnselected = a.GetDimensionPixelSize(Resource.Styleable.SegmentedControlOption_lineHeightUnselected, 0);
            lineHeightSelected = a.GetDimensionPixelSize(Resource.Styleable.SegmentedControlOption_lineHeightSelected, 0);

            a.Recycle();
        }

        protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);

            if (linePaint.Color != 0 && (lineHeightSelected > 0 || lineHeightUnselected > 0))
            {
                var lineHeight = Checked ? lineHeightSelected : lineHeightUnselected;

                if (lineHeight > 0)
                {
                    var rect = new Rect(0, Height - lineHeight, Width, Height);
                    canvas.DrawRect(rect, linePaint);
                }
            }
        }
    }
}

CustomSegmentedControl.cs (置于共享应用模块中)

using System;
using Xamarin.Forms;
using System.Collections.Generic;

namespace App
{
    public class CustomSegmentedControl : View, IViewContainer<CustomSegmentedControlOption>
    {
        public IList<CustomSegmentedControlOption> Children { get; set; }

        public TPSegmentedControl()
        {
            Children = new List<CustomSegmentedControlOption>();
        }

        public event ValueChangedEventHandler ValueChanged;

        public delegate void ValueChangedEventHandler(object sender, EventArgs e);

        private string selectedValue;

        public string SelectedValue
        {
            get { return selectedValue; }
            set
            {
                selectedValue = value;
                if (ValueChanged != null)
                    ValueChanged(this, EventArgs.Empty);
            }
        }
    }

    public class CustomSegmentedControlOption : View
    {
        public static readonly BindableProperty TextProperty = BindableProperty.Create<CustomSegmentedControlOption, string>(p => p.Text, "");

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public TPSegmentedControlOption()
        {
        }
    }
}

CustomSegmentedView.cs (放入共享应用模块)

using System;
using XLabs.Forms.Controls;
namespace App
{
    public class CustomSegmentedView : SegmentedControlView
    {
        public ISegmentedControlView Listener { get; set;}
        public CustomSegmentedView()
        {

        }

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            if (propertyName == "SelectedItem") {
                if (Listener == null)
                    return;

                Listener.SegmentedControlOnValueChanged(SelectedItem);
            }
        }
    }

    public interface ISegmentedControlView {
        void SegmentedControlOnValueChanged(int selectedIndex);
    }
}

SegmentedControl.axml (放置在Droid / Resources / layout中)

<?xml version="1.0" encoding="utf-8"?>
<App.Droid.CustomSegmentedControlButton
    style="@style/SegmentedControlOption" />

attrs.xml (放置在Droid / Resources / values中)

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <declare-styleable name="SegmentedControlOption">
    <attr name="segmentedControlOptionStyle" format="string" />

    <attr name="lineColor" format="color" />
    <attr name="lineHeightUnselected" format="dimension" />
    <attr name="lineHeightSelected" format="dimension" />
  </declare-styleable>

  <declare-styleable name="ScaleImageView">

  </declare-styleable>

</resources>

segment_control_option_bg.xml (放置在Droid / Resources / layout中)

<shape xmlns:android="http://schemas.android.com/apk/res/android">

  <solid android:color="#4aa3f4" />

  <stroke
        android:width="0.5dp"
        android:color="#0a0e3f" />

  <corners
      android:bottomLeftRadius="0dp"
      android:bottomRightRadius="0dp"
      android:topLeftRadius="0dp"
      android:topRightRadius="0dp" />

</shape>

segment_control_selected_option_bg.xml (放置在Droid / Resources / layout中)

<shape xmlns:android="http://schemas.android.com/apk/res/android">

  <solid android:color="#0271d5" />

  <stroke
        android:width="0.5dp"
        android:color="#0a0e3f" />

  <corners
      android:bottomLeftRadius="0dp"
      android:bottomRightRadius="0dp"
      android:topLeftRadius="0dp"
      android:topRightRadius="0dp" />

</shape>

答案 1 :(得分:-1)

问题在于segmented control.axml我提供了错误的命名空间。

<?xml version="1.0" encoding="utf-8"?>
<*MyFirstApp*.Droid.SegmentedControlButton
style="@style/SegmentedControlOption" />

它解决了一切。 #Needle in the haystack#

@ user12345我已经在帖子中提供了正确的代码。谢谢你的巧妙解释。