在Cascades框架中,使用Signals(信号)和slots(信号槽)机制来实现对象间通讯,如类A中封装了登录的相关操作,有一个signal为loginResult(bool result)函数,你可以在B中设置一个slot为onLoginResult(bool result)函数,把它们绑定起来:connect(A, SIGNAL(loginResult(bool)), B, onLoginResult(bool)),这样当A中emit loginResult(bool)的时候(发送信号),会通知调用B中onLoginResult(bool),这样就可以在B中进行相应的处理了,而且A和B的耦合度非常低。signal和slot都是类的成员函数,它们之间不是一对一的关系,你可以不为signal设置slot,也可以为一个signal设置多个slot;同时也可以让signal和另一个对象的signal绑定,这样当signal被emit的时候,另一个signal也会被emit。
1. 使用UI控件中预定义的signals和slots
Cascades框架中的很多UI控件声明了一些signals,当用户与app进行交互的时就会触发某个signal,如点击按钮、滑动滑块等,你可以在QML或者C++中处理这些信号。
(1)在QML中,操作起来比较简单,预定义signals对应的signal处理程序会自动创建,名字为:signal名字前+on且把signal首字母大写,采用驼峰命名风格。如:Button的一个signal为clicked(),对应的信号处理程序则为onClicked,如:

Page {
    content: Button {
        text: "Click me"

        onClicked: {
            text = "Clicked!"
        }
    }
}

一些预定义的signals也包含了参数,你可以在对应的信号处理程序中直接使用,如CheckBox控件的一个signal:checkedChanged (bool checked),你可以在onCheckedChanged中直接使用checked:

Page {
    content: Container {
        CheckBox {
            onCheckedChanged: {
                if (checked)
                    checkLabel.text = "Checked";
                else
                    checkLabel.text = "Not checked";
            }
        }

        Label {
            id: checkLabel
            text: "Not checked"
        }
    } // end of Container
} // end of Page

(2)在C++中,需要使用QObject::connect(A, SIGNAL(a()), B, b())这样的形式将A中的signal和B中的slot连接起来,A发送信号,B接收信号。SIGNAL和SLOT是两个宏,目的就是为了把signal a和slot b转换成字符串常量。下面可以实现这样一个效果:通过拖动滑块改变一个矩形区域的宽度。实现思路很简单,把拖动滑块时发送的signal跟设置矩形区域宽度的slot连接起来,示例代码:

Slider* mySlider = Slider::create()
                    .from(100)
                    .to(500);
Container* mySizedContainer = Container::create()
                               .preferredWidth(100)
                               .preferredHeight(100)
                               .background(Color::DarkGreen);
bool res = QObject::connect(mySlider, SIGNAL(immediateValueChanged(float)),
                            mySizedContainer, SLOT(setPreferredWidth(float)));
Q_ASSERT(res);
Q_UNUSED(res);

由于,connect函数中的东东容易写错,所以这里使用Q_ASSERT()对返回值res进行检验。需要注意的是,signal和slot参数匹配时类型必须是一样的,如果signal参数为int,而slot参数为QString,就会connect不成功。不过参数的个数不需要是相同的,slot可以比signal的参数个数少,如signal(int i, String s)跟slot(int i)连接时,是可以connect成功的,只不过signal的String类型的参数被忽略掉了,不能出现signal参数比slot参数少。还要注意的是如果signal和slot的参数类型为Cascades定义的类型,则需要写上类的完全限定名:前面加上命名空间,如:

QObject::connect(myImageView,
                 SIGNAL(imageChanged(bb::cascades::Image*)),
                 myHandler,
                 SLOT(onImageChanged(bb::cascades::Image*)));

2. 创建自定义的signals和slots
(1)在QML中,自定义signal很简单,在函数前面加上signal关键字就行了,emit的时候直接调用函数。如:

        Button {
            id: myButton
            signal clickedWithText(string currentText)
            text: "Hello world"
            onClicked: {
                clickedWithText(text)
            }
            onClickedWithText: {
                console.debug("onClickedWithText: " + text)
            }
        }

点击按钮的时候会emit clickedWithText,跟预定义的信号一样,会自动生成一个onXXsignal处理程序,因此在控制台会打印出内容 onClickedWithText:Hello World, 如图

你也可以将自定义的JavaScript函数指定为slot,然后跟自定义的signal connect起来,可以分别把signal和slot定义在不同的qml文件,如:新建MyLabel.qml,内容如下:

import bb.cascades 1.0

Container {
    function handleText(message) {
        label.text = message
        console.debug("handleText: " + message)
    }
    Label {
        id: label
        text: qsTr("Hello World")
        textStyle.base: SystemDefaults.TextStyles.BigText
        horizontalAlignment: HorizontalAlignment.Center
    }
}

main.qml中添加:

        MyLabel {
             id: myLabel
        }

        Button {
            id: myButton
            signal clickedWithText(string currentText)
            text: "Click me"
            onClicked: {
                myButton.clickedWithText.connect(myLabel.handleText)
                myButton.clickedWithText("Alex Zhou")
            }
        }

点击按钮的时候,handleText函数会被调用,打印日志(handleText: Alex Zhou):

(2)在C++中:在头文件中定义signals和slots,下面来模拟实现一个登陆验证的例子,创建LoginService类,用来进行登陆时验证用户名和密码,LoginService.h:

/*
 * LoginService.h
 *
 *  Created on: 2013-3-23
 *      Author: Administrator
 */

#ifndef LOGINSERVICE_H_
#define LOGINSERVICE_H_

#include <QObject>

class LoginService : public QObject{
	Q_OBJECT
public:
	LoginService();
	virtual ~LoginService();

signals:
	void loginResult(bool result);

public slots:
	void startLogin(const QString& username, const QString &passwd);

};

#endif /* LOGINSERVICE_H_ */

声明了一个signal(登陆处理结果信号)和一个slot(验证登陆请求),来实现LoginService.cpp:

/*
 * LoginService.cpp
 *
 *  Created on: 2013-3-23
 *      Author: Administrator
 */

#include "LoginService.h"
#include <QDebug>

LoginService::LoginService() {
	// TODO Auto-generated constructor stub

}

LoginService::~LoginService() {
	// TODO Auto-generated destructor stub
}

void LoginService::startLogin(const QString &username, const QString &passwd)
{
	qDebug() << "startLogin: username=" << username << "&password=" << passwd;
	if(username == "alexzhou" && passwd == "123456")
		emit loginResult(true);
	else
		emit loginResult(false);
}

接着来创建UserLogin类,内部也封装了一个signal和一个slot,该类可以封装一些函数供qml调用。
UserLogin.h:

/*
 * UserLogin.h
 *
 *  Created on: 2013-3-23
 *      Author: Administrator
 */

#ifndef USERLOGIN_H_
#define USERLOGIN_H_

#include <QObject>

class LoginService;
class UserLogin : public QObject{
	Q_OBJECT
public:
	UserLogin();
	virtual ~UserLogin();

signals:
	void login(const QString &username, const QString &passwd);

public slots:
	void onLoginResult(bool result);

private:
	LoginService *m_pLoginService;
};

#endif /* USERLOGIN_H_ */

UserLogin.cpp:

/*
 * UserLogin.cpp
 *
 *  Created on: 2013-3-23
 *      Author: Administrator
 */

#include "UserLogin.h"
#include "LoginService.h"
#include <QDebug>

UserLogin::UserLogin() {
	// TODO Auto-generated constructor stub
	m_pLoginService = new LoginService();
	bool result = QObject::connect(this, SIGNAL(login(const QString&, const QString&)), m_pLoginService, SLOT(startLogin(const QString&, const QString&)));
	Q_ASSERT (result);
	result = QObject::connect(m_pLoginService, SIGNAL(loginResult(bool)), this, SLOT(onLoginResult(bool)));
	Q_ASSERT(result);
	Q_UNUSED(result);

	emit login("alexzhou", "123456");
}

UserLogin::~UserLogin() {
	if(m_pLoginService)
	{
		delete m_pLoginService;
		m_pLoginService = NULL;
	}
}

void UserLogin::onLoginResult(bool result) {
	qDebug() << "login result: " << result;
}

这里在UserLogin类的构造函数中进行了相关绑定和emit login操作,只是为了方便,在实际开发中可以在UserLogin定义一个登陆接口,然后在登陆按钮的onClicked处理程序中调用该登陆接口。
使用的时候在(ProjectName).cpp的构造函数中加入UserLogin userLogin;就行了。运行程序,打印日志为:

BlackBerry10 TextField自动获取焦点并弹出输入法

今天来实现这样一个效果:进入注册页面时,文本输入框自动获取焦点并弹出输入法键盘。顺便吐槽一下,这个功能本身来说是很容易实现的,但是由于BB10资料太少...

阅读全文

BlackBerry10 Cascades应用生命周期

Cascades框架可以让我们在BlackBerry应用平台上创建UI组件变得更容易和更快速,它基于QT4.8,QT4.8是用c++开发的应用框架。 所有的Cascades应用都有类似的生...

阅读全文

BlackBerry10 让Cascades应用运行到手机上

上一篇博客我们实现了一个简单的Cascades Demo,并成功运行到模拟器中,但是在模拟器上运行还是有点不爽,今天要把他跑在手机上。BB10程序安装到真机调试还是...

阅读全文

2 条评论

  1. 哥们,关于你的代码有些事情想问你啊 求指导 ,能加我qq不 350430415

  2. 本人提供淘宝店真实推广服务,按销量给提成,有意者联系我QQ 81937975 洽谈商品及提成比例。

欢迎留言