Unity图文混排EmojiText的使用方式和注意事项

                ​​​​​​​  效果演示:

使用方式:

1、导入表情

2、设置图片格式

3、生成表情图集

4、创建/修改目标材质球

5、测试

修复换行问题

修复前:

修复后:

修复代码:

组件扩展

1、右键扩展

2、组件归类:

注意事项

文章引用:


EmojiText组件代码来源工程地址:https://github.com/zouchunyi/EmojiText

效果演示:

使用方式:

1、导入表情

将表情图片素材(png格式)导入到Unity工程中的这个目录中:Assets/Emoji/Input,目录可以按需更换。

注意表情图片的尺寸必须一致,命名规范:纯字母.png或 纯字母_数字.png,例:a.png, b_0.png,b_1.png。

同一个表情的序列帧图片,以_数字结尾。

2、设置图片格式

设置图片格式为Default,设置Non-Power of 2(2的n次方)为ToNearest,勾选Read/Write Enabled。最后点击Apply按钮。

3、生成表情图集

点击菜单EmojiText/Build Emoji后,会按照EmojiBuilder脚本中的默认值进行创建图集保存数据,为了方便操作在这里扩展成一个UnityEditor窗口。

/*
    Description:Create the Atlas of emojis and its data texture.
    How to use?
    1)
        Put all emojies in Asset/Framework/Resource/Emoji/Input.
        Multi-frame emoji name format : Name_Index.png , Single frame emoji format: Name.png
    2)
        Excute EmojiText->Build Emoji from menu in Unity.
    3)
        It will outputs two textures and a txt in Emoji/Output.
        Drag emoji_tex to "Emoji Texture" and emoji_data to "Emoji Data" in UGUIEmoji material.
    4)
        Repair the value of "Emoji count of every line" base on emoji_tex.png.
    5)
        It will auto copys emoji.txt to Resources, and you can overwrite relevant functions base on your project.
    
    Author:zouchunyi
    E-mail:zouchunyi@kingsoft.com
*/
using System;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
public class EmojiBuilder : EditorWindow  {
    private static string OutputPath = "Assets/Emoji/Output/";
    private static string InputPath = "Assets/Emoji/Input/";
    private const string CopyTargetPath = "Assets/Resources/emoji.txt";
    private static readonly Vector2[] AtlasSize = new Vector2[]{
        new Vector2(32,32),
        new Vector2(64,64),
        new Vector2(128,128),
        new Vector2(256,256),
        new Vector2(512,512),
        new Vector2(1024,1024),
        new Vector2(2048,2048)
    };
    struct EmojiInfo
    {
        public string key;
        public string x;
        public string y;
        public string size;
    }
    private static int EmojiSize = 32;//the size of emoji.
    [MenuItem("EmojiText/Build Emoji Wnd")]
    public static void BuildEmojiWnd()
    {
        GetWindow<EmojiBuilder>();
    }
    private void OnGUI()
    {
        
        InputPath = EditorGUILayout.TextField("表情散图存放路径", InputPath);
        OutputPath = EditorGUILayout.TextField("表情图集生成路径", OutputPath);
        EditorGUILayout.HelpBox("注意:每个表情图片尺寸需要统一。",MessageType.Warning);
        EmojiSize = EditorGUILayout.IntField("单个表情图尺寸", EmojiSize);
        if (GUILayout.Button("生成表情图集")) {
            BuildEmoji (); 
        }
    }
    // [MenuItem("EmojiText/Build Emoji")]
    public static void BuildEmoji()
    {
        // List<char> keylist = new List<char> ();
        // for(int i = 0; i<100; i++)
        // {
        //  keylist.Add(i.ToString());
        // }
        // for (int i = 48; i <= 57; i++) {
        //  keylist.Add (System.Convert.ToChar(i));//0-9
        // }
        // for (int i = 65; i <= 90; i++) {
        //  keylist.Add (System.Convert.ToChar(i));//A-Z
        // }
        // for (int i = 97; i <= 122; i++) {
        //  keylist.Add (System.Convert.ToChar(i));//a-z
        // }
        //search all emojis and compute they frames.
        Dictionary<string,int> sourceDic = new Dictionary<string,int> ();
        string[] files = Directory.GetFiles (Application.dataPath.Replace("Assets", "") + InputPath,"*.png");
        for (int i = 0; i < files.Length; i++) {
            string[] strs = files [i].Split ('/');
            string[] strs2 = strs [strs.Length - 1].Split ('.');
            string filename = strs2 [0];
            string[] t = filename.Split('_');
            string id = t [0];
            if (sourceDic.ContainsKey(id)) {
                sourceDic[id]++;
            } else {
                sourceDic.Add (id, 1);
            }
        }
            
        //create the directory if it is not exist.
        if (!Directory.Exists (OutputPath)) {
            Directory.CreateDirectory (OutputPath);
        }   
        Dictionary<string,EmojiInfo> emojiDic = new Dictionary<string, EmojiInfo> ();
        int totalFrames = 0;
        foreach (int value in sourceDic.Values) {
            totalFrames += value;
        }
        Vector2 texSize = ComputeAtlasSize (totalFrames);
        Texture2D newTex = new Texture2D ((int)texSize.x, (int)texSize.y, TextureFormat.ARGB32, false);
        Texture2D dataTex = new Texture2D ((int)texSize.x / EmojiSize, (int)texSize.y / EmojiSize, TextureFormat.ARGB32, false);
        int x = 0;
        int y = 0;
        int keyindex = 0;
        foreach (string key in sourceDic.Keys) {
            for (int index = 0; index < sourceDic[key]; index++) {
                
                string path = InputPath + key;
                if (sourceDic[key] == 1) {
                    path += ".png";
                } else {
                    path += "_" + (index + 1).ToString() + ".png";
                }
                Texture2D asset = AssetDatabase.LoadAssetAtPath<Texture2D> (path);
                Color[] colors = asset.GetPixels (0); 
                for (int i = 0; i < EmojiSize; i++) {
                    for (int j = 0; j < EmojiSize; j++) {
                        newTex.SetPixel (x + i, y + j, colors [i + j * EmojiSize]);
                    }
                }
                string t = System.Convert.ToString (sourceDic [key] - 1, 2);
                float r = 0, g = 0, b = 0;
                if (t.Length >= 3) {
                    r = t [2] == '1' ? 0.5f : 0;
                    g = t [1] == '1' ? 0.5f : 0;
                    b = t [0] == '1' ? 0.5f : 0;
                } else if (t.Length >= 2) {
                    r = t [1] == '1' ? 0.5f : 0;
                    g = t [0] == '1' ? 0.5f : 0;
                } else {
                    r = t [0] == '1' ? 0.5f : 0;
                }
                dataTex.SetPixel (x / EmojiSize, y / EmojiSize, new Color (r, g, b, 1));
                if (! emojiDic.ContainsKey (key)) {
                    EmojiInfo info;
                    // if (keyindex < keylist.Count)
                    // {
                    //  info.key = "[" + char.ToString(keylist[keyindex]) + "]";
                    // }else
                    // {
                    //  info.key = "[" + char.ToString(keylist[keyindex / keylist.Count]) + char.ToString(keylist[keyindex % keylist.Count]) + "]";
                    // }
                    info.key = "[" + keyindex + "]";
                    info.x = (x * 1.0f / texSize.x).ToString();
                    info.y = (y * 1.0f / texSize.y).ToString();
                    info.size = (EmojiSize * 1.0f / texSize.x).ToString ();
                    emojiDic.Add (key, info);
                    keyindex ++;
                }
                x += EmojiSize;
                if (x >= texSize.x) {
                    x = 0;
                    y += EmojiSize;
                }
            }
        }
        byte[] bytes1 = newTex.EncodeToPNG ();
        string outputfile1 = OutputPath + "emoji_tex.png";
        File.WriteAllBytes (outputfile1, bytes1);
        byte[] bytes2 = dataTex.EncodeToPNG ();
        string outputfile2 = OutputPath + "emoji_data.png";
        File.WriteAllBytes (outputfile2, bytes2);
        using (StreamWriter sw = new StreamWriter (OutputPath + "emoji.txt",false)) {
            sw.WriteLine ("Name\tKey\tFrames\tX\tY\tSize");
            foreach (string key in emojiDic.Keys) {
                sw.WriteLine ("{" + key + "}\t" + emojiDic[key].key + "\t" + sourceDic[key] + "\t" + emojiDic[key].x + "\t" + emojiDic[key].y + "\t" + emojiDic[key].size);
            }
            sw.Close ();
        }
        File.Copy (OutputPath + "emoji.txt",CopyTargetPath,true);
        AssetDatabase.Refresh ();
        FormatTexture ();
        EditorUtility.DisplayDialog ("生成成功", "生成表情图集成功!", "确定");
    }
    private static Vector2 ComputeAtlasSize(int count)
    {
        long total = count * EmojiSize * EmojiSize;
        for (int i = 0; i < AtlasSize.Length; i++) {
            if (total <= AtlasSize [i].x * AtlasSize [i].y) {
                return AtlasSize [i];
            }
        }
        return Vector2.zero;
    }
    private static void FormatTexture() {
        TextureImporter emojiTex = AssetImporter.GetAtPath (OutputPath + "emoji_tex.png") as TextureImporter;
        emojiTex.filterMode = FilterMode.Point;
        emojiTex.mipmapEnabled = false;
        emojiTex.sRGBTexture = true;
        emojiTex.alphaSource = TextureImporterAlphaSource.FromInput;
        emojiTex.textureCompression = TextureImporterCompression.Uncompressed;
        emojiTex.SaveAndReimport ();
        TextureImporter emojiData = AssetImporter.GetAtPath (OutputPath + "emoji_data.png") as TextureImporter;
        emojiData.filterMode = FilterMode.Point;
        emojiData.mipmapEnabled = false;
        emojiData.sRGBTexture = false;
        emojiData.alphaSource = TextureImporterAlphaSource.None;
        emojiData.textureCompression = TextureImporterCompression.Uncompressed;
        emojiData.SaveAndReimport ();
    }
}

生成成功后可以在“表情图集生成路径”中看到有三个文件。

其中emoji文本文件记录了,当前生成的图集中每个表情的数据信息。

该文件会在生成的时候拷贝到Resources目录,该地址可以通过脚本中CopyTargetPath属性值进行指定。

4、创建/修改目标材质球

原工程默认会自带一个材质球“UGUIEmoji”,目标位于材质球“Material”文件夹中,如果灭有可以手动创建。右键Shader文件夹中的“UI-EmojiFont”文件可以直接创建目标材质球。也可以创建出来材质球后手动指定材质球的Shader。

将生成好的emoji_data和emoji_tex分别拖放到材质球对应的属性中。

因为生成的图集“emoji_tex”的每一行是4个表情,所以设置Emoji count of every line为4,FrameSpeed是每秒播放序列帧数量,可根据实际情况调整。

5、测试

创建一个空对象,挂载“EmojiText”脚本组件,在输入文本内容“[0]你好[1]”,给组件添加改好的材质球,即可看到效果。

修复换行问题

修复前:

修复后:

问题修复需要改动“EmojiText”脚本。修复工程源码来源:https://github.com/ry02/EmojiText

修复代码:
// Textは自動改行が入ると、改行コードの位置にもvertsの中に頂点情報が追加されるが、
// 自動改行が入らないと、改行コードのための頂点情報は無いので、Indexを調整する
if (emojiDic.Count > 0)
{
    MatchCollection newLines = Regex.Matches(emojiText, "\\n");
    // TextのRect範囲外は行(lineCount)にならないので、全文字が表示されている(characterCount)かも確認する。
    if (cachedTextGenerator.lineCount == newLines.Count + 1 && emojiText.Length < cachedTextGenerator.characterCount)
    {
        // 絵文字があり、自動改行が入っていないので、indexを改行コードの数だけ調整する
        Dictionary<int, EmojiInfo> emojiDicReplace = new Dictionary<int, EmojiInfo>();
        foreach (var ed in emojiDic)
        {
            int index = ed.Key;
            int offset = 0;
            foreach (Match nl in newLines)
            {
                if (nl.Index < index)
                {
                    offset -= 1;
                }
            }
            emojiDicReplace.Add(index + offset, ed.Value);
        }
        emojiDic = emojiDicReplace;
    }
}

修复后的EmojiText源代码:

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class EmojiText : Text
{
    private const float ICON_SCALE_OF_DOUBLE_SYMBOLE = 0.7f;
    public override float preferredWidth =>
        cachedTextGeneratorForLayout.GetPreferredWidth(emojiText, GetGenerationSettings(rectTransform.rect.size)) /
        pixelsPerUnit;
    public override float preferredHeight =>
        cachedTextGeneratorForLayout.GetPreferredHeight(emojiText, GetGenerationSettings(rectTransform.rect.size)) /
        pixelsPerUnit;
    private string emojiText => Regex.Replace(text, "\\[[a-z0-9A-Z]+\\]", "%%");
    private static Dictionary<string, EmojiInfo> m_EmojiIndexDict = null;
    struct EmojiInfo
    {
        public float x;
        public float y;
        public float size;
    }
    readonly UIVertex[] m_TempVerts = new UIVertex[4];
    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (font == null)
        {
            return;
        }
        if (m_EmojiIndexDict == null)
        {
            m_EmojiIndexDict = new Dictionary<string, EmojiInfo>();
            //load emoji data, and you can overwrite this segment code base on your project.
            TextAsset emojiContent = Resources.Load<TextAsset>("emoji");
            string[] lines = emojiContent.text.Split('\n');
            for (int i = 1; i < lines.Length; i++)
            {
                if (!string.IsNullOrEmpty(lines[i]))
                {
                    string[] strs = lines[i].Split('\t');
                    EmojiInfo info;
                    info.x = float.Parse(strs[3]);
                    info.y = float.Parse(strs[4]);
                    info.size = float.Parse(strs[5]);
                    m_EmojiIndexDict.Add(strs[1], info);
                }
            }
        }
        Dictionary<int, EmojiInfo> emojiDic = new Dictionary<int, EmojiInfo>();
        if (supportRichText)
        {
            int nParcedCount = 0;
            //[1] [123] 替换成#的下标偏移量          
            int nOffset = 0;
            MatchCollection matches = Regex.Matches(text, "\\[[a-z0-9A-Z]+\\]");
            for (int i = 0; i < matches.Count; i++)
            {
                EmojiInfo info;
                if (m_EmojiIndexDict.TryGetValue(matches[i].Value, out info))
                {
                    emojiDic.Add(matches[i].Index - nOffset + nParcedCount, info);
                    nOffset += matches[i].Length - 1;
                    nParcedCount++;
                }
            }
        }
        // We don't care if we the font Texture changes while we are doing our Update.
        // The end result of cachedTextGenerator will be valid for this instance.
        // Otherwise we can get issues like Case 619238.
        m_DisableFontTextureRebuiltCallback = true;
        Vector2 extents = rectTransform.rect.size;
        var settings = GetGenerationSettings(extents);
        cachedTextGenerator.Populate(emojiText, settings);
        Rect inputRect = rectTransform.rect;
        // get the text alignment anchor point for the text in local space
        Vector2 textAnchorPivot = GetTextAnchorPivot(alignment);
        Vector2 refPoint = Vector2.zero;
        refPoint.x = Mathf.Lerp(inputRect.xMin, inputRect.xMax, textAnchorPivot.x);
        refPoint.y = Mathf.Lerp(inputRect.yMin, inputRect.yMax, textAnchorPivot.y);
        // Apply the offset to the vertices
        IList<UIVertex> verts = cachedTextGenerator.verts;
        float unitsPerPixel = 1 / pixelsPerUnit;
        int vertCount = verts.Count;
        // We have no verts to process just return (case 1037923)
        if (vertCount <= 0)
        {
            toFill.Clear();
            return;
        }
        
        // Textは自動改行が入ると、改行コードの位置にもvertsの中に頂点情報が追加されるが、
        // 自動改行が入らないと、改行コードのための頂点情報は無いので、Indexを調整する
        if (emojiDic.Count > 0)
        {
            MatchCollection newLines = Regex.Matches(emojiText, "\\n");
            // TextのRect範囲外は行(lineCount)にならないので、全文字が表示されている(characterCount)かも確認する。
            if (cachedTextGenerator.lineCount == newLines.Count + 1 && emojiText.Length < cachedTextGenerator.characterCount)
            {
                // 絵文字があり、自動改行が入っていないので、indexを改行コードの数だけ調整する
                Dictionary<int, EmojiInfo> emojiDicReplace = new Dictionary<int, EmojiInfo>();
                foreach (var ed in emojiDic)
                {
                    int index = ed.Key;
                    int offset = 0;
                    foreach (Match nl in newLines)
                    {
                        if (nl.Index < index)
                        {
                            offset -= 1;
                        }
                    }
                    emojiDicReplace.Add(index + offset, ed.Value);
                }
                emojiDic = emojiDicReplace;
            }
        }
        
        Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
        roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
        toFill.Clear();
        if (roundingOffset != Vector2.zero)
        {
            for (int i = 0; i < vertCount; ++i)
            {
                int tempVertsIndex = i & 3;
                m_TempVerts[tempVertsIndex] = verts[i];
                m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
                m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
                m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
                if (tempVertsIndex == 3)
                {
                    toFill.AddUIVertexQuad(m_TempVerts);
                }
            }
        }
        else
        {
            for (int i = 0; i < vertCount; ++i)
            {
                EmojiInfo info;
                int index = i / 4;
                if (emojiDic.TryGetValue(index, out info))
                {
                    //compute the distance of '[' and get the distance of emoji 
                    //计算2个%%的距离
                    float emojiSize = 2 * (verts[i + 1].position.x - verts[i].position.x) *
                                      ICON_SCALE_OF_DOUBLE_SYMBOLE;
                    float fCharHeight = verts[i + 1].position.y - verts[i + 2].position.y;
                    float fCharWidth = verts[i + 1].position.x - verts[i].position.x;
                    float fHeightOffsetHalf = (emojiSize - fCharHeight) * 0.5f;
                    float fStartOffset = emojiSize * (1 - ICON_SCALE_OF_DOUBLE_SYMBOLE);
                    m_TempVerts[3] = verts[i]; //1
                    m_TempVerts[2] = verts[i + 1]; //2
                    m_TempVerts[1] = verts[i + 2]; //3
                    m_TempVerts[0] = verts[i + 3]; //4
                    m_TempVerts[0].position += new Vector3(fStartOffset, -fHeightOffsetHalf, 0);
                    m_TempVerts[1].position +=
                        new Vector3(fStartOffset - fCharWidth + emojiSize, -fHeightOffsetHalf, 0);
                    m_TempVerts[2].position += new Vector3(fStartOffset - fCharWidth + emojiSize, fHeightOffsetHalf, 0);
                    m_TempVerts[3].position += new Vector3(fStartOffset, fHeightOffsetHalf, 0);
                    m_TempVerts[0].position *= unitsPerPixel;
                    m_TempVerts[1].position *= unitsPerPixel;
                    m_TempVerts[2].position *= unitsPerPixel;
                    m_TempVerts[3].position *= unitsPerPixel;
                    float pixelOffset = emojiDic[index].size / 32 / 2;
                    m_TempVerts[0].uv1 = new Vector2(emojiDic[index].x + pixelOffset, emojiDic[index].y + pixelOffset);
                    m_TempVerts[1].uv1 = new Vector2(emojiDic[index].x - pixelOffset + emojiDic[index].size,
                        emojiDic[index].y + pixelOffset);
                    m_TempVerts[2].uv1 = new Vector2(emojiDic[index].x - pixelOffset + emojiDic[index].size,
                        emojiDic[index].y - pixelOffset + emojiDic[index].size);
                    m_TempVerts[3].uv1 = new Vector2(emojiDic[index].x + pixelOffset,
                        emojiDic[index].y - pixelOffset + emojiDic[index].size);
                    toFill.AddUIVertexQuad(m_TempVerts);
                    i += 4 * 2 - 1;
                }
                else
                {
                    int tempVertsIndex = i & 3;
                    m_TempVerts[tempVertsIndex] = verts[i];
                    m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
                    if (tempVertsIndex == 3)
                    {
                        toFill.AddUIVertexQuad(m_TempVerts);
                    }
                }
            }
        }
        m_DisableFontTextureRebuiltCallback = false;
    }
}

组件扩展

1、右键扩展

在使用中为了方便的创建对象,如同创建Text时的右键菜单,这时候我们可以扩展一下脚本。

新建一个脚本 “EmojiMenu”,添加如下代码:

private static Transform FindParent()
{
    // 获取当前选择的对象,并检索是否符合条件
    var transform = Selection.activeTransform;
    if (transform == null)
    {
        var canvas = FindObjectOfType<Canvas>();
        if (canvas)
        {
            return canvas.transform;
        }
    }
    else if (transform.GetComponentInParent<Canvas>())
    {
        return transform;
    }
    // 创建一个Canvas对象
    var gameObject = new GameObject("UICanvas");
    if (transform != null)
    {
        gameObject.transform.SetParent(transform);
    }
    gameObject.AddComponent<Canvas>();
    gameObject.AddComponent<CanvasScaler>();
    gameObject.AddComponent<GraphicRaycaster>();
    return gameObject.transform;
}
[MenuItem("GameObject/UI/Emoji Text")]
public static void AddEmojiText(MenuCommand menuCommand)
{
    var child = new GameObject("Emoji Text", typeof(EmojiText));
    RectTransform rectTransform = child.GetComponent<RectTransform>();
    rectTransform.SetParent(FindParent());
    rectTransform.sizeDelta = new Vector2(160, 30);
    rectTransform.localPosition = Vector3.zero;
    rectTransform.localRotation = Quaternion.identity;
    rectTransform.localScale = Vector3.one;
}
2、组件归类:

在“EmojiText”类前面添加即可实现,展开组件菜单的UI项,可以找到当前类型。

[AddComponentMenu("UI/EmojiText", 100)]

注意事项

1、存在换行时或者一条字符串中有多个表情时,添加空格会导致文本错乱!!!

2、在使用EmojiText组件时,父节点中如果存在Canvas,请注意Canvas的Additional Shader Channels 属性是否选择了TexCoord1,如果没有选择请勾选该选项,否则会导致图文混排显示异常。

文章引用:

1、GitHub:zouchunyi/EmojiText

2、GitHub:ry02/EmojiText

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/603193.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LINUX 入门 4

LINUX 入门 4 day6 7 20240429 20240504 耗时&#xff1a;240min 课程链接地址 第4章 LINUX环境编程——实现线程池 C基础 第3节 #define里面的行不能乱空行&#xff0c;要换行就打\ typedef 是 C 和 C 中的一个关键字&#xff0c;用于为已有的数据类型定义一个新的名字。…

新书速览|图神经网络基础、模型与应用实战

掌握PyTorch图神经网络基础与模型&#xff0c;实战自然语言处理、计算机视觉、推荐系统、社交网络应用开发 01 本书内容 图神经网络不仅能够解决传统机器学习方法无法解决的图数据问题&#xff0c;而且能够应用于许多实际场景&#xff0c;例如社交网络、药物发现、网络安全、…

新手向的s2-046漏洞复现

一、前期准备 1.docker容器 作为第一次接触struts2漏洞类型的小白&#xff0c;第一步从搭建环境开始。首先我们需要准备一个服务器或者本地系统&#xff0c;我这里是使用本地的kali&#xff0c;kali里面需要有docker容器&#xff0c;docker容器的安装教程请自行搜索&#xff0c…

女性名字有孤寡数,易离婚

丁老师&#xff1a;您好&#xff01;我孩子&#xff08;女孩&#xff09;准备取名&#xff1a;周小程&#xff0c;宝宝出生于阳历2016年8月13号16时30分左右&#xff0c;准备给孩子取个名字&#xff0c;在网上查询了哈&#xff0c;这个名字的分数还蛮高的&#xff0c;99分&…

韦东山嵌入式Liunx驱动大全二

文章目录 一、LCD1-1 不同接口的LCD硬件操作原理1-2 LCD驱动程序框架1-3 结合APP分析LCD驱动程序框架1-4 LCD硬件时序图1-5 分析内核自带的LCD驱动程序1-6 编程LCD驱动程序框架_使用设备树1-7 LCD驱动程序框架_引脚配置1-8 LCD驱动程序框架_时钟配置1-9 LCD驱动程序框架_LCD控制…

1069 微博转发抽奖

solution #include<iostream> #include<string> #include<map> using namespace std; int main(){int n, m, s, loop 0, have 0;string id;map<string, int> mp;cin >> m >> n >> s;for(int i 1; i < m; i){//编号从1开始cin…

专题五_位运算(3)

目录 137. 只出现一次的数字 II 解析 题解 面试题 17.19. 消失的两个数字 解析 题解 137. 只出现一次的数字 II 137. 只出现一次的数字 II - 力扣&#xff08;LeetCode&#xff09; 解析 注意这里指的是比特位上的01来进行统计的 题解 class Solution { public:int sin…

Unity与C#的关系

第一&#xff0c;我们首先需要知道Unity与C#的关系是什么&#xff1f; 第二&#xff0c;我们要明白为什么Unity会使用C#&#xff0c;而不是C&#xff1f; 第三&#xff0c;我们需要知道Unity是怎么使用C#的&#xff1f; 第一点&#xff1a; 先说结论&#xff1a;C#是Unity用…

windows如何安装python框架

windows如何安装Python框架&#xff1f;以安装Django框架为例&#xff0c;具体如下&#xff1a; 在idea上运行Python项目时&#xff0c;出现了如下错误&#xff0c;这是因为系统中只安装了Python&#xff0c;没有安装Django。 既然报错的原因就是缺少Django&#xff0c;那我们…

深度解析 Spring 源码:揭秘 AbstractAutowireCapableBeanFactory 的 Bean 生命周期处理

文章目录 一、AbstractAutowireCapableBeanFactory 概述1.1 详细分析1.2 设计思想 二、深入解析AbstractAutowireCapableBeanFactory 的实现细节2.1 Bean 实例化过程分析2.1.1 createBean()2.1.2 createBeanInstance() 2.2 Bean 属性注入的实现机制2.2.1 populateBean()2.2.2 a…

560. 和为 K 的子数组 974. 和可被 K 整除的子数组 【前缀和】

题目链接 ​​​​​​​974. 和可被 K 整除的子数组 560. 和为 K 的子数组 今天刷题的时候&#xff0c;刷了这两题&#xff0c;感觉挺有意思的。代码写起来挺简单的&#xff0c;但是思路和其中的细节以及涉及到的知识点确实让我挺意外的。这里写个博客解析一波&#xff0c;也是…

Charles抓包工具

Charles是一个HTTP代理工具&#xff0c;使开发人员能够查看客服端和服务器之间的所有HTTP/ HTTPS/SSL网络请求。 Charles是在PC环境下常用的网络抓包截取工具&#xff0c;在做移动开发时&#xff0c;我们为了调试客户端与服务端的网络通讯协议&#xff0c;常常需要截取网络请求…

MCP3008-I/SL 模数转换器ADC SPI接口 模拟信号采集

MCP3008-I/SL 模数转换器ADC 贴片SOIC16 MCP3008-I/SL 是一款模数转换器&#xff08;ADC&#xff09;&#xff0c;属于 SAR&#xff08;逐次逼近寄存器&#xff09;架构的 ADC。它具有以下特点&#xff1a; 8 通道单 ADC 最大采样率&#xff1a;200ksps&#xff08;千样点每秒…

鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉坑指南

几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 是在 OpenHarmony 的 kernel_liteos_a 基础上给内核源码加上中文注解的版本.与官方源码按月保持同步,同步历史如下: 2021/10/09 – 增加性能优化模块perf,优化了文件映射模块2021/09/14 – common,extended等几个目录结构和M…

文献速递:深度学习医学影像心脏疾病检测与诊断--基于深度学习的低剂量SPECT心肌灌注图像去噪:定量评估与临床表现

Title 题目 Deep learning–based denoising of low‑dose SPECT myocardialperfusion images: quantitative assessment and clinical performance 基于深度学习的低剂量SPECT心肌灌注图像去噪&#xff1a;定量评估与临床表现 01 文献速递介绍 单光子发射计算机断层扫描&a…

uniapp + vue3 设置 axios proxy 代理,并重写路径

uniapp vue2 设置代理如下&#xff1a; 已生成的项目架构里面找到manifest.json文件&#xff0c;通过源码视图的方式打开文件&#xff0c;在文件中添加一下代码即可完成代理&#xff1a; "h5": {"devServer": {"disableHostCheck": true, //禁…

基于StatefulSet控制器在Kubernetes上部署MySQL一主多从

一、前提--StatefuSet特性 1.1 有状态的节点控制器 -- StatefulSet 及其网络状态 容器的解决方案是针对无状态应用场景的最佳实践&#xff0c;但对于有状态应用来说&#xff0c;就并非如此了。Kubernetes 用 StatefulSet 解决了有状态应用编排的问题&#xff0c;本文我们就来…

GitHub介绍,GitHub如何订阅充值?

一、GitHub介绍 GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持git 作为唯一的版本库格式进行托管&#xff0c;故名Github。 GitHub于2008年4月10日正式上线&#xff0c;除了git代码仓库托管及基本的Web管理界面以外&#xff0c;还提供了订阅、讨论组、…

爬取深圳2024年链家二手房数据,共3000条数据(其他城市也可)

文章目录 专栏导读1.目标2.导入相关库3.获取每个二手房的链接4.获取每个链接中的相关数据5.保存数据6.数据展示 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫…

探索数据结构

什么是数据结构 数据结构是由&#xff1a;“数据”与“结构”两部分组成 数据与结构 数据&#xff1a;如我们所看见的广告、图片、视频等&#xff0c;常见的数值&#xff0c;教务系统里的&#xff08;姓名、性别、学号、学历等等&#xff09;&#xff1b; 结构&#xff1a;当…