I'm currently trying to create a custom navigation-bar for my navigation-controller in iOS using MonoTouch. The application I'm developing has the need to have a single string available on-screen no matter where you are on the application, and I wan't to achieve something like this (I know this is a little out off proportion, but you should be able to get the point):
+-----------------------------------------+
| /---------| +---+ |
| / Back | Controller Name |btn| |
| \---------| +---+ |
+-----------------------------------------+
| Current building <- global string |
+-----------------------------------------+
The bottom bar should be as small as possible (while not being hard to read), and the original navigation-bar might need to be a tiny bit smaller than normal.
Also, I've tried not to use the designer, but to write all the UI-code myself, but if that is impossible to achieve this, then I'll off cause have to use the designer. Currently I've found a project for creating a custom UINavigationBar in MonoTouch, but I have no idea of how to apply that to my NavigationController (see as the NavigationBar-property is read-only). The project I was talking about can be found here: https://github.com/mafis/Monotouch-Custom-Control/tree/master/CustomControls. Also, I would like the design of the actual navigationbar (the top part) to be standard iOS design, and work as a navigationbar normally would.
Any hints, or pointers at how to do this would be appreciated.
This is how I ended up solving this problem. I subclassed UIViewController like this:
using System;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
using System.Drawing;
using System.Collections.Generic;
using FdvWeb.Core;
namespace FdvWeb
{
[MonoTouch.Foundation.Preserve(AllMembers=true)]
public class MainNavigationController : UINavigationController
{
private const float NAV_BAR_HEIGHT = 44;
private readonly float buildingBarTop = 44;
private readonly float buildingBarHeight = 17;
private readonly float viewOffset;
private readonly HashSet<UIViewController> modifiedViewControllers = new HashSet<UIViewController>();
UITextView buildingTextView;
[MonoTouch.Foundation.Preserve]
public MainNavigationController ()
{
viewOffset = buildingBarTop + buildingBarHeight - NAV_BAR_HEIGHT;
var tt = new UITextView (new RectangleF (0, buildingBarTop, 320, buildingBarHeight));
NavigationBarHidden = false;
NavigationBar.AddSubview (tt);
tt.Font = UIFont.BoldSystemFontOfSize (12);
tt.TextAlignment = UITextAlignment.Center;
tt.TextColor = UIColor.LightTextColor;
tt.BackgroundColor = UIColor.ViewFlipsideBackgroundColor;
tt.Editable = false;
tt.ContentInset = new UIEdgeInsets (-9, 0, 0, 0);
buildingTextView = tt;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public string BuildingText
{
get {
return buildingTextView.Text;
}
set {
container.Resolve<IUIThreadDispatcher> ().DispatchOnUIThread (delegate { // Run on UI thread
buildingTextView.Text = value;
});
}
}
public override void PushViewController (UIViewController viewController, bool animated)
{
if (!modifiedViewControllers.Contains (viewController))
{
viewController.View = new PaddedView (viewController.View, new InnsetF (0, viewOffset, 0, 0));
modifiedViewControllers.Add (viewController);
}
viewController.NavigationItem.RightBarButtonItem = pickBuildingItem;
base.PushViewController (viewController, animated);
}
private class PaddedView : UIView
{
private UIView view;
private InnsetF innsets;
public PaddedView (UIView view, InnsetF innsets)
: base(view.Frame)
{
this.view = view;
this.innsets = innsets;
this.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
this.AddSubview (view);
}
public override void LayoutSubviews ()
{
//apply the insets to the subview
view.Frame = new RectangleF (innsets.Left, innsets.Top,
Frame.Size.Width - innsets.Left - innsets.Right,
Frame.Size.Height - innsets.Top - innsets.Bottom);
}
}
private class InnsetF
{
private float top, bottom, left, right;
public InnsetF (float left, float top, float right, float bottom)
{
this.top = top;
this.left = left;
this.bottom = bottom;
this.right = right;
}
public float Top
{
get { return top; }
set { top = value; }
}
public float Bottom
{
get { return bottom; }
set { bottom = value; }
}
public float Right
{
get { return right; }
set { right = value; }
}
public float Left
{
get { return left; }
set { left = value; }
}
}
}
}
So you want the reverse of what Spotify does when it is offline (the string message you're referring to appears above the UINavigationController).
I don't think the approach you're taking is going to work - as far as I know, the NavigationBar can only be 44 pixels high. Could you take a similar approach to Spotify and move it above the NavigationBar? This way you could leave the NavigationController etc as is and simply make it's view smaller, then add the string to the window object - that way it will persist as you navigate through your application.
Related
I'm new at Xamarin development and I'm trying to display the list of comments for a specific ticket. The comments' body have different text size. I have a custom view TicketCommentRowUIView.xib:
using Foundation;
using System;
using UIKit;
using ObjCRuntime;
namespace Project.iOS
{
public partial class TicketCommentRowUIView : UIView
{
public TicketCommentRowUIView(IntPtr handle) : base(handle)
{
}
public static TicketCommentRowUIView CreateView(TicketCommentModel model)
{
TicketCommentRowUIView v = CreateView();
v.LoadModelInfo(model);
return v;
}
private static TicketCommentRowUIView CreateView()
{
var arr = NSBundle.MainBundle.LoadNib("TicketCommentRowUIView", null, null);
var v = Runtime.GetNSObject<TicketCommentRowUIView>(arr.ValueAt(0));
return v;
}
private void LoadModelInfo(TicketCommentModel model)
{
DateReply.Text = model.CreatedAt.ToShortDateString();
MessageReply.Text = model.Content;
ReplierImage.Image = UIImage.FromBundle("default_image_user");
}
}
}
And I'm adding that custom view to an stack view in TicketDetailViewController dinamically, but I don't know how to resize the custom view according its self size:
using Foundation;
using System;
using UIKit;
using System.Collections.Generic;
using System.Linq;
using CoreGraphics;
namespace Project.iOS
{
public partial class TicketDetailViewController : UIViewController
{
private nfloat TicketItemHeight = 200;
TicketModel _ticketItem;
List<TicketCommentModel> _ticketCommentsList;
TicketDetailPresenter _presenter;
public TicketDetailViewController (IntPtr handle) : base (handle)
{
_presenter = new TicketDetailPresenter(this);
}
public async override void ViewDidLoad()
{
base.ViewDidLoad();
ScrollView.LoadingStart();
await _presenter.LoadTicketComments(_ticketItem.Id);
FillComentsStackView();
ScrollView.LoadingComplete();
}
public void LoadMenuItem(TicketModel ticketItem)
{
_ticketItem = ticketItem;
}
public void FillComentsStackView()
{
foreach (TicketCommentModel item in _ticketCommentsList)
{
TicketCommentRowUIView itemView = TicketCommentRowUIView.CreateView(item);
itemView.HeightAnchor.ConstraintEqualTo(TicketItemHeight).Active = true;
CommentStackView.AddArrangedSubview(itemView);
}
}
}
}
My issue is that I don't know how to add an self size for each custom view, because the height of MessageReply UILabel it is variable... I put a constraint equals to 200 but when the text is too big it looks overlapped. And when it is a short text there is to much of blank space. Is there any way to do this?? In my android project I just set WrapContent layout params and it worked. But I can't find the way to apply that to the iOS project.
It seems you have set the Label's Line 0 to wrap its content.
Then you can set each item's constraint in your xib to make your custom view self size fit. From your screenshot you can set the left, top constraint of your image and top, left constraint of your DateReply, at last the left, top, right , bottom constraint of the MessageReply like:
Adjust the constant to feed your request. Moreover when you add the custom view in the TicketDetailViewController, there's no need to set the HeightAnchor.
I got a UIViewController that init a UIView.
This view containing Interactive Elements like UITextField or UIButton.
View is added on ViewDidLoad, at the bottom of the method to be sure that when I made it visible it can be reach by user interaction.
But when I show the view, no interaction can be done on this View.
Is this only possible? Am I doing something wrong?
The View
public class AddBusinessEventView : UIView
{
public UILabel LblTitle;
public UITextField TxtType;
public UIButton BtnClose;
public AddBusinessEventView(nfloat bw, nfloat bh)
{
//Bouton pour fermer le popup
BtnClose = new UIButton();
BtnClose.SetImage(UIImage.FromFile("Images/Boutons/lightbox_close.png"), UIControlState.Normal);
BtnClose.Frame = new CGRect(bw - 80, 30, BtnFermer.ImageView.Image.CGImage.Width * 0.5, BtnFermer.ImageView.Image.CGImage.Height * 0.5);
//Doit se trouver par dessus txtSite et ajouté après dans la vue pour se trouvé en premier plan
LblTitle = new UILabel();
LblTitle.Frame = new CGRect((bw - (lw + 200)) / 2, 100, lw + 200, 30);
LblTitle.Text = "Fill with your event elements";
LblTitle.Font = UIFont.FromName("GillSans-Bold", 22);
LblTitle.TextColor = UIColor.FromRGB(211, 3, 67);
LblTitle.TextAlignment = UITextAlignment.Center;
TxtType = new UITextField(new CGRect((bw - 750) / 2, 140, 350, 40));
TxtType.BackgroundColor = UIColor.White;
TxtType.TextAlignment = UITextAlignment.Center;
TxtType.BorderStyle = UITextBorderStyle.RoundedRect;
TxtType.AutocorrectionType = UITextAutocorrectionType.No;
TxtType.AutocapitalizationType = UITextAutocapitalizationType.AllCharacters;
TxtType.Placeholder = "Type";
AddSubviews(BtnClose, LblTitle, TxtType);
}
}
The UIViewController
partial class EvenementViewController : EnhancedUIViewController
{
AddBusinessEventView AddBusinessEventView;
public EvenementViewController(IntPtr handle) : base(handle) { }
public EvenementViewController() : base() { }
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if (myEvent == null)
{
ShowAddBusinessEventView();
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
nfloat bw = View.Bounds.Width;
nfloat bh = View.Bounds.Height;
//Another Elements are adding to view here
//...
AddBusinessEventView = new AddBusinessEventView(bw, bh);
AddBusinessEventView.Hidden = true;
//Much more View.Add with all elements here
//...
View.Add(AddBusinessEventView);
AddBusinessEventView.BtnType.TouchUpInside += BtnClose_TouchUpInside;
}
#region BusinessEventAdd
void ShowAddBusinessEventView()
{
UIView.Animate(duration: 1,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animation: () =>
{
AddBusinessEventView.Alpha = 1.0f;
},
completion: () =>
{
AddBusinessEventView.Hidden = false;
AddBusinessEventListener();
}
);
}
void HideAddBusinessEventView()
{
UIView.Animate(duration: 1,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animation: () =>
{
AddBusinessEventView.Alpha = 0.0f;
},
completion: () =>
{
AddBusinessEventView.Hidden = true;
RemoveBusinessEventListener();
}
);
}
void BtnClose_TouchUpInside(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Touching myself");
}
#endregion
}
please concidere EnhancedViewController as standard UIViewController, I'm juste adding some enhancements to show users a message from an Overlay.
As I said, we can't interact neither with TxtType nor BtnClose.
Edit :
Don't sure if it can help; but when the View is added to the Main View on the UIViewController, it display well, but all user interaction are catches on element under this View
i.e : The AddBusinessEventView act as a popup, so it covered all other element, when i press an element, if another element adding prior of this View is under, it's this element rather than the AddBusinessEventView element that catch the touch event.
As said, the main purpose is to cut View element on different file for more readability and maintenability of this application.enter code here
There is no reason you can't do that.
You can architect your code like
UIViewController
Controller1
Controller2
...
Views
View1
View2
...
And then use View1 and View2 in any of Controller.
I tried your code and I got all the things popup correctly.
As you said, if view show up on screen but it's element from another View from below that interact with user maybe you can try to bring your View to front.
Try this
View.BringSubviewToFront(AddBusinessEventView);
At the end of your ViewDidLoad function.
Stuart's N-06 Books sample is good for getting basic understanding about using MvxSimpleTableViewSource.
[Register("FirstView")]
public class FirstView : MvxViewController
{
public override void ViewDidLoad()
{
View = new UIView(){ BackgroundColor = UIColor.White};
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var textField = new UITextField(new RectangleF(10, 10, 300, 40));
Add(textField);
var tableView = new UITableView(new RectangleF(0, 50, 320, 500), UITableViewStyle.Plain);
Add(tableView);
tableView.RowHeight = 88;
var source = new MvxSimpleTableViewSource(tableView, BookCell.Key, BookCell.Key);
tableView.Source = source;
var set = this.CreateBindingSet<FirstView, Core.ViewModels.FirstViewModel>();
set.Bind(textField).To(vm => vm.SearchTerm);
set.Bind(source).To(vm => vm.Results);
set.Apply();
tableView.ReloadData();
}
}
But how can resize the tableview's height according it's content once it loads data?
Not entirely sure what you want to do... Normally in an iOS UI, the tableview size is fixed regardless of its content.
However, if you did want to resize the table then you could:
Inherit from MvxTableViewSource or UITableView and provide some logic there
Or add a binding in your class to some View property TableCount, bind that property and then implement the sizing logic there. Something like:
set.Bind(this).For(v => v.TableCount).To(vm => vm.Results.Count);
private int _tableCount
public int TableCount {
get { return _tableCount; }
set {
// implement your sizing animations here (maybe animate constraints?)
}
}
Just to add to Stuart's answer, here is an example for the frame of the table:
int _tableHeight;
public int TableHeight
{
get { return _tableHeight; }
set
{
_tableHeight = value;
_myPlayers.Frame = _tableHeight > 0 ? new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * _tableHeight) : new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * BusinessConstants.GetBiggestPositionCount());
_myPlayers.ReloadData();
}
}
I need to be able to hide controls on a page that uses constraints and remove the empty space that Hidden=true leaves. It needs to be similar to how the web handles visibility. If it's invisible, it doesn't take up space.
Does anyone know of a clean way to accomplish this?
Please let me know if you need more details.
Thx
Example:
UIButton | UIButton | UIButton
"empty space for hidden UIButton"
UIButton
That should really be rendered like this:
UIButton | UIButton | UIButton
UIButton
Edit: I'm using Xamarin Studio and VS2012 for development.
Since original question is related to Xamarin, I provide complete C# solution.
First, create height constraint for your view and give it an identifier in Xcode Interface Builder:
Then in controller override ViewDidAppear() method and wrap view with HidingViewHolder:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
applePaymentViewHolder = new HidingViewHolder(ApplePaymentFormView, "ApplePaymentFormViewHeightConstraint");
}
It is important to create HidingViewHolder when view was laid out, so it has real height assigned.
To hide or show view you can use corresponding methods:
applePaymentViewHolder.HideView();
applePaymentViewHolder.ShowView();
HidingViewHolder source:
using System;
using System.Linq;
using UIKit;
/// <summary>
/// Helps to hide UIView and remove blank space occupied by invisible view
/// </summary>
public class HidingViewHolder
{
private readonly UIView view;
private readonly NSLayoutConstraint heightConstraint;
private nfloat viewHeight;
public HidingViewHolder(UIView view, string heightConstraintId)
{
this.view = view;
this.heightConstraint = view
.GetConstraintsAffectingLayout(UILayoutConstraintAxis.Vertical)
.SingleOrDefault(x => heightConstraintId == x.GetIdentifier());
this.viewHeight = heightConstraint != null ? heightConstraint.Constant : 0;
}
public void ShowView()
{
if (!view.Hidden)
{
return;
}
if (heightConstraint != null)
{
heightConstraint.Active = true;
heightConstraint.Constant = viewHeight;
}
view.Hidden = false;
}
public void HideView()
{
if (view.Hidden)
{
return;
}
if (heightConstraint != null)
{
viewHeight = heightConstraint.Constant;
heightConstraint.Active = true;
heightConstraint.Constant = 0;
}
view.Hidden = true;
}
}
In storyboard wire your constrains first. Then try this
self.viewToHideHeight.constant = 0;
self.lowerButtonHeightFromTop.constant = self.viewToHideHeightFromTop.constant + self.viewToHideHeight.constant;
[UIView animateWithDuration:0.5f animations:^{
self.viewToHide.alpha = 0.0f;
[self.view layoutIfNeeded];
}];
i wanna know, how to fit screen my tabulation bar on blackberry. because my tab is match with blackberry 9700 but for blackberry 9900, my tab is too small. i wanna my tab is fit to all device scree.
thanks in advance :)
this is the code, i got from other post. sorry:
BottomPanel class
public class BottomPanel extends VerticalFieldManager implements
FieldChangeListener {
Bitmap home_bit = Bitmap.getBitmapResource("home.png");
Bitmap home_bit_hover = Bitmap.getBitmapResource("home_h.png");
Bitmap map_bit = Bitmap.getBitmapResource("map.png");
Bitmap map_bit_hover = Bitmap.getBitmapResource("map_h.png");
Bitmap contact_bit = Bitmap.getBitmapResource("contact.png");
Bitmap contact_bit_hover = Bitmap.getBitmapResource("contact_h.png");
PictureBackgroundButtonField home_pic, map_pic, contact_pic;
HorizontalFieldManager hr;
int current_index = 0;
public BottomPanel(int current_index) {
super(FOCUSABLE);
this.current_index = current_index;
VerticalFieldManager ver = new VerticalFieldManager(USE_ALL_WIDTH
| USE_ALL_HEIGHT) {
protected void sublayout(int width, int height) {
super.sublayout(width, home_bit.getHeight());
setExtent(width, home_bit.getHeight());
}
};
hr = new HorizontalFieldManager(FIELD_HCENTER);
if (current_index == 1) {
home_pic = new PictureBackgroundButtonField(home_bit.getWidth(),
home_bit.getHeight(), Field.NON_FOCUSABLE
| Field.FIELD_VCENTER, home_bit_hover,
home_bit_hover);
} else {
home_pic = new PictureBackgroundButtonField(home_bit.getWidth(),
home_bit.getHeight(),
Field.FOCUSABLE | Field.FIELD_VCENTER, home_bit,
home_bit_hover);
}
home_pic.setChangeListener(this);
hr.add(home_pic);
if (current_index == 2) {
map_pic = new PictureBackgroundButtonField(map_bit.getWidth(),
map_bit.getHeight(), Field.NON_FOCUSABLE
| Field.FIELD_VCENTER, map_bit_hover, map_bit_hover);
} else {
map_pic = new PictureBackgroundButtonField(map_bit.getWidth(),
map_bit.getHeight(), Field.FOCUSABLE | Field.FIELD_VCENTER,
map_bit, map_bit_hover);
}
map_pic.setChangeListener(this);
hr.add(map_pic);
if (current_index == 3) {
contact_pic = new PictureBackgroundButtonField(
contact_bit.getWidth(), contact_bit.getHeight(),
Field.NON_FOCUSABLE | Field.FIELD_VCENTER,
contact_bit_hover, contact_bit_hover);
} else {
contact_pic = new PictureBackgroundButtonField(
contact_bit.getWidth(), contact_bit.getHeight(),
Field.FOCUSABLE | Field.FIELD_VCENTER, contact_bit,
contact_bit_hover);
}
contact_pic.setChangeListener(this);
hr.add(contact_pic);
ver.add(hr);
add(ver);
}
public void fieldChanged(Field field, int context) {
if (field == home_pic) {
LoadingScreen loadingScreen = new LoadingScreen(1);
UiApplication.getUiApplication().popScreen(
UiApplication.getUiApplication().getActiveScreen());
UiApplication.getUiApplication().pushScreen(loadingScreen);
loadingScreen.createGUI();
} else if (field == map_pic) {
LoadingScreen loadingScreen = new LoadingScreen(2);
UiApplication.getUiApplication().popScreen(
UiApplication.getUiApplication().getActiveScreen());
UiApplication.getUiApplication().pushScreen(loadingScreen);
loadingScreen.createGUI();
} else if (field == contact_pic) {
LoadingScreen loadingScreen = new LoadingScreen(3);
UiApplication.getUiApplication().popScreen(
UiApplication.getUiApplication().getActiveScreen());
UiApplication.getUiApplication().pushScreen(loadingScreen);
loadingScreen.createGUI();
}
}
Loading Screen class
public class LoadingScreen extends MainScreen {
private LabelField text;
private LabelField texthasil;
private VerticalFieldManager manager;
int current_index = 0;
BottomPanel bottomPanel;
public LoadingScreen(int current_index) {
this.current_index = current_index;
bottomPanel = new BottomPanel(current_index);
setStatus(bottomPanel);
}
public void createGUI() {
manager = new VerticalFieldManager(Manager.VERTICAL_SCROLL
| Manager.VERTICAL_SCROLLBAR);
setStatus(bottomPanel);
}
PictureBackgroundButtonField class
public class PictureBackgroundButtonField extends Field {
private String _label;
private int _labelHeight;
private int _labelWidth;
private Font _font;
private Bitmap _currentPicture;
private Bitmap _onPicture;
private Bitmap _offPicture;
public PictureBackgroundButtonField(int width, int height, long style,
Bitmap picture, Bitmap selectedPic) {
super(style);
_font = getFont();
_label = "";
_labelHeight = height;
_labelWidth = width;
_currentPicture = picture;
_onPicture = selectedPic;
_offPicture = picture;
}
protected void drawFocus(Graphics graphics, boolean on) {
// Do nothing
}
public int getPreferredHeight() {
return _labelHeight;
}
public int getPreferredWidth() {
return _labelWidth;
}
protected void layout(int width, int height) {
setExtent(getPreferredWidth(), getPreferredHeight());
}
protected boolean navigationClick(int status, int time) {
fieldChangeNotify(1);
return true;
}
protected void onFocus(int direction) {
_currentPicture = _onPicture;
invalidate();
}
protected void onUnfocus() {
_currentPicture = _offPicture;
invalidate();
}
protected void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, getPreferredWidth(), getPreferredHeight(),
_currentPicture, 0, 0);
graphics.setFont(_font);
graphics.drawText(
_label,
4,
2,
(int) (getStyle() & DrawStyle.ELLIPSIS | DrawStyle.HALIGN_MASK),
getWidth() - 6);
}
You don't show us what kind of tab bar background you have, and the solution does depend a little on that. If you are happy having a tab bar that is always the same height (in pixels), but just changes width, then you could use something like this.
I create a Manager subclass called TabBarManager. It will span the whole width of your screen, with a fixed height. It can have Field objects added to it like any normal manager. It is intended to have button fields added to it, so that when you click the button field, something happens. Probably, you'd also want the appearance of the button fields to change, depending on which tab is selected. However, it wasn't clear that this question was about that problem, so I didn't show that code. All this code does is give you a Manager to add tab fields to, that will draw a full-width background.
The tab bar fields that you add to this should contain icon images and/or labels, that have transparent backgrounds. For example, a white silhouette icon of a globe, if the tab is a map view. The transparent background shows through to the TabBarManager background.
The technique is to draw (in Photoshop, or whatever) three images. A left, right, and center image. Think of drawing a full tab bar image. Then, crop off the left few pixels, and save as TabBar-left.png. Crop the right few pixels and save as TabBar-right.png, and then crop a few pixels out of the center, and save as TabBar-center.png. Example images are shown below the code:
/**
* A TabBarManager provides a horizontal bar of button fields, that serve as a tab bar
* header or footer, used to select between available subviews in a larger Screen.
*/
private final class TabBarManager extends HorizontalFieldManager {
private int height;
private Bitmap left;
private Bitmap center;
private Bitmap right;
public TabBarManager() {
super(HorizontalFieldManager.NO_VERTICAL_SCROLL); // tab bar itself doesn't scroll
left = Bitmap.getBitmapResource("TabBar-left.png");
right = Bitmap.getBitmapResource("TabBar-right.png");
center = Bitmap.getBitmapResource("TabBar-center.png");
height = left.getHeight();
}
public void sublayout(int width, int h) {
super.sublayout(width, height);
setExtent(width, height); // restrict height to a fixed value
}
public int getPreferredHeight() {
return height;
}
public void paint(Graphics g) {
// draw the background image for the tab bar with two sides and a center section,
// to account for the fact that different devices have different widths
int width = Display.getWidth();
g.drawBitmap(0, 0, left.getWidth(), height, left, 0, 0);
// fill in the center by repeating the center image as many times as needed
int x = left.getWidth();
int centerWidth = center.getWidth();
int leftEdgeOfRightBitmap = width - right.getWidth();
while (x < leftEdgeOfRightBitmap) {
g.drawBitmap(x, 0, centerWidth, height, center, 0, 0);
x += centerWidth;
}
// draw right side
g.drawBitmap(leftEdgeOfRightBitmap, 0, right.getWidth(), height, right, 0, 0);
// use super.paint() to draw the icons/labels on top of our background
super.paint(g);
}
}
Left, center, and right PNGs (must be same height ... width doesn't matter):
, ,
How You Use It
In the code you show, you can either replace your hr variable with an instance of my TabBarManager. Or you can rename my TabBarManager class to BottomPanel, and add the additional code you need to it ... things like the current index, and the field change listener callback.
Limitations
The above implementation will only stretch the tab bar's width. The height is fixed. For a fully stretchable tab bar, you could either mimic a 9-patch image by drawing 9 images (top-left, top-center, top-right, left, center, right, bottom-left, bottom-center, bottom-right). Or use something like this to get 9-patch stretchable images for BlackBerry
References
http://supportforums.blackberry.com/t5/Java-Development/Create-tabbed-view-screens/ta-p/444969