I was wondering how the people who develop for here BlackBerry go about managing the screens in their app. The most common practice (and the one I'm using) seems to be just to instantiate and push the new screen from the current one. The other option I've seen is using actions in the Main Application class to do the transitions. How do you guys manage?
We have a ScreenManager class manages the display of screens. It contains a Hashmap which has Screen name -> MainScreen pairs,public methods for adding and showing a screen.
When our application starts up all the screens required are created and added to the ScreenManager class.
In the showScreen() method we get the reference to the appropriate MainScreen class. Then we use UiApplication.getUiApplication().popScreen(screen) to hide the current screen. If the screen has already been shown we simply use popScreen() to remove screens until we reach the screen we want. Otherwise we just pushScreen() to move the screen to the top of the pile.
Calls to using the UiApplication are contained within a synchronized(UiApplication.getEventLock()) block
This approach does the job for us. We can create all the screens once at the application startup so it does not need to be done over and over again during the course of the application.
I also just use the above method of pushing to the screen-stack however an addition to this I also pass references of screens within my stack as parameter to new screens added.
If you have any public methods (which might for example update the contents of the screen etc) these can be called from other screens within your stack using this kind of reference. E.g.
Screen 1
public class MyScreen1 extends MainScreen
{
private LabelField content;
public MyScreen1(){
content = new LabelField(“this is the original content”);
add(content);
}
public void UpdateScreen(String newContent){
content.setText(newContent);
}
private void PushScreen{
MyScreen2 screen = new MyScreen2( (MyScreen1)UiApplication.getUiApplication().getActiveScreen());
UiApplication.getUiApplication().pushScreen(screen);
}
}
Screen 2
public class MyScreen2 extends MainScreen
{
private MyScreen1 originalScreen
public MyScreen2(MyScreen1 originalScreen){
this.originalScreen = originalScreen
public MyScreen1 () {
LabelField content = new LabelField(“Screen1 will now be changed.”);
add(content);
UpdateScreen1();
}
private void UpdateScreen1(){
originalScreen.UpdateScreen(“This is new content”);
}
}
In this example, once MyScreen2 has been popped, the content of MyScreen1 will have changed. This is useful if you have a scenario where you display details of an object then push an edit screen for the object which would pop back to an older version of the object.
Related
I have a custom view which previously was a Grid but I have changed its class inheritance to a View as this to me seems like the correct thing to do based on what I have read online.
This custom view has content inside of it. (A grid Which has content inside of itself)
This custom View has a handler in the native code.
Then each project has its own version of the handler, where I can handle the mapping methods.
I am adding the content from my custom view to the platform view using a ContentMapper (Inside the native iOS handler)
public static void MapContent(MyHandler handler, MyView view)
{
if (view.Content == null)
return;
handler.PlatformView.AddSubview(view.Content.ToPlatform(handler.MauiContext));
}
And inside of CreatePlatformView() (Native iOS project) I currently have.
UIView uIView = new UIView();
uIView.BackgroundColor = UIColor.Yellow;
return uIView;
But I can't see any of my content, I can however see the yellow background takes up the whole page.
I Have tried doing this inside of the CreatePlatformView()
VirtualView.ToPlatform(VirtualView.Handler.MauiContext) But one it doesn't work and two I don't think that should work anyway.
I could be doing it all wrong I am unsure. If needed I can create a demo project and upload it to GitHub.
My MvvmCross app uses a custom presenter that creates a SplitView when run on an Ipad. Both master and detail contain a navigation controller. This works fine except that I don't know how to hint the system where I want the next view to show.
I have a couple of views that sometimes should be shown in the detail view and sometimes in the master. If run on an iPhone they will be shown in the single navigation controller.
So in the ViewModel I would like to hint where to put the next view. Something like
ShowViewModel(paramObject, ShowInMaster);
If run on an iPhone the ShowInMaster will be ignored.
Is this possible or am I perhaps doing this all wrong?
There's an optional presentationBundle parameter you can use in most of the ShowViewModel overrides - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNavigatingObject.cs#L39
You can create a bundle simply from a Dictionary<string,string>() - e.g. you could use new MvxBundle(new Dictionary<string,string>() { { "ShowSplit":"true" } })
When used, this presentation bundle will get placed into the MvxViewModelRequest - in the public IDictionary<string, string> PresentationValues { get; set; } member - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross/ViewModels/MvxViewModelRequest.cs#L33
The request will then get passed to your UI presenter (aka the 'navigation service' in other frameworks) - and your custom code in the presenters on each platform can then decide what to do with these 'presentation' hints - e.g. it can override public override void Show(MvxViewModelRequest request) to inspect the presentation hint contents and to then do some custom split view display (see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Touch/Views/Presenters/MvxTouchViewPresenter.cs#L45 for the default behaviour)
If it helps, a simple split view display (using fixed logic rather than presentation hints) is in N=24 of http://mvvmcross.blogspot.com
I want to show a .gif banner in the top of my app. I'm using this code to show the animated field BB Animated GIF Field. The issue is that I want to refresh this banner every minute. I've tried so many things:
create a new object, but this doesn't change the banner.
create a new animated field and try to replace it....IllegalArgumentException. (I'm trying to change that from inside a Thread, using invokeLater()...I've used invokeAndWait() too)
remove this animated field and put a new one (from the invokeLater() or invokeAndWait() -> IllegalException)
setting a bitmap to this field. The first animate doesn't show and I can see an image from the other banner but it isn't animated.
Any ideas?
If you need to see some code, I will try to post it tomorrow.
If you're using a minimum BlackBerry OS of 6.0 (or above), the BitmapField class directly supports animated gifs.
If you need to support a lower OS version, then you just need to add a method to your AnimatedGIFField class to swap out the old image, and use a new one:
public void setImage(EncodedImage image) {
// Call BitmapField#setImage()
super.setImage(image);
// Store the image and its dimensions.
_image = image;
_width = image.getWidth();
_height = image.getHeight();
_currentFrame = 0;
// Stop the previous thread.
_animatorThread.stop();
// Start a new animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
}
Note that this is a UI operation. So, if you want to update the image from a background thread, make sure to wrap it with a call that puts it on the UI thread. For example:
final EncodedImage eImage = EncodedImage.getEncodedImageResource("img/banner.gif");
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
_animatedBanner.setImage(eImage);
}
});
Update: I also notice that the way the AnimatedGIFField class works is not really good multi-threading practice (for stopping the thread). If you really want to make the code better, you could implement this solution, or
use the technique I show in this answer.
Read more about this here.
void invalidate()
Invalidates the entire screen.
void invalidateLayout()
Invalidates this screen's layout (including all controlled fields).
The javadoc doesn't help much. Could you tell me why do we need invalidateLayout()?
Depending on how you build your UI, you may be interested in invalidating, or updating layout, when you want the display to change.
If you have a UI that's composed of standard Field objects, like TextField, or ButtonField, or BrowserField, without much customization, then the work of defining how the UI looks is mostly in how those fields are laid out. Layout refers to setting the size of fields, and their position.
Sometimes, you'll have a UI where you need to deal at a lower level. For example, you may do some custom painting, which involves drawing lines, filling areas, drawing gradients, etc. This work is normally done in a Field object's paint() method. Sometimes, you also have drawing code in other places like drawFocus() or paintBackground().
If you want to trigger repainting, which calls your custom paint() method, then you want to use invalidate() to make that happen.
If you want to trigger a new layout, which arranges child fields with certain sizes, or positions, then invalidateLayout() can be used for that.
However, I will note that invalidateLayout() is a method in the Screen class. If you use a RIM Screen subclass, like MainScreen or FullScreen, then they come with their own top level delegate Manager objects. I believe that calling Screen.invalidateLayout() will trigger the sublayout() method on that built-in Manager to be called. That may not be what you really want.
Unless you implement your own Screen subclass like this:
public class MyScreen extends Screen {
public MyScreen() {
super(new MyCustomManager());
}
}
you probably aren't implementing the code (including sublayout()) for that delegate manager yourself.
I have found that when I want to force sublayout() to be called again, I trigger that with the updateLayout() method, not invalidateLayout(). Also, as opposed to invalidateLayout(), which is a method of the Screen, updateLayout is available for all Manager and Field classes, so you can choose just the specific part of the UI element tree that you want to refresh. But, that's just my experience.
Here's some comments on the BlackBerry forums on this topic
I have three buttons in on screen,when i click each button it navigate to corresponding button contends.i need to change part of the screen without changing whole screen.Can anyone give an idea about this.
Thank You
Besides Ray's answer I think you should look to replace method:
vfm.replace(oldField, newField);
On a thread:
public void run() { //this is your Runnable for your Thread
//do stuff
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
vfm.replace(oldField, newField);
}
});}
Generally speaking if you modify a manager you should invalidate that manager which is the way to tell the device a repaint is required.
http://www.blackberry.com/developers/docs/4.0.2api/net/rim/device/api/ui/Manager.html#invalidate()
Edit: information about adding to/deleting from VerticalFieldManager
The documentation specific to a VerticalFieldManager is here:
http://www.blackberry.com/developers/docs/3.7api/net/rim/device/api/ui/container/VerticalFieldManager.html
The methods you should use to update its child fields are all inherited from the Manager class. You can also see a list of other subclasses here:
http://www.blackberry.com/developers/docs/3.7api/net/rim/device/api/ui/Manager.html
E.g. Check the methods for delete, deleteAll, deleteRange, insert, add.