一步一步实现自己的模拟控件(9)——消息处理

这次我们将要给Widget增加一些状态,并使其能够接受出消息处理扩展,测试工程中实现了一个按钮的消息处理扩展。

Widget状态:

之前的控件只是绘制了一个边框,并且总是会在窗口中显示。实际上我们往往会希望能够让某个控件显示或者隐藏、可用或者不可用等等,那么控件应该具有能够标识这些状态的属性,于是我们给Widget增加了状态概念。

// 状态相关
void AddStates(size_t states);
void SubStates(size_t states);
bool CheckState(widget::StateBitField s);

上面是状态相关的几个接口,包括了增加状态组,减少状态组,检测状态。这里有个状态组的概念,是因为我将状态用位域来实现,那么他们就可以通过or运算来得到多个状态的集合,我就这个集合称为状态组。

enum StateBitField{
STATE_VISIBLE
= 1 << 0 , // 控件可见?决定控件是否被绘制
STATE_ENABLE = 1 << 1 , // 控件可用?决定控件是否响应鼠标键盘消息
STATE_TRANSPARENT = 1 << 2 , // 控件透明?决定控件是否响应鼠标键盘消息以及是否显示tooltip
};

目前我给Widget定义了3个状态:可见、可用、透明。默认创建的Widget是不可见、不可用、不透明的,需要在创建成功后手动设置,例如:

// 建立根控件
auto pRootWidget = ghost::Widget::Create();
pRootWidget
-> AddStates(
  ghost::widget::STATE_VISIBLE | ghost::widget::STATE_ENABLE | ghost::widget::STATE_TRANSPARENT);

消息处理扩展:

通常对于控件来说,没有消息处理就等于没有生命意义,那么为Widget添加消息处理扩展就意味着使Widget活起来,这次我们就来完成这个任务,期待Widget活起来的那激动人心的一刻。和以往添加扩展支持一样,为扩展编写一个抽象基类,在Widget的关联对象管理中添加这个扩展的关联处理,然后在Widget的SendMessage处理逻辑里增加对消息处理扩展的支持。那么我们的SendMessage实现就变成了这样:

int Widget::SendMessage(widget::Message message, void * param1 /* = 0 */ , void * param2 /* = 0 */ )
{
if ( ! CheckState(widget::STATE_ENABLE))
{
// 不响应鼠标键盘消息
if ((widget::MSG_MOUSE_FIRST <= message
&& widget::MSG_MOUSE_LAST >= message)
|| (widget::MSG_KEY_FIRST <= message
&& widget::MSG_KEY_LAST >= message))
{
return widget::MSG_RESULT_NONE;
}
}
if (CheckState(widget::STATE_TRANSPARENT))
{
// 不响应鼠标键盘消息,不显示tooltip
if ((widget::MSG_MOUSE_FIRST <= message
&& widget::MSG_MOUSE_LAST >= message)
|| (widget::MSG_KEY_FIRST <= message
&& widget::MSG_KEY_LAST >= message))
{
return widget::MSG_RESULT_NONE;
}
}

auto pRelatedObject
= GetRelatedObject();
if (pRelatedObject)
{
auto pMessageHandle
= pRelatedObject -> GetMessageHandle();
if (pMessageHandle)
{
bool handled = false ;
int res = pMessageHandle -> OnMessage( this , message, param1, param2, handled);
if (handled)
{
return res;
}
}
}

switch (message)
{
#ifdef _DEBUG
case widget::MSG_DRAW:
{
HBRUSH hBrush
= ::CreateSolidBrush(pImpl_ -> testFrameColor_);
::FrameRect((HDC)param1,
& pImpl_ -> absoluteRect_, hBrush);
::DeleteObject(hBrush);
}
break ;
#endif // _DEBUG
case widget::MSG_HIT_TEST:
if (::PtInRect( & pImpl_ -> absoluteRect_, * ( const POINT * )param1))
{
return ~ widget::MSG_RESULT_NONE;
}
break ;
}
return widget::MSG_RESULT_NONE;
}

我多次提到了tooltip,但是我们这次并没有为Widget增加其支持,它将在后面被加入到Widget中来。可以看到这里处理有对消息处理扩展的支持,还有状态对于消息的影响。这里出现了一个MSG_HIT_TEST,这是一个新增的消息。这次为Widget添加了很多的消息,包括了鼠标、键盘等,要将鼠标消息准确的发送给正确的控件,那么点击测试是必不可少的,这个MSG_HIT_TEST消息则是用于控件处理点击测试的。

点击测试:

当容器产生了鼠标事件的时候,我们能够得到鼠标热点在容器中的坐标。前面我也多次提到,模拟控件是容器中的某个区域,那么当鼠标热点位于某个控件所处的区域的时候,那么这个鼠标事件我们就应当交由这个控件进行处理(这是通常情况,也有可能某个控件作为透明控件,不接受任何点击测试)。于是我们便通过点击测试(HitTest)这个访问接口来找到应该处理鼠标事件的控件。在找到控件之后,我们还将坐标映射到了控件的相对坐标系,这样控件就可以以自身的相对位置来处理鼠标事件了。

当然,这次的内容非常多,包括坐标映射、区域映射,捕获鼠标的控件、活动控件、焦点控件等概念都未提到,但在代码中还是能够看到这些概念的。如果一一介绍,那文章就会非常冗长,也会使Widget实现进展缓慢,因此我通常都会省略一些内容,这些内容也就只能通过代码阅读来得到了。

按钮:

为了测试我们这次实现的内容,我们编写了一个按钮的消息处理扩展。简单起见,我们使其不产生命令、不绘制文本,仅仅只是展示对鼠标消息的处理和状态的变化而已。

2011042410321772.png

我们将原先测试代码中的中间那个控件关联了按钮的消息处理,那么它就称为了一个按钮控件了。我们可以将鼠标移到它上面点击看看会发生什么。

下载测试工程代码 因为我一直都在使用VC10来编写Widget,也用到了一些新的特性,所以子啊这次的测试工程我生成了一份release下的程序,没有VC10的人至少能够看到其运行效果。

转载于:https://www.cnblogs.com/EvilGhost/archive/2011/04/24/Abstract_Widget_9.html

自定义控件是Android开发中常见的任务之一。下面是一步一步教你如何自定义控件的简要指南: 第一步:创建一个新的Java类作为你的自定义控件。 首先,创建一个新的Java类,可以命名为你想要的控件名称。这个类应该继承自Android框架中的现有控件,例如View、TextView等。例如,如果你想要创建一个自定义按钮,可以创建一个名为CustomButton的类,并让它继承自Button类。 第二步:实现构造函数和属性。 在你的自定义控件类中,你可以实现构造函数和属性,以便对控件进行初始化和设置。你可以定义自己的属性,例如颜色、大小等,以及相应的getter和setter方法。 第三步:重写绘制方法。 要自定义控件的外观,你需要重写它的绘制方法。最常用的方法是重写`onDraw()`方法,在其中使用Canvas绘制你想要的形状、文本等。 第四步:处理用户交互。 如果你的自定义控件需要与用户进行交互,你可以重写相应的触摸事件(例如`onTouchEvent()`)或点击事件(例如`setOnClickListener()`)来处理用户操作。 第五步:在布局文件中使用自定义控件。 完成以上步骤后,你可以在布局文件中使用你的自定义控件了。只需在布局文件中添加一个与你的控件类名相对应的XML标签,并设置相应的属性。 这只是一个简要的指南,帮助你开始自定义控件的过程。在实际开发中,你可能需要更多的步骤和细节来完成你的自定义控件。你可以参考Android官方文档或其他教程来获取更多信息和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值