【问题标题】:How to detect touch move over a large number of views?如何在大量视图中检测触摸移动?
【发布时间】:2019-07-30 11:22:08
【问题描述】:

我的要求:我需要通过 Android 应用程序通过 BluetoothLE 传递帧来制作 7*16 LED 显示屏的动画。我已经在应用程序上创建了显示设计,并为它们添加了带有渐变可绘制背景的空视图。当我的触摸进入它们时,这些视图的颜色需要改变。在我的情况下,为每个视图添加一个触摸监听器并没有帮助。

我的成就:我有大量的视图(100+)以编程方式添加,每个视图都设置了一个标签。我为添加了这些视图的父视图设置了 OnTouch 事件处理程序。 通过跟踪触摸事件的绝对坐标(x 和 y)并与我在触摸事件处理程序中循环的几个单独视图的绝对边界进行比较,我能够检测到像触摸移动一样的悬停(超出边界到 in bounds) 超过 3-4 个视图。

我已经参考了https://stackoverflow.com/a/21903640的解决方案

卡住的地方:但是,当我尝试增加循环大小以覆盖所有添加的视图时,应用程序响应速度变慢,并且大多数视图上的悬停检测都失败了。我知道发生这种情况是因为 OnTouch 事件处理程序中的大量计算是我不应该做的。

我需要什么:我需要在性能方面对此解决方案进行改进,或者采用替代方法来实现我的目标。

代码片段

void DrawScreen()
        {
            for (int column = 0; column < 8; column++)
            {
                for (int row = 0; row < 17; row++)
                {
                    relativeLayout.AddView(DrawRect(row, column));
                }
            }
        }

View DrawRect(int row, int column)
        {
            View customView = new View(Context);
            GradientDrawable shape = new GradientDrawable();
            shape.SetShape(ShapeType.Rectangle);
            shape.SetCornerRadii(new float[] { 10, 10, 10, 10, 10, 10, 10, 10 });
            shape.SetColor(Color.ParseColor("#3F0000"));
            RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
            param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
            param.Width = width;
            param.Height = height;
            param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
            customView.Background = shape;
            customView.LayoutParameters = param;
            customView.Tag = (8 - column).ToString() + "," + (17 - row).ToString();           
            return customView;
        }

private void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
    {
        if(e.Event.Action == MotionEventActions.Up)
        {
            out_of_bounds = true;
            view_in_bound = null;
        }
        else
        {
            for (int row = 1; row < 8; row++)
            {
                for (int column = 1; column < 17; column++)
                {
                    View view = relativeLayout.FindViewWithTag(row.ToString() + "," + column.ToString());

                    if (CheckInterSection(view, e.Event.RawX, e.Event.RawY))
                    {
                        if (out_of_bounds == true)
                        {
                            view_in_bound = view;
                            out_of_bounds = false;
                            Log.Debug("Touch", "Inside");
                            //ToggleViewState(view);                                   
                        }
                        else
                        {
                            Log.Debug("Touch", "Still Inside");
                        }
                    }
                    else
                    {
                        if (view == view_in_bound)
                        {
                            out_of_bounds = true;
                            view_in_bound = null;
                            Log.Debug("Touch", "Outside");
                        }                                
                    }
                }
            }
        }
    }

bool CheckInterSection(View view, float rawX, float rawY)
        {
            int[] location = new int[2];
            view.GetLocationOnScreen(location);           
            int x = location[0] - h_spacing/2;
            int y = location[1] - v_spacing/2;
            int width = (view.Width + h_spacing/2);
            int height = (view.Height + v_spacing/2);
            return (!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height)));
        }

【问题讨论】:

  • 为什么要为添加了这些视图的父视图设置OnTouch Event Handler。这么多视图是怎么显示的?你可以详细解释一下为什么这样做。你想实现的具体功能是什么?这将有助于给出解决方案。
  • @JuniorJiang-MSFT 添加了我的要求以进一步澄清我的问题。希望有帮助
  • 感谢更新。如果视图很多,需要同时响应Touch,UI的负担非常大,最终会出现界面卡顿,显示不及时。有一个可能的解决方案,就是在父视图中只添加一个 Touch 事件。当手指在屏幕的多个子视图上滑动时,通过代码计算Touch的范围和轨迹,这似乎是处理算法的一种解决方案。
  • @JuniorJiang-MSFT 实际上,我已经设法实现了一个公式,其中只要一个孩子被触摸,相邻的孩子视图就会成为将循环大小减小到 9 (3*3) 的范围。虽然,我注意到只有很小的改进。我还考虑过添加轨迹和最小触摸移动距离以进一步微调性能。但是,我观察到触摸事件每秒仅触发 40 次左右,这对于进一步优化来说太少了,因为它会导致无响应的触摸。是不是比平时少?
  • 不确定特殊时间。但不明白需要计算每秒多少次。如果在视图范围内触摸,则更改颜色。并且父视图中只有一个触摸事件,它移动到哪里,改变它的颜色。

标签: android xamarin.android hover android-touch-event touchmove


【解决方案1】:

我尝试使用轨迹角度来进一步减小循环大小,但性能从未达到我的预期,并且经常错过与视图相关的触摸事件。

然后我意识到我走错了方向,并找到了一个更简单的解决方案。由于我的视图是以编程方式添加的并且大小相同,因此我知道布局中每个视图的坐标和边界。所以我将布局划分为一个网格,根据触摸坐标,我能够识别触摸在哪个部分。以下是我一直运行良好的解决方案。但是,我会等待一段时间,直到我将此标记为解决方案,因为有人可以更好地实施我的技术或替代解决方案。

void DrawScreen()
        {            
            for (int column = 1; column < 17; column++)
            {
                for (int row = 1; row < 8; row++)
                {
                    relativeLayout.AddView(DrawRect(row, column));
                }
            }
        }

View DrawRect(int row, int column)
        {
            View customView = new View(Context);
            if (!CheckBit(row - 1, column - 1))
            {
                customView.SetBackgroundResource(Resource.Drawable.off_rounded_btn);
            }
            else
            {
                customView.SetBackgroundResource(Resource.Drawable.rounded_btn);
            }
            RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
            param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
            param.Width = width;
            param.Height = height;
            param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
            customView.LayoutParameters = param;
            customView.Tag = row.ToString() + "," + column.ToString();           
            return customView;
        }

void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
        {
            if (e.Event.Action == MotionEventActions.Up)
            {
                view_in_bound = null;
            }
            else
            {
                int row = CheckTouchArea(e.Event.RawX, e.Event.RawY)[0];
                if (row != 0)
                {
                    int column = CheckTouchArea(e.Event.RawX, e.Event.RawY)[1];
                    check_view = GetView(row, column);
                    if (check_view != view_in_bound)
                    {
                        ChangeViewState(check_view, Touch_CheckBit(row - 1, column - 1), row - 1, column - 1);
                        view_in_bound = check_view;
                    }
                }   
            }
        }

int[] CheckTouchArea(float rawX, float rawY)
        {
            int[] tag = new int[2];
            int[] location = new int[2];
            relativeLayout.GetLocationOnScreen(location);
            float x = location[0] + h_padding / 2;
            int y = location[1] + v_padding / 2;
            float width = relativeLayout.Width - h_padding;
            int height = relativeLayout.Height - v_padding;
            if ((!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height))))
            {
                int column = (int)Math.Ceiling((rawX - x) * 16 / width);
                int row = (int)(Math.Ceiling((rawY - y) * 7 / height));
                tag[0] = row;
                tag[1] = column;
            }            
            return tag;
        }  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多