GU源码分析10:InputField

源码10:InputField

InputField就是输入框 提供给用户输入文字内容的 是一个重要的交互手段。比如常见的用来输入用户名 ,密码等等。

    public class InputField
        : Selectable,
        IUpdateSelectedHandler,
        IBeginDragHandler,
        IDragHandler,
        IEndDragHandler,
        IPointerClickHandler,
        ISubmitHandler,
        ICanvasElement,
        ILayoutElement
    {
    	...
    }

上面就是InputField在实现过程中继承的类和接口 具体的接口描述就不再解释了,前面的文章说的比较多。


       protected override void OnEnable()
        {
            base.OnEnable();
            if (m_Text == null)
                m_Text = string.Empty;
            m_DrawStart = 0;
            m_DrawEnd = m_Text.Length;

            // If we have a cached renderer then we had OnDisable called so just restore the material.
            if (m_CachedInputRenderer != null)
                m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture);

            if (m_TextComponent != null)
            {
                m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty);
                m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel);
                m_TextComponent.RegisterDirtyMaterialCallback(UpdateCaretMaterial);
                UpdateLabel();
            }
        }

OnEnable 组件刚激活

1.设置了输入文字绘制开始和结束的位置

2.给文字组件添加了了两个RegisterDirtyVerticesCallback 事件MarkGeometryAsDirty 和UpdateLabel

调用了RegisterDirtyMaterialCallback,添加UpdateCaretMaterial回调

最后调用UpdateLable

        private void MarkGeometryAsDirty()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(gameObject))
                return;
#endif

            CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
        }

MarkGeometryAsDirty 在CanvasUpdateRegistry里把自己注册到图形重建序列。(后面文章分析)

UpdateLable 方法主要就是输入的时候更新显示

    /// </summary>
    protected void UpdateLabel()
    {
        if (m_TextComponent != null && m_TextComponent.font != null && !m_PreventFontCallback)
        {
            m_PreventFontCallback = true;
			
			//获取全部的显示文字字串
            //当有IME组合字符串的时候就需要根据光标位置把组合串接入计算进去 
            //https://docs.unity3d.com/cn/2018.4/ScriptReference/Input-compositionString.html
            string fullText;
            if (EventSystem.current != null && gameObject == EventSystem.current.currentSelectedGameObject && compositionString.Length > 0)
                fullText = text.Substring(0, m_CaretPosition) + compositionString + text.Substring(m_CaretPosition);
            else
                fullText = text;

			//密码类型的输入 就用*代替输入
            string processed;
            if (inputType == InputType.Password)
                processed = new string(asteriskChar, fullText.Length);
            else
                processed = fullText;
			
			//无输入就显示Placeholder
            bool isEmpty = string.IsNullOrEmpty(fullText);
            if (m_Placeholder != null)
                m_Placeholder.enabled = isEmpty;

            // If not currently editing the text, set the visible range to the whole text.
            // The UpdateLabel method will then truncate it to the part that fits inside the Text area.
            // We can't do this when text is being edited since it would discard the current scroll,
            // which is defined by means of the m_DrawStart and m_DrawEnd indices.
            if (!m_AllowInput)
            {
                m_DrawStart = 0;
                m_DrawEnd = m_Text.Length;
            }

            if (!isEmpty)
            {
            	//获取文本生成设置(Text组件功能)
                // Determine what will actually fit into the given line
                Vector2 extents = m_TextComponent.rectTransform.rect.size;
                var settings = m_TextComponent.GetGenerationSettings(extents);
                settings.generateOutOfBounds = true;
				//生成Mesh的顶点数据和文本字符串数据,
                cachedInputTextGenerator.PopulateWithErrors(processed, settings, gameObject);
				//根据TextGenerator和caretPos(光标位置)来计算m_DrawStart和m_DrawEnd,并根据这两个值取字符串的子字符串,设置光标可见(使用协程闪烁)。
                SetDrawRangeToContainCaretPosition(caretSelectPositionInternal);
				//获取实际上能绘制显示的字符串
                processed = processed.Substring(m_DrawStart, Mathf.Min(m_DrawEnd, processed.Length) - m_DrawStart);
				//设置光标可见(使用协程闪烁)。
                SetCaretVisible();
            }
            //加工过的字符串给到Text组件  注册到图形重建列表中
            m_TextComponent.text = processed;
            MarkGeometryAsDirty();
            m_PreventFontCallback = false;
        }
    }

  private void UpdateCaretMaterial()
        {
            if (m_TextComponent != null && m_CachedInputRenderer != null)
                m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture);
        }
  • 调用m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture);创建一个默认的材质
  • 调用m_CachedInputRenderer.SetMaterial把创建的材质赋值给光标,更新光标的材质

  public void DeactivateInputField()
    {
        // Not activated do nothing.
        if (!m_AllowInput)
            return;

        m_HasDoneFocusTransition = false;
        m_AllowInput = false;

        if (m_Placeholder != null)
            m_Placeholder.enabled = string.IsNullOrEmpty(m_Text);

        if (m_TextComponent != null && IsInteractable())
        {
            if (m_WasCanceled)
                text = m_OriginalText;

            SendOnSubmit();

            if (m_Keyboard != null)
            {
                m_Keyboard.active = false;
                m_Keyboard = null;
            }

            m_CaretPosition = m_CaretSelectPosition = 0;
            if (input != null)
                input.imeCompositionMode = IMECompositionMode.Auto;
        }

        MarkGeometryAsDirty();
    }

停用Input 事件 通常在输入结束 或者失去Focus disable等情况调用

  • 设置m_Keyboard.active为false,
  • 设置光标位置为0,
  • 如果IsInteractable为true,调用SendOnSubmit,发送onEndEdit事件, onEndEdit.Invoke(m_Text),
  • 最后调用MarkGeometryAsDirty等待重建。

  public void ActivateInputField()
        {
            if (m_TextComponent == null || m_TextComponent.font == null || !IsActive() || !IsInteractable())
                return;

            if (isFocused)
            {
                if (m_Keyboard != null && !m_Keyboard.active)
                {
                    m_Keyboard.active = true;
                    m_Keyboard.text = m_Text;
                }
            }

            m_ShouldActivateNextUpdate = true;
        }

激活输入 ,通常是在Click的时候 选中状态下激活键盘进行输入


   public override void OnSelect(BaseEventData eventData)
        {
            base.OnSelect(eventData);

            if (shouldActivateOnSelect)
                ActivateInputField();
        }

重写Selectable 中的OnSelect 选中 激活键盘输入

    public virtual void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left)
            return;

        ActivateInputField();
    }

和上面一样当点击的时候激活键盘输入

 public override void OnDeselect(BaseEventData eventData)
    {
        DeactivateInputField();
        base.OnDeselect(eventData);
    }

取消选中停用输入


    public virtual void OnUpdateSelected(BaseEventData eventData)
    {
        if (!isFocused)
            return;

        bool consumedEvent = false;
        while (Event.PopEvent(m_ProcessingEvent))
        {
            if (m_ProcessingEvent.rawType == EventType.KeyDown)
            {
                consumedEvent = true;
                var shouldContinue = KeyPressed(m_ProcessingEvent);
                if (shouldContinue == EditState.Finish)
                {
                    DeactivateInputField();
                    break;
                }
            }

            switch (m_ProcessingEvent.type)
            {
                case EventType.ValidateCommand:
                case EventType.ExecuteCommand:
                    switch (m_ProcessingEvent.commandName)
                    {
                        case "SelectAll":
                            SelectAll();
                            consumedEvent = true;
                            break;
                    }
                    break;
            }
        }

        if (consumedEvent)
            UpdateLabel();

        eventData.Use();
    }

实现了IUpdateSelectedHandler 的方法 是由StandaloneInputModule 每帧检测是否有选中对象 进行事件发送

这里主要是处理一系列键盘输入的操作,比如退格键,复制粘贴(Ctrl+ c Ctrl+v)。


    private void OnFillVBO(Mesh vbo)
    {
        using (var helper = new VertexHelper())
        {
            if (!isFocused)
            {
                helper.FillMesh(vbo);
                return;
            }

            Vector2 roundingOffset = m_TextComponent.PixelAdjustPoint(Vector2.zero);
            if (!hasSelection)
                GenerateCaret(helper, roundingOffset);
            else
                GenerateHighlight(helper, roundingOffset);

            helper.FillMesh(vbo);
        }
    }

如果没有选择区域,调用GenerateCaret生成光标的Mesh,否则,调用GenerateHightlight生成选中区域的Mesh。然后调用VertexHelper.FillMesh,填充Mesh。


总结

InpuFiled源码涉及到很多显示 键盘操作相关的东西 ,看起来确实比较凌乱 。但是包含了UGUI的很多特性功能,值得仔细揣摩

参考文章:https://zhuanlan.zhihu.com/p/340600190

后面要开始分析比较重头的内容 Graphic UGUI的的显示核心

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值