草庐IT

Unity UI点击事件系统

KamikazePilot 2023-08-23 原文

一、概述

  • 在Unity 的UGUI开发过程中,我们经常需要对UI图片进行操作响应各种处理比如:点击,长点击,长按,拖拽等多种功能的实现,这时原本的Button组件就不够用了
  • 我们需要自己实现一个简单的点击事件系统来响应用户的各种操作,下面可以分析一下思路。
  • 首先你需要提前了解点击事件的各个接口功能,了解C#中的委托使用,然后再学习这个系统

二、如何实现

  • 当我们想要响应长按等根时间挂钩的功能,就必须要使用Update函数的多帧调用来计算时间(FixUpdate应该也行)
  • 所以,我建议将自己实现的功能尽量写在Updata函数中,并通过一个流程来实现按键的状态切换。

1 基础框架

下图中POINTSTATE 枚举用于改变当前按键状态,通过外部修改按键状态,或者自身状态的变更,来实现按键的多种状态监测。(这里不添加拖拽状态,拖拽状态将会另外添加一个拖拽脚本专门用于响应拖拽事件)

public enum POINTSTATE{
    NONE,
    DOWN,
    STAY,
    UP,
    UPSPACE,
    EXIT,
    EXITSPACE,
}
public class UIEvent: MonoBehaviour
{
	void Update()
    {
        if (!gameObject.activeSelf) return;

        switch(state)
        {
            case POINTSTATE.NONE:   //无状态
                return;
            case POINTSTATE.DOWN:   //按键按下
                return;
            case POINTSTATE.STAY:   //点击停留时
                return;
            case POINTSTATE.UP:     //抬起
                return;
            case POINTSTATE.UPSPACE:                    
                return;
            case POINTSTATE.EXIT:   //退出
                return;
            case POINTSTATE.EXITSPACE:
                return;
        }
    }
}
  • 其次我们需要继承点击事件接口来进行按键响应
  • 每次监测到按下,抬起,结束时,都需要将按键状态进行变更,以便于在Update中进行响应(点击状态这里在Update中更改,所以在重写的OnClick方法中没有进行状态改变)
  • 同时可以将注册的点击事件(后面会写)进行调用,点击监测别直接写这里,后面需要的话可以进行动态添加,所以这里要进行判空操作
  • 注意点击事件只能被有图片的节点响应,所以添加一个特性使其不被无图片的节点继承
[RequireComponent(typeof(Image))]   //有图片组件才能被继承
public class UIEvent: MonoBehaviour , IPointerDownHandler, IPointerClickHandler, IPointerExitHandler, IPointerUpHandler
{
	//点击回调
    Action OnClick = null;
    //按下回调
    Action OnDown = null;
    //抬起回调
    Action OnUp = null;
    //结束回调
    Action OnExit = null;
    //当前点击状态
    POINTSTATE state = POINTSTATE.NONE;
	public void OnPointerClick(PointerEventData eventData)
    {
        if (OnClick != null) OnClick();
    }
    public void OnPointerDown(PointerEventData eventData)
    {
        state = POINTSTATE.DOWN;
        if (OnDown != null) OnDown();
    }
    public void OnPointerUp(PointerEventData eventData)
    {
        state = POINTSTATE.UP;
        if (OnDown != null) OnUp();
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        state = POINTSTATE.EXIT;
        if (OnDown != null) OnExit();
    }
}

2 功能结构

  • 之后便可以进行外部接口的设计,这里添加以下几个
	//点击回调
    Action OnClick = null;
    //按下回调
    Action OnDown = null;
    //抬起回调
    Action OnUp = null;
    //结束回调
    Action OnExit = null;
	//长按回调 单次相应
    Action OnLongClick = null;
    //按下回调 多次相应,且相应速度加快
    Action OnPress = null;
	// 设置点击间隔(防止短时间大量连点操作)
    public static void SetClickSpace(int space)
    // 注册一般点击
    public static void AddClick(GameObject _go, Action _func)
    // 注册按键按下
    public static void AddDown(GameObject _go, Action _func)
    // 注册按键抬起
    public static void AddUp(GameObject _go, Action _func)
    // 注册按键退出
    public static void AddExit(GameObject _go, Action _func)
    // 注册长点击
    public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
    // 注册长按
    public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
  • 到这里整个结构就比较清晰了,首先通过调用UIEvent的静态接口,给需要的节点添加事件,并在按键按下时,在Update函数中调用已经添加的事件。

2 具体实现

  • 首先实现一个可能被多次调用的接口, 对传入的对象更改RayCastTarget,并返回UIEvent脚本,没有挂载就添加一个。

  • 这里为了方便起见直接写在UIEvent中,如果想结构更加清晰一点可以分开写

    //检查图像并挂载脚本
    static UIEvent Get(GameObject _go)
    {
        Graphic graphic = _go.GetComponent<Graphic>();
        if (graphic) graphic.raycastTarget = true;

        UIEvent uiEvent = _go.GetComponent<UIEvent>();
        if (uiEvent == null) uiEvent = _go.AddComponent<UIEvent>();
        return uiEvent;
    }
  • 然后说外部接口,具体实现如下:
  • 先挂载脚本,然后将改脚本下得相应点击事件值进行更改即可。
  • 其中LongClick函数和Press函数可以额外传入长按时间、最短响应间隔时间。
#region 外部注册相关接口
    // 设置点击间隔
    public static void SetClickSpace(int space)
    {
        timeClickSpace = space;
    }
    /// <summary>
    /// 注册点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddClick(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnClick = _func;
    }
    /// <summary>
    /// 注册按下
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddDown(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnDown = _func;
    }
    /// <summary>
    /// 注册抬起
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddUp(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnUp = _func;
    }
    /// <summary>
    /// 注册结束点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddExit(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnClick = _func;
    }
    /// <summary>
    /// 注册长点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    /// <param name="_space">长按时间</param>
    public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent)
        {
            uIEvent.timeLongClickSpace = _space;
            uIEvent.OnLongClick = _func;
        }
    }
    /// <summary>
    /// 注册长按
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    /// <param name="_space">长按时间</param>
    /// <param name="_minSpace">最短响应间隔</param>
    public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent)
        {
            uIEvent.timePressSpace = _space;
            uIEvent.OnPress = _func;
            uIEvent.timePressMinSpace = _minSpace;
        }
    }
  • 最后看一下UpdateLongClickPress事件的执行条件
  1. 首先在上面注册事件
  2. 然后当重写的接口触发 按下 或 抬起 的事件,并将状态进行切换
  3. Update中监测到state值变化,就开始一个新的流程如下
  • 进入POINTSTATE.DOWN,初始化需要用到的值
  • 第二次进入Update函数,此时state已被改为POINTSTATE.STAY
  • 开始判断是否存在LongClickPress回调,有则执行,并且在这个时候,state值不会Update函数内被改变
  • 直到在接口中将值改为POINTSTATE.UP为止
#region 注册相关的属性
    //点击回调
    Action OnClick = null;
    //按下回调
    Action OnDown = null;
    //抬起回调
    Action OnUp = null;
    //结束回调
    Action OnExit = null;
    //长按回调 单次相应
    Action OnLongClick = null;
    //按下回调 多次相应,且相应速度加快
    Action OnPress = null;

    //单词击计数器
    float timeClick = 0;
    //一秒内点击限制
    static int timeClickSpace = 10;

    //长点击计数器
    float timeLongClick = 0;
    //长点击生效时长
    float timeLongClickSpace = 1.0f;

    //长按计数器
    float timePress = 0;
    //长按递减间隔
    float timePressSpace = 1.0f;
    //长按递减间隔缓存
    float timePressSpaceCache = 0;
    //长按最低间隔
    float timePressMinSpace = 0.1f;
    //当前点击状态
    POINTSTATE state = POINTSTATE.NONE;

    #endregion
    //事件响应流程写在Update中
    void Update()
    {
        if (!gameObject.activeSelf) return;

        switch(state)
        {
            case POINTSTATE.NONE:   //无状态
                return;
            case POINTSTATE.DOWN:   //
                timeLongClick = Time.time;              //记录长点击开始时间

                // 如果注册了OnPress事件则直接执行
                if (OnPress != null && Time.time - timePress > (1 / timeClickSpace)) OnPress();
                timePress = Time.time;                  //记录长按开始时间
                timePressSpaceCache = timePressSpace;   // 长按事件响应间隔
                state = POINTSTATE.STAY;
                return;
            case POINTSTATE.STAY:                       //点击停留时
                if(OnLongClick != null) //长点击
                {
                    if(Time.time - timeLongClick > timeLongClickSpace)  //到时间了开始执行
                    {
                        OnLongClick();
                        state = POINTSTATE.NONE;    //进入结束状态
                    }
                }
                if(OnPress != null)     //长按
                {
                    float spaceTime = Time.time - timePress;    //距离上一次间隔时间
                    if(spaceTime > timePressSpaceCache && spaceTime > (1 / timeClickSpace))
                    {
                        //记录相应时间点
                        timePress = Time.time;
                        //缩短相应间隔
                        timePressSpaceCache *= (2 / 3f);
                        timePressSpaceCache = Mathf.Max(timePressSpaceCache, timePressMinSpace);
                        //执行回调
                        OnPress();
                    }
                }
                return;
            case POINTSTATE.UP:                         //抬起
                state = POINTSTATE.UPSPACE;
                return;
            case POINTSTATE.UPSPACE:                    
                return;
            case POINTSTATE.EXIT:                       //退出
                state = POINTSTATE.EXITSPACE;
                return;
            case POINTSTATE.EXITSPACE:
                return;
        }
    }

关于窗口拖拽事件,这里没有选择实现在UIEvent上,只是使用了UIEvent 进行事件注册,过程与之前的方式类似。两个函数分别是

    static UIEventDrag GetDrag(GameObject _go)
    {
        UIEventDrag uIEventDrag = _go.GetComponent<UIEventDrag>();
        if (!uIEventDrag) uIEventDrag = _go.AddComponent<UIEventDrag>();
        return uIEventDrag;
    }
    /// <summary>
    /// 注册拖拽
    /// </summary>
    /// <param name="_go">被移动对象</param>
    /// <param name="_window">需要点击的对象</param>
    public static void AddDrag(GameObject _go, Transform _window)
    {
        UIEventDrag uIEvent = GetDrag(_go);
        uIEvent.IsWindowDrag = true;
        uIEvent.Window = _window;
    }
  • UIEventDrag是另一个挂了Nomo的脚本,代码贴在最后面了,它的实现可以相对独立;这里的实现针对性较强,大家用的时候可以根据需要进行自定义

新增双击

	//双击回调
    Action OnDoubleClick = null;
    //双击最大间隔时间
    private float timeDoubleClick = 0.5f;
    //双击计时
    private float timeDoubleClickCount = 0f;

	void Update()
	{
		switch (pointState)
        {
        	case POINTERSTATE.UP:
                //Log.Print("抬起");                
                //双击
                if (OnDoubleClick != null && Time.time - timeDoubleClickCount <= timeDoubleClick) OnDoubleClick();
                timeDoubleClickCount = Time.time;
                break;
        }
	}
	//注册双击
    public static void AddDoubleClick(GameObject _go, Action _fun, float _space = 0.7f)
    {
        var uievent = Get(_go);
        if (uievent)
        {
            uievent.timeDoubleClick = _space;
            uievent.OnDoubleClick = _fun;
        }
    }

三、完整代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public enum POINTSTATE{
    NONE,
    DOWN,
    STAY,
    UP,
    UPSPACE,
    EXIT,
    EXITSPACE,
}

[RequireComponent(typeof(Image))]   //有图片组件才能被继承
public class UIEvent: MonoBehaviour , IPointerDownHandler, IPointerClickHandler, IPointerExitHandler, IPointerUpHandler
{
    #region 注册相关的属性
    //点击回调
    Action OnClick = null;
    //按下回调
    Action OnDown = null;
    //抬起回调
    Action OnUp = null;
    //结束回调
    Action OnExit = null;
    //长按回调 单次相应
    Action OnLongClick = null;
    //按下回调 多次相应,且相应速度加快
    Action OnPress = null;
	//双击回调
    Action OnDoubleClick = null;
    
    //单词击计数器
    float timeClick = 0;
    //一秒内点击限制
    static int timeClickSpace = 10;

	//双击最大间隔时间
    private float timeDoubleClick = 0.5f;
    //双击计时
    private float timeDoubleClickCount = 0f;

    //长点击计数器
    float timeLongClick = 0;
    //长点击生效时长
    float timeLongClickSpace = 1.0f;

    //长按计数器
    float timePress = 0;
    //长按递减间隔
    float timePressSpace = 1.0f;
    //长按递减间隔缓存
    float timePressSpaceCache = 0;
    //长按最低间隔
    float timePressMinSpace = 0.1f;
    //当前点击状态
    POINTSTATE state = POINTSTATE.NONE;

    #endregion
    //事件响应流程写在Update中
    void Update()
    {
        if (!gameObject.activeSelf) return;

        switch(state)
        {
            case POINTSTATE.NONE:   //无状态
                return;
            case POINTSTATE.DOWN:   //
                timeLongClick = Time.time;              //记录长点击开始时间

                // 如果注册了OnPress事件则直接执行
                if (OnPress != null && Time.time - timePress > (1 / timeClickSpace)) OnPress();
                timePress = Time.time;                  //记录长按开始时间
                timePressSpaceCache = timePressSpace;   // 长按事件响应间隔
                state = POINTSTATE.STAY;
                return;
            case POINTSTATE.STAY:                       //点击停留时
                if(OnLongClick != null) //长点击
                {
                    if(Time.time - timeLongClick > timeLongClickSpace)  //到时间了开始执行
                    {
                        OnLongClick();
                        state = POINTSTATE.NONE;    //进入结束状态
                    }
                }
                if(OnPress != null)     //长按
                {
                    float spaceTime = Time.time - timePress;    //距离上一次间隔时间
                    if(spaceTime > timePressSpaceCache && spaceTime > (1 / timeClickSpace))
                    {
                        //记录相应时间点
                        timePress = Time.time;
                        //缩短相应间隔
                        timePressSpaceCache *= (2 / 3f);
                        timePressSpaceCache = Mathf.Max(timePressSpaceCache, timePressMinSpace);
                        //执行回调
                        OnPress();
                    }
                }
                return;
            case POINTSTATE.UP:                         //抬起
            	//双击
            	if (OnDoubleClick != null && Time.time - timeDoubleClickCount <= timeDoubleClick) OnDoubleClick();
                timeDoubleClickCount = Time.time;
                
                state = POINTSTATE.UPSPACE;
                return;
            case POINTSTATE.UPSPACE:                    
                return;
            case POINTSTATE.EXIT:                       //退出
                state = POINTSTATE.EXITSPACE;
                return;
            case POINTSTATE.EXITSPACE:
                return;
        }
    }

    //检查图像并挂载脚本
    static UIEvent Get(GameObject _go)
    {
        Graphic graphic = _go.GetComponent<Graphic>();
        if (graphic) graphic.raycastTarget = true;

        UIEvent uiEvent = _go.GetComponent<UIEvent>();
        if (uiEvent == null) uiEvent = _go.AddComponent<UIEvent>();
        return uiEvent;
    }

    static UIEventDrag GetDrag(GameObject _go)
    {
        UIEventDrag uIEventDrag = _go.GetComponent<UIEventDrag>();
        if (!uIEventDrag) uIEventDrag = _go.AddComponent<UIEventDrag>();
        return uIEventDrag;
    }

    #region 外部注册相关接口
    // 设置点击间隔
    public static void SetClickSpace(int space)
    {
        timeClickSpace = space;
    }
    /// <summary>
    /// 注册点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddClick(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnClick = _func;
    }
    /// <summary>
    /// 注册按下
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddDown(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnDown = _func;
    }
    /// <summary>
    /// 注册抬起
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddUp(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnUp = _func;
    }
    /// <summary>
    /// 注册结束点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    public static void AddExit(GameObject _go, Action _func)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent) uIEvent.OnClick = _func;
    }
    /// <summary>
    /// 注册长点击
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    /// <param name="_space">长按时间</param>
    public static void AddLongClick(GameObject _go, Action _func, float _space = 1f)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent)
        {
            uIEvent.timeLongClickSpace = _space;
            uIEvent.OnLongClick = _func;
        }
    }
    /// <summary>
    /// 注册长按
    /// </summary>
    /// <param name="_go">被注册对象</param>
    /// <param name="_func">回调函数</param>
    /// <param name="_space">长按时间</param>
    /// <param name="_minSpace">最短响应间隔</param>
    public static void AddPress(GameObject _go, Action _func, float _space = 1f, float _minSpace = 0.1f)
    {
        UIEvent uIEvent = Get(_go);
        if (uIEvent)
        {
            uIEvent.timePressSpace = _space;
            uIEvent.OnPress = _func;
            uIEvent.timePressMinSpace = _minSpace;
        }
    }
    /// <summary>
    /// 注册拖拽
    /// </summary>
    /// <param name="_go">被移动对象</param>
    /// <param name="_window">需要点击的对象</param>
    public static void AddDrag(GameObject _go, Transform _window)
    {
        UIEventDrag uIEvent = GetDrag(_go);
        uIEvent.IsWindowDrag = true;
        uIEvent.Window = _window;
    }
    //注册双击
    public static void AddDoubleClick(GameObject _go, Action _fun, float _space = 0.7f)
    {
        var uievent = Get(_go);
        if (uievent)
        {
            uievent.timeDoubleClick = _space;
            uievent.OnDoubleClick = _fun;
        }
    }
    #endregion  

    #region 点击重写
    public void OnPointerClick(PointerEventData eventData)
    {
        //检查间隔
        if (Time.time - timeClick < 1.0f / timeClickSpace) { return; }

        if (OnClick != null)
        {
            OnClick();
            timeClick = Time.time;
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        state = POINTSTATE.DOWN;
        if (OnDown != null) OnDown();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        state = POINTSTATE.UP;
        if (OnDown != null) OnUp();
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        state = POINTSTATE.EXIT;
        if (OnDown != null) OnExit();
    }
    #endregion
}

using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.EventSystems;

public class UIEventDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public bool IsWindowDrag = false;
    public Transform Window;
    private Vector2 winDiffPoint;
    private Vector2 winDeltaPoint;

    private float lx;
    private float ly;
    public void OnDrag(PointerEventData _eventData)
    {
        if (IsWindowDrag)
        {
            winDeltaPoint = _eventData.position - winDiffPoint;
            Window.localPosition += new Vector3(winDeltaPoint.x, winDeltaPoint.y, 0);
            if (Window.localPosition.x > lx) Window.localPosition = new Vector3(lx, Window.localPosition.y, 0);
            if (Window.localPosition.x < -lx2) Window.localPosition = new Vector3(-lx2, Window.localPosition.y, 0);
            if (Window.localPosition.y > ly) Window.localPosition = new Vector3(Window.localPosition.x, ly, 0);
            if (Window.localPosition.y < -ly2) Window.localPosition = new Vector3(Window.localPosition.x, -ly2, 0);
            winDiffPoint = _eventData.position;
        }
    }

    public void OnBeginDrag(PointerEventData _eventData)
    {

        if (IsWindowDrag)
        {
            winDiffPoint = _eventData.position;
            RectTransform rt = GetComponent<RectTransform>();
            lx = (Screen.width - rt.rect.width) / 2 - transform.localPosition.x;
            lx2 = (Screen.width - rt.rect.width) / 2 + transform.localPosition.x;
            ly = (Screen.height - rt.rect.height) / 2 - transform.localPosition.y;
            ly2 = (Screen.height - rt.rect.height) / 2 + transform.localPosition.y;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {

    }
}

有关Unity UI点击事件系统的更多相关文章

  1. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  2. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  5. ruby-on-rails - 事件记录 : Select max of limit - 2

    我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).

  6. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  7. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

  8. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  9. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

  10. ruby-on-rails - 如何构建复杂的 Rails 系统 - 2

    关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭8年前。Improvethisquestion我们有以下(以及更多)系统,我们将数据从一个应用推送/拉取到另一个:托管CRM(InsideSales.com)Asterisk电话系统(内部)横幅广告系统(openx,我们托管)潜在客户生成系统(自行开发)电子商务商店(spree,我们托管)工作板(本土)一些工作网站抓取+入站工作提要电子邮件传送系统(如Mailchimp,自主开发)事件管理系统(如eventbrite,自主开发)仪表板系统(大量图表和

随机推荐