Chat Style ListView

While I am continuing to add features to my Meetup for BlackBerry 10 app, I was wondering how I could add a chat like look when I show the Event Comments.  I was hoping that there would be a simple setting in the ListView to display the items in that format, but alas, that’s not the case.  However, with the help from Joe on the BBM Group for BB10 developer newbies (Baby Steps: BB Dev), we came up with a simple solution.

The first step is to get your DataGroupModel in the right format.  When Meetup sends the Event Comments, it is in the order of when the comments were created; newest to oldest.  However, that doesn’t help if someone puts a comment reply to an earlier comment, because now it will be out of place.  When you get your comment list, you need to make sure that it has a way to point back to the parent comment.  Luckily, Meetup does provide a “reply to ID” for the comments.  Since I load all of my comments in to a sqlite table, when I want to build the DataGroupModel, I simple call the first query of only the top level comments (they will have a reply ID of “0”).  As I loop through each of those comments, I run a second query to call all the comments whose “reply to ID” matches the parent comment I am working with.


if (query.exec()) {
	while(query.next()) {
		QVariantMap map;
		// set the parentCommentID for the second query
		int parentCommentID = query.value(0).toInt();

		map["id"] =  query.value(0).toInt();
		map["inReplyTo"] = query.value(5).toInt();
		map["comment"] = query.value(6).toString();
		map["memberName"] = query.value(7).toString();
		map["memberID"] = query.value(9).toInt();
		map["commentRow"] = commentRow;

		returnGDM->insert(map);
		commentRow++;

		if (query2.exec()) {
			while(query2.next()) {
				QVariantMap map2;
				map2["id"] =  query2.value(0).toInt();
				map2["inReplyTo"] = query2.value(5).toInt();
				map2["comment"] = query2.value(6).toString();
				map2["memberName"] = query2.value(7).toString();
				map2["memberID"] = query2.value(9).toInt();
				map2["commentRow"] = commentRow;

				returnGDM->insert(map2);
				commentRow++;
			}
		}
	}
}

If you had more than one level of comments, then I guess you would have to do more loops.  Which is probably why it is a good thing not to have too many levels of comments.  The returnGDM gets populated at each pass and all the comments are in the correct order.  At this point, the ListView would look very plain.  So, next we need to make things pretty and that’s were ImageView , Nine-slice Scaling and Joe comes in.

Chat_ListView

Joe created a couple of images to be used as the chat bubbles.  With those images, you also need AssetMetaData files to control the images for scaling.  This basically means, when the image adjusts itself to the ImageView area, it won’t get distorted because the amd file shows which areas can be stretched and which ones can’t be stretched.

Here are the BBM border images and amd file codes.  Not much to it.  The only real difference is making one a Left chat bubble and the other a Right chat bubble.
bbmborder


#RimCascadesAssetMetaData version=1.0
source: "bbmborder.png"
sliceMargins: 22 22 22 22

lbbmborder


#RimCascadesAssetMetaData version=1.0
source: "lbbmborder.png"
sliceMargins: 22 22 22 22

Once we have the images, then we need put everything in a Container for placement and display.  We actually need to Containers; first for the ImageView and second for the text Label.  A DockLayout is used for the outer Container so that the ImageView overlaps the area used by the inner Container and the Label within it. The horizontalAlignment and verticalAlignment are set to Fill, which expands the image to the full size of the ImageView.  In order to switch the chat bubble from a left to a right chat, the imageSource is changed using JavaScript that checks to see if there is a “Reply to ID”.  This check is also used to adjust the padding based on if the things need to be pushed to the left or to the right.


Container {
	id: cOutterChat
	preferredWidth: Qt.mainTab.screenWidth
	layout: DockLayout {}
	leftPadding: (ListItemData.inReplyTo == "0") ? 15.0 : 40.0
	rightPadding: (ListItemData.inReplyTo == "0") ? 40.0 : 15.0
	ImageView {
		imageSource: (ListItemData.inReplyTo == "0") ?  "asset:///images/lbbmborder.amd" : "asset:///images/bbmborder.amd"
		horizontalAlignment: HorizontalAlignment.Fill
		verticalAlignment: VerticalAlignment.Fill
	}
	Container {
		id: cInnerBubble
		leftPadding: 20.0
		topPadding: 20.0
		rightPadding: 20.0
		bottomPadding: 30.0
		Label {
			minHeight: (Math.round(String(ListItemData.comment.toString()).length / 55) + 1) * 40
			text: ListItemData.comment
			textStyle {
				color: Color.White
				fontFamily: "Slate Pro"
				fontSize:  FontSize.Small
				textAlign: TextAlign.Left
			}
			preferredWidth: cOutterChat.preferredWidth - 100.0
			textFormat: TextFormat.Auto
			multiline: true
		}
	}
}

The Label has to expand in order to accommodate for multiple lines of text. Since we don’t want to use the same height regardless of the number of lines of text, we simply just calculate the number of lines and adjust the minimumHieght.  The calculation used is based on a FontSize.Small of a device with the default font.  This will not work if someone has increased their font size on their device.  This can be resolved in OS 10.2.1 with the minimum and maximum Font size restrictions, which basically lock out any device settings made by the user.

BBM_chat_ListView

Finally, to make everything work, we add the Container to a ListView that is populated from the comment data.  I populate the data in the c++ code by using a findChild for the ListView.  You can populate the ListView anyway you like, just as long as the chats are in the correct order.  And, you can change the chat bubbles, I made them match my app.

	ListView* tlvMeetupChat = Application::instance()->findChild<ListView*>("lvMeetupChat");
	qDebug() << "Setting Datamodel for lvMeetupChat";
	tlvMeetupChat->setDataModel(returnGDM);

Here is a comparison of what the Comments look like on the Meetup webpage vs the ListView

BBM Chat vs Meetup Chat

Here is the complete code for the ListView

ListView {
	id: lvMeetupChat
	objectName: "lvMeetupChat"
	listItemComponents: ListItemComponent {
		type: "item"
		Container {
			id: cMain
			layout: StackLayout {

			}
			preferredWidth: Qt.mainTab.screenWidth
			preferredHeight: 130;
			topMargin: 10.0
			bottomMargin: 10.0
			Container {
				id: cOutterChat
				preferredWidth: Qt.mainTab.screenWidth
				layout: DockLayout {}
				leftPadding: (ListItemData.inReplyTo == "0") ? 15.0 : 40.0
				rightPadding: (ListItemData.inReplyTo == "0") ? 40.0 : 15.0
				ImageView {
					imageSource: (ListItemData.inReplyTo == "0") ?  "asset:///images/lbbmborder.amd" : "asset:///images/bbmborder.amd"
					horizontalAlignment: HorizontalAlignment.Fill
					verticalAlignment: VerticalAlignment.Fill
				}
				Container {
					id: cInnerBubble
					leftPadding: 20.0
					topPadding: 20.0
					rightPadding: 20.0
					bottomPadding: 30.0
					Label {
						minHeight: (Math.round(String(ListItemData.comment.toString()).length / 55) + 1) * 40
						text: ListItemData.comment
						textStyle {
							color: Color.White
							fontFamily: "Slate Pro"
							fontSize:  FontSize.Small
							textAlign: TextAlign.Left
						}
						preferredWidth: cOutterChat.preferredWidth - 100.0
						textFormat: TextFormat.Auto
						multiline: true
					}
				}
			}
			Label {
				text: ListItemData.memberName
				textStyle.color: Color.White
				preferredWidth: cMain.preferredWidth - 30.0
				multiline: false
				textStyle.fontFamily: "Slate Pro"
				textStyle.fontSize: FontSize.XSmall
				rightMargin: 10.0
				textStyle.textAlign: (ListItemData.inReplyTo == "0") ? TextAlign.Left : TextAlign.Right
				translationX: (ListItemData.inReplyTo == "0") ? 15.0 : 5.0
			}
		}
	}
}

About DeanLogic
Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.

About DeanLogic

Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.