上一篇博客完成了BlackBerry10开发环境的搭建,现在来开发一个简单的图片切换程序,该应用的主要功能是:在屏幕中间放置两张图片,首先把上面那张图片的透明度设置为0,这样一开始只能看到下面的那张图片,然后通过一个滑块左右拖动改变上面图片的透明度,达到切换图片的效果。该程序源码官网有下载,不过为了加深印象,多了解一些细节问题,还是重新手工敲一遍比较好~~
1. 创建一个空项目
(1)打开QNX Momentics IDE,File > New > BlackBerry Project
(2)选择 Application > Cascades 然后点击 Next.

(3) 选择Standard empty project 然后点击 Next.

(4) 填入项目的名称,如MyFirstBB10App,Finish。
这样一个空的BB10项目就创建好了,项目结构如下图,我们现在暂时只关心assets和src目录,assets目录存放的是应用中需要使用的图片等资源文件;src中存放的是源代码文件,我们应用中的图片存放在assets/images目录下,图片到这里去下载。
现在把这个空项目部署到模拟器上去,右击项目 > Build Configurations > Set Active 勾选 Simulator-Debug,然后Build Project,编译结束后Run As > BlackBerry C/C++ Application,运行成功后,你会看到模拟器会显示:Hello World。如图:

2. 分析这个空项目相关的源码
(1)打开src目录下的main.cpp文件,源码如下:

// Default empty project template
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

#include <QLocale>
#include <QTranslator>
#include <Qt/qdeclarativedebug.h>
#include "MyFirstBB10App.hpp"

using namespace bb::cascades;

Q_DECL_EXPORT int main(int argc, char **argv)
{
    // this is where the server is started etc
    Application app(argc, argv);

    // localization support
    QTranslator translator;
    QString locale_string = QLocale().name();
    QString filename = QString( "MyFirstBB10App_%1" ).arg( locale_string );
    if (translator.load(filename, "app/native/qm")) {
        app.installTranslator( &translator );
    }

    new MyFirstBB10App(&app);

    // we complete the transaction started in the app constructor and start the client event loop here
    return Application::exec();
    // when loop is exited the Application deletes the scene which deletes all its children (per qt rules for children)
}

Application类是应用的核心,它开启了程序的主事件循环,处理应用的初始化和回收,以及用户和应用的所有交互,如触摸事件、按钮点击事件等。
QmlDocument类表示一个QML文档,通过这个类可以加载qml格式的文件,BB10可以通过qml或c++来创建UI,但是使用qml开发效率更高。AbstractPane是一个抽象类,它是所有Page类和Pane类的基类。QTranslator和QLocale是QT类,它们是用来处理字符串的本地化的。MyFirstBB10App.hpp,hpp文件与*.h类似,是c++程序的头文件,在我们自己创建的源文件中包含该文件,实现应用程序的UI和逻辑处理。Application::exec()表示开启程序的主事件循环,在程序终止的时候释放资源。
现在来看看MyFirstBB10App.hpp文件源码,它是自动生成的,名称跟项目名称一样,不需要修改。

// Default empty project template
#ifndef MyFirstBB10App_HPP_
#define MyFirstBB10App_HPP_

#include <QObject>

namespace bb { namespace cascades { class Application; }}

/*!
 * @brief Application pane object
 *
 *Use this object to create and init app UI, to create context objects, to register the new meta types etc.
 */
class MyFirstBB10App : public QObject
{
    Q_OBJECT
public:
    MyFirstBB10App(bb::cascades::Application *app);
    virtual ~MyFirstBB10App() {}
};

#endif /* MyFirstBB10App_HPP_ */

这里定义了MyFirstBB10App类,它继承了QObject,QObject是所有QT类的基类,MyFirstBB10App类的构造函数需要一个Application对象指针,所以它在前面对Application类做了前向声明。接下来去看看它的源文件MyFirstBB10App.cpp的源码:

// Default empty project template
#include "MyFirstBB10App.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

using namespace bb::cascades;

MyFirstBB10App::MyFirstBB10App(bb::cascades::Application *app)
: QObject(app)
{
    // create scene document from main.qml asset
    // set parent to created document to ensure it exists for the whole application lifetime
    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);

    // create root object for the UI
    AbstractPane *root = qml->createRootObject<AbstractPane>();
    // set created root object as a scene
    app->setScene(root);
}

调用了QObject的构造函数,官网上说是为了保证当程序终止的时候,MyFirstBB10App对象被回收。因为默认是使用qml定义UI的,所以这里使用QmlDocument加载了main.qml文件,然后用QmlDocument对象创建了UI的根对象,作为应用的场景。main.qml文件定义和组织了屏幕上显示的UI元素,看看main.qml的内容:

// Default empty project template
import bb.cascades 1.0

// creates one page with a label
Page {
    Container {
        layout: DockLayout {}
        Label {
            text: qsTr("Hello World")
            textStyle.base: SystemDefaults.TextStyles.BigText
            verticalAlignment: VerticalAlignment.Center
            horizontalAlignment: HorizontalAlignment.Center
        }
    }
}

qml格式跟css很像,最外面的是Page,你可以想象成一个屏幕,用它来包含其他容器元素,一般是往容器里面放容器或者其他UI元素,跟Android中的ViewGroup和View类似。接下来是Container ,它包含了一个layout和一个Label,layout表示Container子元素的布局方式为DockLayout。BlackBerry 10 Cascades中提供了三种布局管理器:DockLayout、StackLayout、AbsoluteLayout。为了篇幅,这里就简单介绍一下吧。
DockLayout:按照“上,中,下”,“左,中,右”的组合进行摆放,不会按照它在qml中定义的顺序摆放,可以设置组件与父容器之间的间隙。

StackLayout:按照qml文件中定义的顺序排列,可以指定是水平排列或者垂直排列。

AbsoluteLayout:也不按照qml文件中组件定义的次序摆放,按照组件的xy坐标值进行绝对定位。

Label表示一个文字标签,这里设置为水平和垂直方向都居中显示。对应那个“Hello World”效果图就看明白了。
到目前为止,我们分析了项目的执行的流程和相关源码,可以看出我们只需要修改MyFirstBB10App源文件中构造函数的代码,把创建UI元素的代码替换成我们这个例子程序所需要的代码就可以了。这篇博文用C++来创建UI,不过一般在开发中还是使用QML来创建UI界面开发效率要高很多,类似于Android开发中使用xml创建界面一样,接下来终于要开始写代码了啊,兴奋吧,吼吼~

首先我们分成三部分:root container,上部container(包含两张图片),底部container(包含一个slider和两张图标),如图:

1. 创建root container
root container用来管理上部container和底部container,可以使用StackLayout来实现。先把MyFirstBB10App构造函数中的代码清空,然后添加下面的代码:

	// create root container
	Container *pRootContainer = Container::create();
	pRootContainer->setLayout(StackLayout::create());
	pRootContainer->setTopPadding(20.0f);
	// Set the background to a dark color
	pRootContainer->setBackground(Color::fromARGB(0xff262626));

上述代码设置布局方式为StackLayout,背景色为深灰色。看到上面的代码格式,是不是感觉跟cocos2d-x很像呢。
2. 创建Image Container
Image Container用来存放两张背景图的,背景图用ImageView表示,添加代码:

	// create the images container
	Container *pImageContainer = Container::create();
	pImageContainer->setLayout(DockLayout::create());
	pImageContainer->setHorizontalAlignment(HorizontalAlignment::Center);

	ImageView *pImageNight = ImageView::create("asset:///images/night.jpg");
	pImageNight->setHorizontalAlignment(HorizontalAlignment::Center);
	pImageNight->setVerticalAlignment(VerticalAlignment::Center);

	ImageView *pImageDay = ImageView::create("asset:///images/day.jpg").opacity(0.0f);
	pImageDay->setHorizontalAlignment(HorizontalAlignment::Center);
	pImageDay->setVerticalAlignment(VerticalAlignment::Center);

	pImageContainer->add(pImageNight);
	pImageContainer->add(pImageDay);

注意:别把asset写成了assets了。第二张图片的透明度设置为0,其他代码很容易理解。
3. 创建Slider Container
Slider Container用来存放一个Slider和两个图标的。添加代码:

	// create the slider container
	Container *pSliderContainer = Container::create().left(20.0f).right(20.0f).top(25.0f).bottom(25.0f);
	pSliderContainer->setLayout(StackLayout::create().orientation(LayoutOrientation::LeftToRight));
	pSliderContainer->setHorizontalAlignment(HorizontalAlignment::Center);

	Slider *pSlider = Slider::create().leftMargin(20.0f).rightMargin(20.0f);
	pSlider->setLayoutProperties(StackLayoutProperties::create().spaceQuota(1.0f));
	pSlider->setHorizontalAlignment(HorizontalAlignment::Fill);

	ImageView *pImageMoon = ImageView::create("asset:///images/moon.png");
	pImageMoon->setVerticalAlignment(VerticalAlignment::Center);
	ImageView *pImageSun = ImageView::create("asset:///images/sun.png");
	pImageSun->setVerticalAlignment(VerticalAlignment::Center);

spaceQuota函数是为了指定控件在空间分配时相对于它的兄弟姐妹所占的权重,默认值是负数,不允许为0,跟android中的weight含义类似,如A为1.0,B为3.0,会分配1/4剩余空间给A,分配3/4剩余空间给B。现在需要实现这个功能:当拖动滑块的时候,上面的那张图片改变透明度。可以使用QT信号和插槽机制把这两个对象连接绑定起来,需要用到connect函数,connect(sender, SIGNAL(signal), receiver, SLOT(slot)):sender和receiver是指向QObject类型的指针,signal和slot是不带参数名的函数名,为了连接成功,两者的参数类型和顺序最好是一样的;如果一方的参数比另一方多,那么多余的部分会被忽略掉。这里发射信号的一方是Slider,对应函数为immediateValueChanged(float),插槽为pImageDay,对应函数为setOpacity(float)。因为Slider从左至右的值为0.0f到1.0f,可以用该值直接设置图片的透明度,添加代码:

	connect(pSlider, SIGNAL(immediateValueChanged(float)), pImageDay, SLOT(setOpacity(float)));

4. 现在需要把上面创建的组件添加到对应的容器中去

	pSliderContainer->add(pImageMoon);
	pSliderContainer->add(pSlider);
	pSliderContainer->add(pImageSun);

	pRootContainer->add(pImageContainer);
	pRootContainer->add(pSliderContainer);

	Page *page = Page::create();
	page->setContent(pRootContainer);

	app->setScene(page);

StackLayout布局方式的需要注意添加顺序。完整源码如下:

MyFirstBB10App::MyFirstBB10App(bb::cascades::Application *app)
: QObject(app)
{
	// create root container
	//Container *pRootContainer = new Container();
	Container *pRootContainer = Container::create();
	pRootContainer->setLayout(StackLayout::create());
	pRootContainer->setTopPadding(20.0f);
	// Set the background to a dark color
	pRootContainer->setBackground(Color::fromARGB(0xff262626));

	// create the images container
	//Container *pImageContainer = new Container();
	Container *pImageContainer = Container::create();
	pImageContainer->setLayout(DockLayout::create());
	pImageContainer->setHorizontalAlignment(HorizontalAlignment::Center);

	ImageView *pImageNight = ImageView::create("asset:///images/night.jpg");
	pImageNight->setHorizontalAlignment(HorizontalAlignment::Center);
	pImageNight->setVerticalAlignment(VerticalAlignment::Center);

	ImageView *pImageDay = ImageView::create("asset:///images/day.jpg").opacity(0.0f);
	pImageDay->setHorizontalAlignment(HorizontalAlignment::Center);
	pImageDay->setVerticalAlignment(VerticalAlignment::Center);

	pImageContainer->add(pImageNight);
	pImageContainer->add(pImageDay);

	// create the slider container
	Container *pSliderContainer = Container::create().left(20.0f).right(20.0f).top(25.0f).bottom(25.0f);
	pSliderContainer->setLayout(StackLayout::create().orientation(LayoutOrientation::LeftToRight));
	pSliderContainer->setHorizontalAlignment(HorizontalAlignment::Center);

	Slider *pSlider = Slider::create().leftMargin(20.0f).rightMargin(20.0f);
	pSlider->setLayoutProperties(StackLayoutProperties::create().spaceQuota(1.0f));
	pSlider->setHorizontalAlignment(HorizontalAlignment::Fill);

	ImageView *pImageMoon = ImageView::create("asset:///images/moon.png");
	pImageMoon->setVerticalAlignment(VerticalAlignment::Center);
	ImageView *pImageSun = ImageView::create("asset:///images/sun.png");
	pImageSun->setVerticalAlignment(VerticalAlignment::Center);

	connect(pSlider, SIGNAL(immediateValueChanged(float)), pImageDay, SLOT(setOpacity(float)));

	pSliderContainer->add(pImageMoon);
	pSliderContainer->add(pSlider);
	pSliderContainer->add(pImageSun);

	pRootContainer->add(pImageContainer);
	pRootContainer->add(pSliderContainer);

	//Page *page = new Page();
	Page *page = Page::create();
	page->setContent(pRootContainer);

	app->setScene(page);

}

大功告成,现在编译运行吧,最终效果图如下,慢慢拖动滑块,图片也会慢慢变化:

BlackBerry10 学习Signals和Slots

在Cascades框架中,使用Signals(信号)和slots(信号槽)机制来实现对象间通讯,如类A中封装了登录的相关操作,有一个signal为loginResult(bool result)函数,你...

阅读全文

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

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

阅读全文

BlackBerry10 Cascades应用生命周期

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

阅读全文

1 条评论

欢迎留言