I want to create horizontal scrolling images. If you saw "Blackberry Travel" app, in this app at the top images scroll dynamically. I want to create same scrolling view. Also at the bottom of image we can see circles. As image changes, particular circle becomes dark. This scroll have both ways means it scrolls timely manner and by clicking mouse it also scrolls. So, please help me to create such scrolling. I am new here. Thank you...
-
Is there anyone who knows the answer of this question? – GoNish May 22 '14 at 11:33
-
I have been thinking about this since you posed the question. It is quite a simple concept but coming up with a clean implementation will take some careful planning. I think what you want is two custom controls. One that will handle scrolling images by the swipe gesture, provide the snap to center and all the other expected behavior. The second custom control would provide the "dots" UI and accept touch and swipe gestures. Then these two controls would communicate with signals and slots. Perhaps a data model object to provide the images. I will see what I can put together. – Richard May 22 '14 at 13:36
-
Richard, I have tried it through ListView with xmldatamodel, but I can not create that "dots" and their proper working. – GoNish May 22 '14 at 13:41
1 Answers
Here is a custom control based on the Container that provides the row of circles/dots selection paradigm. Standard disclaimers apply, for demonstration purposes, I don't guarantee to have caught all error conditions, etc. Specifically the math calculating which index item is selected is a bit off because by default the Container does not put as much space at each end as it does between items. I have to leave you something to do ;)
You will also have to provide images for the selected and deselect form, but this is not difficult.
Here is the class definition:
/*
* ScrollGadget.hpp
*
* Created on: May 22, 2014
* Author: richard
*/
#ifndef SCROLLGADGET_HPP_
#define SCROLLGADGET_HPP_
#include <QObject>
#include <bb/cascades/Container>
#include <bb/cascades/ImageView>
#include <bb/cascades/Layout>
#include <QUrl>
using namespace bb::cascades;
namespace net
{
namespace test
{
class ScrollGadget : public Container
{
Q_OBJECT
Q_PROPERTY(int selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY selectedIndexChanged)
Q_PROPERTY(int itemCount READ itemCount WRITE setItemCount NOTIFY itemCountChanged)
public:
ScrollGadget(Container *parent = 0);
virtual ~ScrollGadget();
int selectedIndex() const;
int itemCount() const;
public slots:
void setSelectedIndex(int index);
void syncSelectedIndex(int index);
void setItemCount(int count);
private slots:
void onTouch(bb::cascades::TouchEvent*);
void handleLayoutFrameUpdated(QRectF);
signals:
void selectedIndexChanged(int selectedIndex);
void itemCountChanged(int count);
private:
int mSelectedIndex, mItemCount, mSaveIndex;
qreal mWidth, mHeight;
QUrl mSelected, mDeselected;
ImageView **mImageView;
};
} /* namespace test */
} /* namespace net */
#endif /* SCROLLGADGET_HPP_ */
And the C++ source:
/*
* ScrollGadget.cpp
*
* Created on: May 22, 2014
* Author: richard
*/
#include <QDebug>
#include <src/ScrollGadget.hpp>
#include <bb/cascades/TouchBehavior>
#include <bb/cascades/TouchType>
#include <bb/cascades/PropagationPhase>
#include <bb/cascades/TouchResponse>
#include <bb/cascades/StackLayout>
#include <bb/cascades/LayoutOrientation>
#include <bb/cascades/LayoutUpdateHandler>
using namespace bb::cascades;
namespace net
{
namespace test
{
ScrollGadget::ScrollGadget(Container *parent) : Container(parent),
mSelectedIndex(0),
mItemCount(0),
mSaveIndex(0),
mWidth(0),
mHeight(0),
mSelected("asset:///active.png"),
mDeselected("asset:///inactive.png"),
mImageView(NULL)
{
qDebug() << __PRETTY_FUNCTION__;
setLayout(StackLayout::create().orientation(LayoutOrientation::LeftToRight));
addTouchBehavior(
TouchBehavior::create()
.addTouchReaction(TouchType::Down,
PropagationPhase::AtTarget,
TouchResponse::StartTracking));
LayoutUpdateHandler::create(this)
.onLayoutFrameChanged(this, SLOT(handleLayoutFrameUpdated(QRectF)));
bool c = this->connect(this, SIGNAL(touch(bb::cascades::TouchEvent*)), this, SLOT(onTouch(bb::cascades::TouchEvent*)));
Q_ASSERT(c);
Q_UNUSED(c);
}
ScrollGadget::~ScrollGadget()
{
if (mImageView) {
delete mImageView;
}
}
int ScrollGadget::selectedIndex() const {
return mSelectedIndex;
}
int ScrollGadget::itemCount() const {
return mItemCount;
}
void ScrollGadget::setSelectedIndex(int index) {
if (index != mSelectedIndex && index >= 0 && index < mItemCount) {
syncSelectedIndex(index);
emit selectedIndexChanged(mSelectedIndex);
qDebug() << "selectedIndexChanged(" << mSelectedIndex << ")";
}
}
void ScrollGadget::syncSelectedIndex(int index) {
if (index != mSelectedIndex && index >= 0 && index < mItemCount) {
mImageView[mSelectedIndex]->setImageSource(mDeselected);
mSelectedIndex = index;
mImageView[mSelectedIndex]->setImageSource(mSelected);
}
}
void ScrollGadget::setItemCount(int count) {
qDebug() << __PRETTY_FUNCTION__;
if (count != mItemCount && count > 0) {
if (mItemCount > 0 && mImageView != NULL) {
for (int i = 0; i < mItemCount; i++) {
if (mImageView[i] != NULL) {
remove(mImageView[i]);
mImageView[i]->deleteLater();
}
}
}
mItemCount = count;
if (mSelectedIndex < 0 || mSelectedIndex >= mItemCount) {
mSelectedIndex = 0;
}
mImageView = new ImageView*[mItemCount];
for (int i = 0; i < mItemCount; i++) {
mImageView[i] = new ImageView(this);
if (i == mSelectedIndex) {
mImageView[i]->setImageSource(mSelected);
} else {
mImageView[i]->setImageSource(mDeselected);
}
add(mImageView[i]);
}
emit selectedIndexChanged(mSelectedIndex);
qDebug() << "selectedIndexChanged(" << mSelectedIndex << ")";
}
}
void ScrollGadget::onTouch(TouchEvent *event) {
//qDebug() << __PRETTY_FUNCTION__ << *event;
int index = event->localX() / (mWidth / mItemCount);
qDebug() << "index" << index;
if (index < 0) index = 0;
if (index >= mItemCount) index = mItemCount - 1;
switch (event->touchType()) {
case TouchType::Down:
mSaveIndex = mSelectedIndex;
setSelectedIndex(index);
break;
case TouchType::Move:
case TouchType::Up:
setSelectedIndex(index);
break;
case TouchType::Cancel:
setSelectedIndex(mSaveIndex);
break;
}
}
void ScrollGadget::handleLayoutFrameUpdated(QRectF rect) {
qDebug() << __PRETTY_FUNCTION__ << rect;
mWidth = rect.width();
mHeight = rect.height();
}
} /* namespace test */
} /* namespace net */
This QML file will demonstrate it in an empty BlackBerry project:
import bb.cascades 1.2
import net.test 1.0
Page {
Container {
Label {
id: label
// Localized text with the dynamic translation and locale updates support
text: qsTr("Hello World ") + scroll.selectedIndex + Retranslate.onLocaleOrLanguageChanged
textStyle.base: SystemDefaults.TextStyles.BigText
}
ScrollGadget {
id: scroll
itemCount: 3
}
}
}
But don't forget to register ScrollGadget before loading the QML document in applicationui.cpp:
#include "ScrollGadget.hpp"
...
qmlRegisterType<ScrollGadget>("net.test", 1, 0, "ScrollGadget");
// Create scene document from main.qml asset, the parent is set
// to ensure the document gets destroyed properly at shut down.
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
...
Here are the active and inactive images I used.
To use this gadget connect the selectedIndex to an image display object, like a list. You can connect the selection signal from the image display, like selectedIndex in a list, to the syncSelectedIndex of the ScrollGadget so that changes in the list are reflected in the gadget.

- 8,920
- 2
- 18
- 24
-
Hey Richard, I have just copy and paste the code and output is "Hello World 0" nothing else. What to do now? – GoNish May 24 '14 at 10:13
-
If you have images for "asset:///active.png" "asset:///inactive.png" you should see three of those images. The first will be active, the other two inactive. You should then be able to click on those images to select which one of the 3 is active, or slide your finger back and forth. – Richard May 24 '14 at 20:19
-
Hi Richard, thanks a lot. I am trying to understand the code some part I understood. Now, suppose I don't know how many images come in to the scroll means some times there may be 3 images or may be 5. Actually, I want to provide images from server so that the count of Active and Inactive images also changes. Do you have any Idea? – GoNish May 26 '14 at 07:17
-
That is what the setItemCount slot is for. You can either call it like a function in C++ or in QML. Use it as a property in QML as I did to set it to 3, or hook it up to a signal from your data source that provides the number of items as an integer. – Richard May 26 '14 at 16:43