结合工作需求用GUI写一个快捷操作Excel的界面

因工作过程中需要考虑游戏内中文显示方便国际化,游戏内涉及到中文的固定字符串用Excel存储。反复操作Excel对程序员来说很麻烦,于是有了写这样一个偷懒工具的想法。

初版界面如图:


ExcelEditor.png

代码分为四块:
1、Strings.cs
序列化生成strings.asset的数据结构。

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

[CreateAssetMenu]
public class Strings : ScriptableObject
{
    public static readonly string STRING_ASSET_PATH = "Assets/Game/strings.asset";

    public List _keys;
    public List _vals;
    public Dictionary _dic;

    private static Strings _instance;
    public static Strings Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = AssetDatabase.LoadAssetAtPath(STRING_ASSET_PATH);
            }
            return _instance;
        }
    }

    public void UpdateDictionary(List keys, List vals)
    {
        _keys = keys;
        _vals = vals;

        if (_keys == null)
        {
            return;
        }

        _dic = new Dictionary(_keys.Count);
        for (int i = 0; i < _keys.Count; ++i)
        {
            _dic.Add(_keys[i], _vals[i]);
        }
    }

    public string Get(string key)
    {
        string val;
        if (_dic.TryGetValue(key, out val))
        {
            return val;
        }
        return key;
    }

}

2、ExcelHelper.cs
封装的一些需要用到的操作Excel的公用方法。

using OfficeOpenXml;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine;

public class ExcelHelper
{
    public static void GetStringsFromFiles(List keys, List vals)
    {
        string[] files = GetFiles();

        foreach (string file in files)
        {
            string extension = Path.GetExtension(file);
            string pattern = ".xlsx$";
            bool isMatch = Regex.IsMatch(extension, pattern);
            if (isMatch)
            {
                ReadSingleExcel(file, keys, vals); ;
            }
        }
    }

    public static string[] GetFiles()
    {
        string dataPath = Application.dataPath;
        int lastIndex = dataPath.LastIndexOf('/');
        string rootPath = dataPath.Substring(0, lastIndex);
        rootPath = Path.Combine(rootPath, "Strings");
        string[] files = Directory.GetFiles(rootPath);
        return files;
    }

    public static void ReadSingleExcel(string path, List keys, List vals)
    {
        Debug.Log("read path:" + path);

        FileStream fs = new FileStream(path, FileMode.Open);
        ExcelPackage package = new ExcelPackage(fs);

        ExcelWorksheet sheet = package.Workbook.Worksheets[1];
        int rowCount = sheet.Dimension.End.Row;

        for (int row = 2; row <= rowCount; ++row)
        {
            string key = sheet.GetValue(row, 1).ToString();
            string cnVal = sheet.GetValue(row, 2).ToString();
            keys.Add(key);
            vals.Add(cnVal);
        }

        fs.Close();
        fs.Dispose();
    }

    public static void ModifiedItem(string path, int row, string key, string cnVal = "", string enVal = "en")
    {
        Debug.Log("write to path:" + path + "   key:" + key + " cnVal:" + cnVal);
        FileInfo fi = new FileInfo(path);
        if (!fi.Exists)
        {
            return;
        }
        ExcelPackage package = new ExcelPackage(fi);
        ExcelWorksheet sheet = package.Workbook.Worksheets[1];
        int operateRow = row;
        sheet.SetValue(operateRow, 1, key);
        sheet.SetValue(operateRow, 2, cnVal);

        package.SaveAs(fi);
    }

    public static void AddItem(string path, string key, string val)
    {
        Debug.Log("add to path:" + path + "   key:" + key + "   val:" + val);
        FileInfo fi = new FileInfo(path);
        if (!fi.Exists)
        {
            return;
        }
        ExcelPackage package = new ExcelPackage(fi);
        ExcelWorksheet sheet = package.Workbook.Worksheets[1];
        int rowCount = sheet.Dimension.Rows;
        int operateRow = rowCount + 1;
        sheet.SetValue(operateRow, 1, key);
        sheet.SetValue(operateRow, 2, val);
        package.SaveAs(fi);
    }

    public static void DeleteItem(string path, int row)
    {
        Debug.Log("delete row:" + row + " in path:" + path);
        FileInfo fi = new FileInfo(path);
        if (!fi.Exists)
        {
            return;
        }
        ExcelPackage package = new ExcelPackage(fi);
        ExcelWorksheet sheet = package.Workbook.Worksheets[1];
        sheet.DeleteRow(row);
        package.SaveAs(fi);
    }
}

3、ExcelTempInstance.cs
可视化编辑界面的一些临时缓存的数据信息。

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Text.RegularExpressions;

/// 
/// ExcelEditorWindow内容显示临时缓存结构
/// 
public class ExcelTempInstance
{
    private List _cacheKeys = new List();
    private List _cacheVals = new List();
    
    public List keys;
    public List vals;

    private string _path;
    public string newKey = "";
    public string newVal = "";

    /// 
    /// all string k-v
    /// 
    private Dictionary _dic;
    /// 
    /// all string v-k
    /// 
    private Dictionary _dicReverse;

    internal void SerializedExcelData(string path)
    {
        _path = path;
        newKey = "";
        newVal = "";
        _cacheKeys.Clear();
        _cacheVals.Clear();
        ExcelHelper.ReadSingleExcel(_path, _cacheKeys, _cacheVals);

        keys = new List(_cacheKeys);
        vals = new List(_cacheVals);

        _dic = new Dictionary(Strings.Instance._keys.Count);
        _dicReverse = new Dictionary(Strings.Instance._keys.Count);
        //Strings.Instance._vals;
        for (int i = 0; i < Strings.Instance._keys.Count; ++i)
        {
            _dic.Add(Strings.Instance._keys[i], Strings.Instance._vals[i]);
            _dicReverse.Add(Strings.Instance._vals[i], Strings.Instance._keys[i]);
        }
        
    }

    //modified
    internal void ModifiedItem(int index)
    {
        string cacheKey = _cacheKeys[index];
        string key = keys[index];

        string cacheVal = _cacheVals[index];
        string val = vals[index];

        bool isKeyModified = !cacheKey.Equals(key);
        bool isValModified = !cacheVal.Equals(val);

        if (!isKeyModified && !isValModified)
        {
            EditorUtility.DisplayDialog("ERROR!", "No modified here!!!", "OK");
            return;
        }

        ExcelHelper.ModifiedItem(_path, index + 2, keys[index], vals[index]);
        SerializedExcelData(_path);
        Debug.Log("apply modified success~");
    }

    //delete
    internal void DeleteItem(int index)
    {
        _cacheKeys.RemoveAt(index);
        _cacheVals.RemoveAt(index);

        keys.RemoveAt(index);
        vals.RemoveAt(index);

        ExcelHelper.DeleteItem(_path, index + 2);
        SerializedExcelData(_path);
    }

    //add
    internal void AddItem()
    {
        if (string.IsNullOrEmpty(newKey) || string.IsNullOrEmpty(newVal))
        {
            EditorUtility.DisplayDialog("ERROR!", "Empty param!!!", "OK");
            return;
        }

        string val;
        if (_dic.TryGetValue(newKey, out val))
        {
            EditorUtility.DisplayDialog("ERROR!", "param \"" + newKey + "\" is repeated!!!", "OK");
            return;
        }

        string valReverse;
        if (_dicReverse.TryGetValue(newVal, out valReverse))
        {
            EditorUtility.DisplayDialog("ERROR!", "param \"" + newVal + "\" is repeated!!!", "OK");
            return;
        }

        ExcelHelper.AddItem(_path, newKey, newVal);
        SerializedExcelData(_path);
    }

    internal void SearchItems(string matchKey, string matchVal)
    {
        keys.Clear();
        vals.Clear();

        bool isEmptyKey = string.IsNullOrEmpty(matchKey);
        bool isEmptyVal = string.IsNullOrEmpty(matchVal);
        if (isEmptyKey && isEmptyVal)
        {
            return;
        }

        string baseKey;
        string baseVal;
        for (int i = 0; i < Strings.Instance._keys.Count; ++i)
        {
            baseKey = Strings.Instance._keys[i];
            baseVal = Strings.Instance._vals[i];

            if (!isEmptyKey)
            {
                if (Regex.IsMatch(baseKey, matchKey))
                {
                    keys.Add(baseKey);
                    vals.Add(baseVal);
                    continue;
                }
            }

            if (!isEmptyVal)
            {
                if (Regex.IsMatch(baseVal, matchVal))
                {
                    keys.Add(baseKey);
                    vals.Add(baseVal);
                }
            }
        }
    }

    internal void RestoreItem(int index)
    {
        keys[index] = _cacheKeys[index];
        vals[index] = _cacheVals[index];
    }
}

/// 
/// Excel内容展示类型
/// 
enum ExcelShowType
{
    /// 
    /// 可操作状态
    /// 
    Default = 0,
    /// 
    /// 搜索状态,不可操作
    /// 
    Search = 1 << 0,
}

4、ExcelEditorWindow.cs
可视化操作界面。

using UnityEngine;
using UnityEditor;
using System.IO;

public partial class ExcelEditorWindow : EditorWindow
{
    [MenuItem("Tools/Strings/Excel Editor")]
    static void DoMain()
    {
        EditorWindow.GetWindowWithRect(typeof(ExcelEditorWindow), new Rect(0, 0, 960, 640), true, "ExcelEditor");
    }

    //head
    private string _searchKey = "";
    private string _searchVal = "";
    //file list
    string[] _filePaths;
    string[] _fileNames;
    private int _nameSelectedIdx = 0;
    private Vector2 _nameScrollPos = Vector2.zero;
    private ExcelTempInstance _excelInst = new ExcelTempInstance();
    //file content
    private Vector2 _contentScrollPos = Vector2.zero;

    //status
    ExcelShowType _showType;

    //use for initialize
    private void OnEnable()
    {
        _filePaths = ExcelHelper.GetFiles();
        _fileNames = new string[_filePaths.Length];

        for (int i = 0; i < _filePaths.Length; ++i)
        {
            _fileNames[i] = Path.GetFileNameWithoutExtension(_filePaths[i]);
        }

        SerializedExcelData();
    }

    private void SerializedExcelData()
    {
        string path = _filePaths[_nameSelectedIdx];
        _excelInst.SerializedExcelData(path);

        _showType = ExcelShowType.Default;
    }

    private void OnGUI()
    {
        DrawHeadTool();
        DrawFileNameList();
        DrawFileContent();

        Event e = Event.current;
        if (e.keyCode == KeyCode.KeypadEnter)
        {
            GUI.FocusControl(null);
        }
    }

    private void DrawHeadTool()
    {
        Rect boxRect = new Rect(15f, 8.0f, Screen.width * 0.965f, Screen.height * 0.05f);
        GUI.Box(boxRect, "");

        Rect areaRect = new Rect(18f, 12f, Screen.width * 0.965f, Screen.height * 0.05f);
        GUILayout.BeginArea(areaRect);
        GUILayout.BeginHorizontal(GUILayout.Height(20));
        GUILayout.Space(5);
        if (GUILayout.Button("open path", GUILayout.Width(100), GUILayout.Height(20)))
        {
            string dataPath = Application.dataPath;
            int lastIndex = dataPath.LastIndexOf('/');
            string rootPath = dataPath.Substring(0, lastIndex);
            rootPath = Path.Combine(rootPath, "Strings");
            System.Diagnostics.Process.Start(rootPath);
        }

        DrawHeadSearch();

        GUILayout.EndHorizontal();
        GUILayout.EndArea();
    }

    private void DrawHeadSearch()
    {
        GUILayout.Space(60);
        GUILayout.Label("key:", GUILayout.Width(30));
        _searchKey = EditorGUILayout.TextField(_searchKey, GUILayout.Width(180));
        GUILayout.Space(10);
        GUILayout.Label("value:", GUILayout.Width(40));
        _searchVal = EditorGUILayout.TextField(_searchVal, GUILayout.Width(180));
        GUILayout.Space(5);
        if (GUILayout.Button("search", GUILayout.Width(60), GUILayout.Height(20)))
        {
            _showType = ExcelShowType.Search;
            _excelInst.SearchItems(_searchKey, _searchVal);
        }
    }

    private void DrawFileNameList()
    {
        Vector2 boxStartPosition = new Vector2(15f, Screen.height * 0.08f);
        Vector2 boxSize = new Vector2(150f, Screen.height * 0.9f);
        Rect boxRect = new Rect(boxStartPosition, boxSize);
        GUI.Box(boxRect, "Files List");

        Vector2 offsetPosition = new Vector2(0f, 25f);
        Vector2 offsetSize = new Vector2(0f, -30f);
        Rect areaRect = new Rect(boxStartPosition + offsetPosition, boxSize + offsetSize);
        GUILayout.BeginArea(areaRect);
        _nameScrollPos = EditorGUILayout.BeginScrollView(_nameScrollPos);

        EditorGUI.BeginChangeCheck();
        _nameSelectedIdx = GUILayout.SelectionGrid(_nameSelectedIdx, _fileNames, 1, GUILayout.Height(30f * _fileNames.Length), GUILayout.Width(125));
        if (EditorGUI.EndChangeCheck())
        {
            SerializedExcelData();
        }

        EditorGUILayout.EndScrollView();
        GUILayout.EndArea();
    }

    private void DrawFileContent()
    {
        Vector2 boxStartPosition = new Vector2(180f, Screen.height * 0.08f);
        Vector2 boxSize = new Vector2(Screen.width - 200f, Screen.height * 0.9f);
        Rect boxRect = new Rect(boxStartPosition, boxSize);
        GUI.Box(boxRect, "");

        Vector2 offsetPosition = new Vector2(0f, 10f);
        Vector2 offsetSize = new Vector2(0f, -15f);
        Rect areaRect = new Rect(boxStartPosition + offsetPosition, boxSize + offsetSize);
        GUILayout.BeginArea(areaRect);

        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(150);
        EditorGUILayout.LabelField("key");
        EditorGUILayout.LabelField("value");
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(10);

        _contentScrollPos = EditorGUILayout.BeginScrollView(_contentScrollPos);
        
        for (int i = 0; i < _excelInst.keys.Count; ++i)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("no." + (i + 1), GUILayout.MaxWidth(40));
            EditorGUI.BeginChangeCheck();
            _excelInst.keys[i] = EditorGUILayout.TextField(_excelInst.keys[i], GUILayout.MaxWidth(180));
            _excelInst.vals[i] = EditorGUILayout.TextField(_excelInst.vals[i]);
            if (EditorGUI.EndChangeCheck())
            {
                //Debug.Log(_excelInst.keys[i] + "  ---> " + _excelInst.vals[i]);
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("");

            if (_showType == ExcelShowType.Default)
            {
                if (GUILayout.Button("apply", GUILayout.Width(100), GUILayout.Height(20)))
                {
                    if (EditorUtility.DisplayDialog("CONFIRM", "Are you sure to change???", "OK", "NO"))
                    {
                        _excelInst.ModifiedItem(i);
                        GUIUtility.keyboardControl = 0;
                    }
                    else
                    {
                        _excelInst.RestoreItem(i);
                        GUIUtility.keyboardControl = 0;
                    }
                }
                if (GUILayout.Button("delete", GUILayout.Width(80), GUILayout.Height(20)))
                {
                    if (EditorUtility.DisplayDialog("CONFIRM", "Are you sure to delete???", "OK", "NO"))
                    {
                        _excelInst.DeleteItem(i);
                        GUIUtility.keyboardControl = 0;
                    }
                }
            }
            
            EditorGUILayout.EndHorizontal();
            GUILayout.Space(15);
        }
        
        EditorGUILayout.EndScrollView();

        if (_showType == ExcelShowType.Default)
        {
            DrawFileContentBottom();
        }

        GUILayout.EndArea();
    }

    private void DrawFileContentBottom()
    {
        GUILayout.Space(20);

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("new", GUILayout.MaxWidth(45));
        _excelInst.newKey = EditorGUILayout.TextField(_excelInst.newKey, GUILayout.MaxWidth(180));
        _excelInst.newVal = EditorGUILayout.TextField(_excelInst.newVal);
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("");
        if (GUILayout.Button("add", GUILayout.Width(120), GUILayout.Height(20)))
        {
            _excelInst.AddItem();
            GUIUtility.keyboardControl = 0;
        }
        EditorGUILayout.EndHorizontal();
    }
}

你可能感兴趣的:(结合工作需求用GUI写一个快捷操作Excel的界面)