Unity_条形图(柱状图)+ UI动画

  • 效果
  • 介绍
  • 关键字
  • 分析及代码
  • 绘图数据
  • 1.定义参数
  • 2.左侧刻度
  • 3.底部描述
  • 4.画条形
  • 5.整体代码
  • 7.使用示例


效果

android简单柱状图 ui柱状图_unity

介绍

实现柱状图的大体思路和数学画图一致:先画底部刻度(底部描述),再画条形。

关键字

1. 对象池
2. DoTween 动画

分析及代码

绘图数据

定义绘图数据的结构GraphData,包含数据的描述信息_desc,数据的值_value。在需要绘图时传递一组GraphData的类型的数据给此模块就可以实现柱状图的绘制。

[System.Serializable]
public struct GraphData
{
    public string _desc;
    [Range(0, 100)]
    public float _value;

    public float Rate
    {
        get
        {
            return _value / 100;
        }
    }

    public GraphData(string desc, float value)
    {
        _desc = desc;
        _value = value;
    }
}

1.定义参数

下图是Unity的布局,为了方便管理,在BarGraphPanel中分别设置了LeftSide(左侧刻度),DescContent(底部描述),BarContent(条形)三个空对象存放相应的子元素。

android简单柱状图 ui柱状图_android简单柱状图_02

public GraphData[] _datas;//数据
    public float _barWidth = 20;//条宽
    public Transform _leftSide;//左侧描述
    public RectTransform _descContent;//描述Content
    public RectTransform _barContent;//条Content
    public Text _descPrefab;//描述Prefab
    public Image _barPrefab;//条Prefab
    public float _tweenTime = 1f;//动画时间

    //描述、条 管理
    private Text[] _descs;
    private Image[] _bars;

2.左侧刻度

因为背景、左侧刻度是不会改变的,所以初始化左侧刻度的方法只需要在Start方法中调用一次就可以(背景我直接用的一张10条横线的图片)。

/// <summary>
    /// 初始化条形图
    /// </summary>
    public void InitBarGraph(GraphData[] data)
    {
        //leftSide
        for (int i = 0; i < _leftSide.childCount; i++)
        {
            _leftSide.GetChild(i).GetComponent<Text>().text = (100 - i * 10).ToString();
        }
    }

3.底部描述

底部描述使用了Unity的自动布局组件,因为在下一步需要绘制时是需要用到这里的位置信息,所以需要在结尾处刷新UI的布局。其次,为了防止动态的刷新数据过于频繁,使用到了对象池的优化(链接)。

/// <summary>
    /// 底部描述
    /// </summary>
    private void DrawDesc()
    {
        _descs = new Text[_datas.Length];
        for (int i = 0; i < _datas.Length; i++)
        {
            Text desc = ObjectPool.Instance.GetObject(_descPrefab.name, _descContent).GetComponent<Text>();
            desc.text = _datas[i]._desc;
            desc.transform.SetAsLastSibling();//使用对象池和自动布局组件会调乱顺序,要重置
            desc.gameObject.SetActive(true);
            _descs[i] = desc;
        }
        LayoutRebuilder.ForceRebuildLayoutImmediate(_descContent);//使用自动布局组件要刷新UI,刷新位置
    }

4.画条形

根据底部描述的位置、数据的值计算条形的高度(Image的FillAmount)。

/// <summary>
    /// 画条
    /// </summary>
    private void DrawBar()
    {
        _bars = new Image[_datas.Length];
        for (int i = 0; i < _datas.Length; i++)
        {
            Image bar = ObjectPool.Instance.GetObject(_barPrefab.name, _barContent).GetComponent<Image>();
            bar.rectTransform.sizeDelta = new Vector2(_barWidth, bar.rectTransform.sizeDelta.y);
            bar.rectTransform.localPosition = new Vector3(_descs[i].rectTransform.localPosition.x, bar.rectTransform.localPosition.y, 0);//锚点在中心
            bar.fillAmount = 0;
            bar.gameObject.SetActive(true);
            bar.DOFillAmount(_datas[i].Rate, _tweenTime);
            _bars[i] = bar;
        }
    }

5.整体代码

using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 条形图
/// </summary>
public class UIBarGraphManager : MonoBehaviour
{
    public GraphData[] _datas;//数据
    public float _barWidth = 20;//条宽
    public Transform _leftSide;//左侧描述
    public RectTransform _descContent;//描述Content
    public RectTransform _barContent;//条Content
    public Text _descPrefab;//描述Prefab
    public Image _barPrefab;//条Prefab
    public float _tweenTime = 1f;//动画时间

    //描述、条 管理
    private Text[] _descs;
    private Image[] _bars;

    private void Awake()
    {
        ObjectPool.Instance.SetPrefab(_descPrefab.gameObject);
        ObjectPool.Instance.SetPrefab(_barPrefab.gameObject);
    }

    /// <summary>
    /// 初始化条形图
    /// </summary>
    public void InitBarGraph(GraphData[] data)
    {
        //leftSide
        for (int i = 0; i < _leftSide.childCount; i++)
        {
            _leftSide.GetChild(i).GetComponent<Text>().text = (100 - i * 10).ToString();
        }
        RefeshBarGraph(data);
    }

    /// <summary>
    /// 刷新条形图
    /// </summary>
    public void RefeshBarGraph(GraphData[] data)
    {
        _datas = data;
        ClearTransform(_descContent);
        ClearTransform(_barContent);
        DrawDesc();
        DrawBar();
    }

    /// <summary>
    /// 底部描述
    /// </summary>
    private void DrawDesc()
    {
        _descs = new Text[_datas.Length];
        for (int i = 0; i < _datas.Length; i++)
        {
            Text desc = ObjectPool.Instance.GetObject(_descPrefab.name, _descContent).GetComponent<Text>();
            desc.text = _datas[i]._desc;
            desc.transform.SetAsLastSibling();//使用对象池和自动布局组件会调乱顺序,要重置
            desc.gameObject.SetActive(true);
            _descs[i] = desc;
        }
        LayoutRebuilder.ForceRebuildLayoutImmediate(_descContent);//使用自动布局组件要刷新UI,刷新位置
    }

    /// <summary>
    /// 画条
    /// </summary>
    private void DrawBar()
    {
        _bars = new Image[_datas.Length];
        for (int i = 0; i < _datas.Length; i++)
        {
            Image bar = ObjectPool.Instance.GetObject(_barPrefab.name, _barContent).GetComponent<Image>();
            bar.rectTransform.sizeDelta = new Vector2(_barWidth, bar.rectTransform.sizeDelta.y);
            bar.rectTransform.localPosition = new Vector3(_descs[i].rectTransform.localPosition.x, bar.rectTransform.localPosition.y, 0);//锚点在中心
            bar.fillAmount = 0;
            bar.gameObject.SetActive(true);
            bar.DOFillAmount(_datas[i].Rate, _tweenTime);
            _bars[i] = bar;
        }
    }

    /// <summary>
    /// 入池
    /// </summary>
    /// <param name="trans"></param>
    /// <param name="pool"></param>
    private void ClearTransform(Transform parent)
    {
        for (int i = 1; i < parent.childCount; i++)
        {
            ObjectPool.Instance.RecycleObj(parent.GetChild(i).gameObject, parent);
        }
    }
    
}

7.使用示例

初始化时调用InitBarGraph(),刷新数据时调用RefeshBarGraph()。下面是示例:

using UnityEngine;

public class UIGraphManager : MonoBehaviour
{
    public UIBarGraphManager _bar;
    private GraphData[] _datas;

    private void Start()
    {
        RefeshData();
        _bar.InitBarGraph(_datas);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            RefeshData();
            _bar.RefeshBarGraph(_datas);
        }
    }

    /// <summary>
    /// 刷新数据
    /// </summary>
    public void RefeshData()
    {
        _datas = new GraphData[8];
        for (int i = 0; i < _datas.Length; i++)
        {
            _datas[i] = new GraphData("数据" + i, Random.Range(0, 100));
        }
    }
}