我的要求:我需要通过BluetoothLE通过Android应用程序传递帧来为7 * 16 LED显示屏制作动画。我在应用程序上创建了显示的设计,并添加了带有渐变可绘制背景的空视图。当我的触摸进入它们时,这些视图的颜色需要改变。在我的情况下,为每个视图添加触摸侦听器不会有所帮助。
我所取得的成就:我以编程方式添加了大量视图(100+),并为每个视图添加了一个标记。我已为添加了这些视图的父视图设置了OnTouch事件处理程序。通过跟踪触摸事件(x和y)的绝对坐标并与我在触摸事件处理程序中循环的几个单独视图的绝对边界进行比较,我能够检测到像触摸移动一样的悬停(超出界限)边界)正确地超过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)));
}
我尝试使用轨迹角度来进一步减小环路尺寸,但性能从未达到我的预期,并且视图上的触摸事件经常被错过。
然后我意识到我走错了路,找到了一个更简单的解决方案。由于我的视图是以编程方式添加的并且大小相同,因此我知道布局中每个视图的坐标和边界。因此,我将布局划分为网格,并根据触摸坐标,我能够识别触摸所在的部分。以下是我的解决方案,它一直运作良好。但是,我会等待一段时间,直到我将此标记为解决方案,因为有人可以更好地实现我的技术或替代解决方案。
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;
}