using System.Linq; using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace XCharts { /// /// 图例组件。 /// 图例组件展现了不同系列的标记,颜色和名字。可以通过点击图例控制哪些系列不显示。 /// [System.Serializable] public class Legend : MainComponent, IPropertyChanged, IEquatable { /// /// Selected mode of legend, which controls whether series can be toggled displaying by clicking legends. /// It is enabled by default, and you may set it to be false to disabled it. /// 图例选择的模式,控制是否可以通过点击图例改变系列的显示状态。默认开启图例选择,可以设成 None 关闭。 /// public enum SelectedMode { /// /// 多选。 /// Multiple, /// /// 单选。 /// Single, /// /// 无法选择。 /// None } [SerializeField] private bool m_Show = true; [SerializeField] private SelectedMode m_SelectedMode; [SerializeField] private Orient m_Orient = Orient.Horizonal; [SerializeField] private Location m_Location = Location.defaultRight; [SerializeField] private float m_ItemWidth = 50.0f; [SerializeField] private float m_ItemHeight = 20.0f; [SerializeField] private float m_ItemGap = 5; [SerializeField] private int m_ItemFontSize = 18; [SerializeField] private string m_Formatter; [SerializeField] private List m_Data = new List(); private Dictionary m_DataBtnList = new Dictionary(); /// /// Whether to show legend component. /// 是否显示图例组件。 /// public bool show { get { return m_Show; } set { m_Show = value; } } /// /// Selected mode of legend, which controls whether series can be toggled displaying by clicking legends. /// 选择模式。控制是否可以通过点击图例改变系列的显示状态。默认开启图例选择,可以设成 None 关闭。 /// /// public SelectedMode selectedMode { get { return m_SelectedMode; } set { m_SelectedMode = value; } } /// /// Specify whether the layout of legend component is horizontal or vertical. /// 布局方式是横还是竖。 /// public Orient orient { get { return m_Orient; } set { m_Orient = value; } } /// /// The location of legend. /// 图例显示的位置。 /// public Location location { get { return m_Location; } set { m_Location = value; } } /// /// the width of legend item. /// 每个图例项的宽度。 /// public float itemWidth { get { return m_ItemWidth; } set { m_ItemWidth = value; } } /// /// the height of legend item. /// 每个图例项的高度。 /// public float itemHeight { get { return m_ItemHeight; } set { m_ItemHeight = value; } } /// /// The distance between each legend, horizontal distance in horizontal layout, and vertical distance in vertical layout. /// 图例每项之间的间隔。横向布局时为水平间隔,纵向布局时为纵向间隔。 /// public float itemGap { get { return m_ItemGap; } set { m_ItemGap = value; } } /// /// font size of item text. /// 图例项的字体大小。 /// public int itemFontSize { get { return m_ItemFontSize; } set { m_ItemFontSize = value; } } /// /// 图例内容字符串模版格式器。支持用 \n 换行。 /// 模板变量为图例名称 {name} /// public string formatter { get { return m_Formatter; } set { m_Formatter = value; } } /// /// Data array of legend. An array item is usually a name representing string. (If it is a pie chart, /// it could also be the name of a single data in the pie chart) of a series. /// If data is not specified, it will be auto collected from series. /// 图例的数据数组。数组项通常为一个字符串,每一项代表一个系列的 name(如果是饼图,也可以是饼图单个数据的 name)。 /// 如果 data 没有被指定,会自动从当前系列中获取。指定data时里面的数据项和serie匹配时才会生效。 /// public List data { get { return m_Data; } } /// /// the button list of legend. /// 图例按钮列表。 /// /// public Dictionary buttonList { get { return m_DataBtnList; } } /// /// 一个在顶部居中显示的默认图例。 /// public static Legend defaultLegend { get { var legend = new Legend { m_Show = false, m_SelectedMode = SelectedMode.Multiple, m_Orient = Orient.Horizonal, m_Location = Location.defaultTop, m_ItemWidth = 60.0f, m_ItemHeight = 20.0f, m_ItemGap = 5, m_ItemFontSize = 16 }; legend.location.top = 30; return legend; } } public void Copy(Legend legend) { m_Show = legend.show; m_SelectedMode = legend.selectedMode; m_Orient = legend.orient; m_Location.Copy(legend.location); m_ItemWidth = legend.itemWidth; m_ItemHeight = legend.itemHeight; m_ItemGap = legend.itemGap; m_ItemFontSize = legend.itemFontSize; m_Data.Clear(); foreach (var d in legend.data) m_Data.Add(d); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } else if (obj is Legend) { return Equals((Legend)obj); } else { return false; } } public bool Equals(Legend other) { if (ReferenceEquals(null, other)) { return false; } return show == other.show && selectedMode == other.selectedMode && orient == other.orient && location == other.location && itemWidth == other.itemWidth && itemHeight == other.itemHeight && itemGap == other.itemGap && itemFontSize == other.itemFontSize && ChartHelper.IsValueEqualsList(m_Data, other.data); } public static bool operator ==(Legend left, Legend right) { if (ReferenceEquals(left, null) && ReferenceEquals(right, null)) { return true; } else if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) { return false; } return Equals(left, right); } public static bool operator !=(Legend left, Legend right) { return !(left == right); } public override int GetHashCode() { return base.GetHashCode(); } /// /// 清空 /// public void ClearData() { m_Data.Clear(); } /// /// 是否包括由指定名字的图例 /// /// /// public bool ContainsData(string name) { return m_Data.Contains(name); } /// /// 移除指定名字的图例 /// /// public void RemoveData(string name) { if (m_Data.Contains(name)) { m_Data.Remove(name); } } /// /// 添加图例项 /// /// public void AddData(string name) { if (!m_Data.Contains(name) && !string.IsNullOrEmpty(name)) { m_Data.Add(name); } } /// /// 获得指定索引的图例 /// /// /// public string GetData(int index) { if (index >= 0 && index < m_Data.Count) { return m_Data[index]; } return null; } /// /// 获得指定图例的索引 /// /// /// public int GetIndex(string legendName) { return m_Data.IndexOf(legendName); } /// /// 移除所有图例按钮 /// public void RemoveButton() { m_DataBtnList.Clear(); } /// /// 给图例绑定按钮 /// /// /// /// public void SetButton(string name, Button btn, int total) { int index = m_DataBtnList.Values.Count; btn.transform.localPosition = GetButtonLocationPosition(total, index); m_DataBtnList[name] = btn; btn.gameObject.SetActive(show); btn.GetComponentInChildren().text = name; } /// /// 更新图例按钮颜色 /// /// /// public void UpdateButtonColor(string name, Color color) { if (m_DataBtnList.ContainsKey(name)) { m_DataBtnList[name].GetComponent().color = color; } } /// /// 参数变更时的回调处理 /// public void OnChanged() { m_Location.OnChanged(); } /// /// 根据图例的布局和位置类型获得具体位置 /// /// /// /// private Vector2 GetButtonLocationPosition(int size, int index) { switch (m_Orient) { case Orient.Vertical: switch (m_Location.align) { case Location.Align.TopCenter: case Location.Align.TopLeft: case Location.Align.TopRight: return new Vector2(0, -index * (itemHeight + itemGap)); case Location.Align.Center: case Location.Align.CenterLeft: case Location.Align.CenterRight: float totalHeight = size * itemHeight + (size - 1) * itemGap; float startY = totalHeight / 2; return new Vector2(0, startY - index * (itemHeight + itemGap)); case Location.Align.BottomCenter: case Location.Align.BottomLeft: case Location.Align.BottomRight: return new Vector2(0, (size - index - 1) * (itemHeight + itemGap)); } return Vector2.zero; case Orient.Horizonal: switch (m_Location.align) { case Location.Align.TopLeft: case Location.Align.CenterLeft: case Location.Align.BottomLeft: return new Vector2(index * (itemWidth + itemGap), 0); case Location.Align.TopCenter: case Location.Align.Center: case Location.Align.BottomCenter: float totalWidth = size * itemWidth + (size - 1) * itemGap; float startX = totalWidth / 2; return new Vector2(-startX + itemWidth / 2 + index * (itemWidth + itemGap), 0); case Location.Align.TopRight: case Location.Align.CenterRight: case Location.Align.BottomRight: return new Vector2(-(size - index - 1) * (itemWidth + itemGap), 0); } return Vector2.zero; } return Vector2.zero; } /// /// 从json字符串解析数据,json格式如:['邮件营销','联盟广告','视频广告','直接访问','搜索引擎'] /// /// public override void ParseJsonData(string jsonData) { if (string.IsNullOrEmpty(jsonData) || !m_DataFromJson) return; m_Data = ChartHelper.ParseStringFromString(jsonData); } public string GetFormatterContent(string category) { if (string.IsNullOrEmpty(m_Formatter)) return category; else { var content = m_Formatter.Replace("{name}", category); content = content.Replace("\\n", "\n"); content = content.Replace("
", "\n"); return content; } } } }