I am trying to make a custom control for the BlackBerry Storm using SDK v5.0.
This control needs to disable scrolling while the user is dragging elements within a field. The problem is that even if I my control consumes every single touch event send to it, when the user lifts their finger off the screen it still flings up or down as if its finishing a scroll action.
Does anyone know of a way to prevent this from happening or what I might be doing wrong ?
Thank you.
I guess I should have posted this earlier.
There seems to be a bug in the MainScreen class which prevents setScrollingInertial(false) from having any effect. To get around this issue and solve the problem I did the folloing:
public static void main(String[] args)
{
MainScreen ms = new MainScreen(NO_HORIZONTAL_SCROLL | NO_VERTICAL_SCROLL);
VerticalFieldManager vfm = VerticalFieldManager()
{
public VerticalFieldManager()
{
super(HORIZONTAL_SCROLL | VERTICAL_SCROLL);
setScrollingInertial(true);
}
protected boolean touchEvent(TouchEvent message)
{
int code = message.getEvent();
boolean result = super.touchEvent(message);
if(code == TouchEvent.DOWN)
{
setScrollingInertial(!result);
}
return result;
}
};
ms.add(vfm);
}
I have had problem with moving the finger off the screen and I can't say I found a solution for that. On the other hand, have you tried changing the scrolling property of the manager or the screen (or the screen itself) with NO_SCROLL?
Related
I am working on a blackberry app where one of the requirements is that a ButtonField has to be displayed on all the screens of the app? How can this be accomplished, since the ButtonField has to be added after 2-3 controls?
Toolbar
Logo
ButtonField (All the screens should have this button)
There are many, many ways to solve this problem. Without seeing a visual description of all your screens, it's a little difficult to know exactly which one will work best for you. But, here's one option:
Create a base class that extends MainScreen, and have that base class add the ButtonField, and make sure that all other fields are added above the buttonfield. You can do this by adding the button field in a footer, that is then aligned with the screen's bottom edge using MainScreen#setStatus(Field).
public class BaseScreen extends MainScreen {
private ButtonField _bottomButton;
/** Can only be called by screen subclasses */
protected BaseScreen() {
// call BaseScreen(long)
this(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
}
protected BaseScreen(long style) {
super(style);
_bottomButton = new ButtonField("Press Me!", ButtonField.CONSUME_CLICK | Field.FIELD_HCENTER);
// TODO: customize your button here ...
Manager footer = new HorizontalFieldManager(Field.USE_ALL_WIDTH);
// just use a vertical field manager to center the bottom button horizontally
Manager spacerVfm = new VerticalFieldManager(Field.USE_ALL_WIDTH | Field.FIELD_HCENTER);
spacerVfm.add(_bottomButton);
footer.add(spacerVfm);
setStatus(footer);
}
/** #return the bottom button, if any subclasses need to access it */
protected ButtonField getBottomButton() {
return _bottomButton;
}
}
Here is then an example of how you'll build all your other screens:
public class BaseTestScreen extends BaseScreen implements FieldChangeListener {
public BaseTestScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
HorizontalFieldManager toolbar = new HorizontalFieldManager();
toolbar.add(new ButtonField("One", ButtonField.CONSUME_CLICK));
toolbar.add(new ButtonField("Two", ButtonField.CONSUME_CLICK));
toolbar.add(new ButtonField("Three", ButtonField.CONSUME_CLICK));
add(toolbar);
BitmapField logo = new BitmapField(Bitmap.getBitmapResource("icon.png"));
add(logo);
// do this if you want each different screen to be able to handle the
// bottom button click event. not necessary ... you can choose to
// handle the click in the BaseScreen class itself.
getBottomButton().setChangeListener(this);
}
public void fieldChanged(Field field, int context) {
if (field == getBottomButton()) {
Dialog.alert("Button Clicked!");
}
}
}
As you can see, this makes it possible to handle the button click (differently) in each screen class you create. Or, you can choose to handle the clicks in the base class (BaseScreen). You'll have to decide which makes sense for you. If the same action is always taken when clicking the button, and the base screen doesn't need additional information to handle the click, then just handle the click in BaseScreen. If not, handle in the subclasses as I show.
P.S. One disadvantage of this approach is that it forces all your screen classes to extend a common base class (BaseScreen). In some situations, this can be limiting. However, on BlackBerry, it's not uncommon to have all your screens extend MainScreen anyway. That said, such an architectural decision is impossible for me to comment on without understanding more about your app.
In my Application, I want to show the screens according to the Orientation. I knew, Whenever the device is Oriented then sublayout() method of current screen is called. According that point of view I write like this:
Here StartUp is another className;
This is my LoadingScreen.java Class;
public class LoadingScreen extends MainScreen
{
public LoadingScreen()
{
createGUI();
}
protected void sublayout(int width, int height)
{
StartUp.screenOrientation();
if(StartUp.isLandscape)
{
deleteAll();
createGUI();
invalidate();
}
else
{
deleteAll();
orientGUI();
invalidate();
}
super.sublayout(width,height);
}
public void createGUI()
{
//For LANDSCAPE Display;
}
public void orientGUI()
{
//For PORTRAIT Display;
}
}
The screenOrientation() method is this:
public static void screenOrientation()
{
if(Display.getOrientation()==Display.ORIENTATION_LANDSCAPE)
{
isLandscape=true;
width=480;
height=360;
}
else
{
isLandscape=false;
width=360;
height=480;
}
}
I am getting the Screen in both Orientation Modes. My problem is, if I take one textboxField and enter something in LANDSCAPE Mode and Oriented to PORTRAIT Mode then the data is gone. Because I am calling createGUI(); or orientGUI(); methods in sublayout().
One more problem that I am getting in Touch screen is:
If I want to enter text in any textboxField It opens the "keypad". Its Not a problem. The main thing is whenever the keypad open/hide both times again calling the sublayout() of that class. So I am getting the refreshed screen.
So, is there any other method to get Orientation? If it is then help me.
To solve the first issue, you have two options.
Dont delete the fields, just redraw the screen for the different
orientation.
or
Save the value of textboxfield before you delete the field and
after orientation change, set the value of textboxfield with the
saved value. You will need to even get the cursor position and set
that as well.
To solve the second issue
After orientation change, set a new variable "currentWidth" as Display.getWidth() . Then when you get a call to sublayout() check the Display.getWidth() and see if its equal to currentWidth. If its equal that means the orientation hasn't changed. If not then the orientation has changed.
hope this helps
Hello all i stuck at one problem. I have implemented a ListField in one Screen. On top of the Screen i have used one HorizontalFieldManager to hold a TitleLabel and Two Butons. I have pushed some screen on all the rows of the listfield. My problem is, letSuppose when i am on 4th row and i have selected what i want then when i clicking on the button, then the button worked but the screen which i have implemented at 4th row also appear how to avoid it. I am testing it on storm 9550 simulator and using Blackberry eclipse plugin5.0 . I am running out of idea please help me .
navigation click is like this
protected boolean navigationClick(int status, int time) {
selectListAndButton();
return true;
}
//And here is the selectListAndButton Method
private selectListAndButton(){
Field field = getFieldWithFocus().getLeafFieldWithFocus();
if(field == backCustomButton){
//Popped the active Screen
}else if(field == saveCustomButton){
//Saving some Data to the Database And pushing another Screen here
// problem comes here if i am at 2nd row of the listfield and selects
something from there and clicked on the button the screen which was
implemented at 2nd row also appears
}
else if (_list.getSelectedIndex() == 0){
//Called a date picker
}
else if (_list.getSelectedIndex() == 1){
//Pushed some another screen
}
else if (_list.getSelectedIndex() == ){
//Pushed some another screen
}
You need to override onTouch for Screen and call specific functionality depend on event coordinates and Field boundaries:
protected boolean touchEvent(TouchEvent message) {
if (isTouchInside(message, saveCustomButton)) {
save();
} else {
showNextScreen();
}
}
private boolean isTouchInside(TouchEvent messages, Field field) {
int eventCode = message.getEvent();
int touchX = message.getX(1);
int touchY = message.getY(1);
XYRect rect = field.getContentRect();
return rect.contains(touchX, touchY);
}
I had solved it. but posting the answer late. I just dropped the idea of using ListField because i dont have so much rows to add. So i have just used the little trick rather using ListField i have used HorizontalFieldManager and its look like a List so everything working fine.
Thanks cheers :)
In my BlackBerry application, I have a home screen. The user can then navigate to a settings screen. When the user goes back to the home screen, is there no method that is called on the home screen indicating that the screen has come to the foreground?
I have tried onFocus() with no avail.
Thanks!
Unfortunately, hooking on the onExposed is not enough. I found that in Blackberry dialogs are also screens and even context menus are screens too. They are pushed on top of your screen so you receive onExposed callback when they are dismissed.
Though it's OK in many cases, in other cases it poses a problem - e.g. if I must refresh the screen's content only when the user returns to it, but not after menus/dialogs, then how do I do that? My case is, unfortunately, one of those.
I found no documented way of detecting "covered"/"uncovered" events. Here is my approach. onCovered/onUncovered callbacks are called when the current screen is covered/uncovered by another screen of the app, but not by dialogs/menus/virtual keyboard:
public class MyAppScreen extends MainScreen {
private boolean isCovered;
protected void onExposed() {
Log.d("onExposed");
super.onExposed();
if (isCovered) {
onUncovered();
isCovered = false;
}
}
protected void onObscured() {
Log.d("onObscured");
super.onObscured();
final Screen above = getScreenAbove();
if (above != null) {
if (isMyAppScreen(above)) {
isCovered = true;
onCovered();
}
}
}
private boolean isMyAppScreen(final Screen above) {
return (above instanceof MyAppScreen);
}
protected void onUncovered() {
Log.d("onUncovered");
}
protected void onCovered() {
Log.d("onCovered");
}
protected void onUiEngineAttached(final boolean attached) {
if (attached) {
Log.d("UI Engine ATTACHED");
} else {
Log.d("UI Engine DETACHED");
}
super.onUiEngineAttached(attached);
}
protected void onFocusNotify(final boolean focus) {
if(focus){
Log.d("focus GAINED");
} else {
Log.d("focus LOST");
}
super.onFocusNotify(focus);
}
}
And a test. Try various combinations and see what events you receive in the log.
public class TestLifecycle extends MyAppScreen implements FieldChangeListener {
private final ABNTextEdit txt1;
private final ButtonField btn1;
private final ButtonField btn2;
public TestLifecycle() {
final Manager manager = getMainManager();
txt1 = new ABNTextEdit();
manager.add(txt1);
btn1 = new ButtonField("Dialog", ButtonField.CONSUME_CLICK);
btn1.setChangeListener(this);
manager.add(btn1);
btn2 = new ButtonField("Screen", ButtonField.CONSUME_CLICK);
btn2.setChangeListener(this);
manager.add(btn2);
}
public void fieldChanged(final Field field, final int context) {
if (field == btn1) {
Dialog.alert("Example alert");
} else if (field == btn2) {
UiApplication.getUiApplication().pushScreen(new TestLifecycle());
}
}
}
Update:
This method has a limitation: if a new screen is pushed when a dialog or the soft keyboard has focus your current screen will not receive onCovered/onUncovered notification.
Example A: if you have an input field of fixed size and you push a new screen when the user completes it, your current screen will not receive the notification if the user types very quickly. This happens because in the moment between you call push(newScreen) and it is actually pushed the user clicks on a letter on soft KB and it grabs the focus. So only onObscured is called, but not onCovered.
Solution: explicitly hide the soft keyboard before the push(newScreen).
Example B: if you have a customized dialog which pushes new screen and then dismisses itself, your current screen will not receive the notification. This happens because your customized dialog is not recognized as a screen, so only onObscured is called, but not onCovered.
Solution: dismiss the dialog in the first place returning a result value, and let your screen push the new screen based on that value. -OR- override isMyAppScreen() to return true also for your customized dialog.
You should be able to use protected void onExposed() to detect when it is displayed again.
I am trying to display a Custom PopupScreen and when the virtual keyboard is being displayed it reduces the size of the popup. I know when you for example, select new message you get a PopupScreen that allows you to select message type (sms, email, etc) and it shows on top of the virtual keyboard. Here is my code am I missing something? I can't find a z-index or something similar...
public class InsertApplicationMenuItem extends ApplicationMenuItem {
public Object run(Object context) {
InsertWhatScreen screen = new InsertWhatScreen();
UiApplication.getUiApplication().pushModalScreen(screen);
return context;
}
}
public class InsertWhatScreen extends PopupScreen {
public InsertWhatScreen() {
super(new VerticalFieldManager(), FOCUSABLE);
}
}
alt text http://dl.dropbox.com/u/2645315/2010-01-20%2015%2017%2023.png
Thanks for the help.
There's no way to put anything on top of the virtual keyboard from a third-party app. If you read the display height while the virtual keyboard is showing, you'll see that the device actually shrinks the "screen size" given to your app while the keyboard is showing.