I have HorizontalFieldManager,VerticalFieldManager and LabelField. LabelField placed in HorizontalFieldManager and multiple HorizontalFieldManager are placed in VerticalFieldManager.
When i try to get LabelField height using labelfield.getHeight(); it returns 0 . if there are multiple line in Labelfield, it also give me height returns 0. same issue i m facing for HorizontalFieldManager .
After getting there height i want to calculate VerticalFieldManager height and set height dynamically for the screen.
How can i calculate the height of Label or Horizontalfieldmanager?
Use labelField.getPreferredHeight() and manager.getPreferredHeight() not labelField.getHeight()
This should work for you
The method Farid suggests it's a bit difficult to use, because you will need labelWidth.
For multiline label this labelWidth may not be the same as parent managers available width, or some exact width you had set, because each line can have different width depending on if words did fit maxWidth or didn't.
NOTE: as Nate pointed out in comments, it's a good idea to also add advance for spaces.
I modified the code to include that.
Here is the method I use to overcome these problems:
public int getPreferredHeight() {
String text = getText();
int maxWidth = getManager().getPreferredWidth();
int spaceAdvance = getFont().getAdvance(' ');
String[] words = StringUtilities.stringToWords(text);
int lastWordAdvance = 0;
int lines = 1;
for (int i = 0; i < words.length; i++) {
int wordAdvance = getFont().getAdvance(words[i]) + spaceAdvance;
if (lastWordAdvance + wordAdvance < maxWidth ) {
lastWordAdvance += wordAdvance;
} else {
lines++;
lastWordAdvance = wordAdvance;
}
}
return (int)lines * getFont().getHeight();
}
If you can leave the decision about the size of the LabelField until after it has been laid out, then you will get it accurately from getHeight(). This means you can actually get the correct result, including factoring in any margin or padding for the LabelField, that I think
int maxWidth = getManager().getPreferredWidth();
will miss.
Initially this seems quite difficult, because typically it is good to know the height when you add the Field to the screen. But the general principle is that you should do this in the Manager's sublayout, so you are just moving the code that is dependent on the height, a bit later in the process. And this has the benefit that the layout is dynamic, so if the LabelField's text is changed, then layout will be invoked and your code that is dependent on the height gets re-invoked too.
It is also possible to use logic like this in sublayout():
super.sublayout(...);
if (myField.getHeight() < 100 ) {
myField.setMargin((100 - myField.getHeight())/2, 0, (100 - myField.getHeight())/2, 0);
super.sublayout(...);
}
This is not a production suitable example, hard coding a pixel height is not recommended. It is just an easy example to understand....
Related
In the case where the segments in a UISegmentedControl are proportional to content widthForSegmentAtIndex seems to return 0 when called from viewDidLayoutSubviews. Is there anyway to get the actual widths? (I am using autolayout.)
If not is there any way to get the subview that corresponds to a segment? segmentedControl.subviews[index] does not work reliably.
If there is no way to do that is there an alternative to UISegmentedControl that can be used?
Thank you!
From my testing it appears that the width of the segments will always be equal, regardless of the size of the text of each individual segment.
CGFloat segmentWidth = segmentedControl.frame.size.width / segmentedControl.numberOfSegments;
EDIT
Unfortunately, the above answer does not work if the segmentedControl is using apportionsSegmentWidthsByContent. I can't find an easy way to get the width of the segment in that case.
However, you could manually get the text of the segment you're interested in, then calculate how long it 'should' be (depending on how much padding you want on either side), then use that number and use the setWidth:forSegmentAtIndex: method to set the segment to the calculated width, then you would know for sure the width for the segment.
The following category gives NSSegmentedControl the ability to tell you its computed segment widths. It's not perfect- see the disclaimer in comments.
#implementation NSSegmentedControl (SegmentedControlWidthComputation)
/* Note: This implementation doesn't properly handle cells with images. It also makes some guesses about AppKit's behavior, and should be treated as an estimation rather than an exact value. */
- (CGFloat)computedWidthForSegment:(NSInteger)SEGIDX
{
CGFloat pad = 0;
switch (self.controlSize) {
case (NSControlSizeMini): pad = 16; break;
case (NSControlSizeSmall): pad = 22; break;
case (NSControlSizeRegular): pad = 28; break;
}
return [[NSAttributedString alloc] initWithString:[self labelForSegment:SEGIDX] attributes:#{NSFontAttributeName:self.font}].size.width + pad;
}
#end
Does Blackberry support subscript superscript? I found the BlackBerry Forum thread "Subscript and superscript in RichTextField", but I am not able to access the BlackBerry knowledge base article.
How can I implement superscript & subscript in a LabelField?
If there's a problem accessing the BlackBerry knowledge base (i.e. from some countries?), here is the content of that page (posted by #MSohm of RIM):
A RichTextField does not nativly support subscript, superscript or
multiple colours. Multiple fonts, font sizes, and font formatting
(for example, Bold, Italic, Underlined) are supported. The following
links explain this further.
How To - Format text in a RichTextField Article Number: DB-00124
http://supportforums.blackberry.com/t5/Java-Development/Format-text-in-a-RichTextField/ta-p/445038
How To - Change the text color of a field Article Number: DB-00114
http://supportforums.blackberry.com/t5/Java-Development/Change-the-text-color-of-a-field/ta-p/442951
If you still want to do this, you could try subclassing RichTextField, or LabelField, and overriding the paint() method. There, you could change the font size and shift the y coordinate of the text. It depends how generic you want to make the solution. Perhaps you could post more information about your problem?
But, as a very simple, hardcoded example, the following code will create a LabelField that prints out: "CO2"
private class SubscriptLabelField extends LabelField {
private int _subscriptTop = 0;
private int _subscriptFontSize = 0;
public SubscriptLabelField(Object text, long style) {
super(text, style);
setFont(getFont());
}
public void setFont(Font newFont) {
super.setFont(newFont);
// we use a subscript that's located at half the normal font's height,
// and is 2/3 as tall as the normal font
int h = newFont.getHeight();
_subscriptTop = h / 2;
_subscriptFontSize = 2 * h / 3;
super.invalidate();
}
protected void layout(int width, int height) {
super.layout(width, height);
// add more space at the bottom for the subscript
int w = getExtent().width;
int h = getExtent().height;
int extraHeight = _subscriptFontSize - (getFont().getHeight() - _subscriptTop);
setExtent(w, h + extraHeight);
}
public void paint(Graphics g) {
// here we hardcode this method to simply draw the last char
// as a "subscript"
String text = getText();
String normalText = text.substring(0, text.length() - 1);
g.drawText(normalText, 0, 0);
// how much space will the normal text take up, horizontally?
int advance = g.getFont().getAdvance(normalText);
// make the subscript a smaller font
Font oldFont = g.getFont();
Font subscript = getFont().derive(Font.PLAIN, _subscriptFontSize);
g.setFont(subscript);
String subscriptText = text.substring(text.length() - 1);
g.drawText(subscriptText, advance, _subscriptTop);
// reset changes to graphics object just to be safe
g.setFont(oldFont);
}
}
And then use it like this:
public SubscriptScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
SubscriptLabelField textField = new SubscriptLabelField("C02", LabelField.NON_FOCUSABLE);
// TODO: this line is just to show the adjusted boundaries of the field -> remove!
textField.setBackground(BackgroundFactory.createSolidBackground(Color.LIGHTGRAY));
add(textField);
}
which gives:
I'm having trouble wrapping my head around this problem, because it actually works when the GridFieldManager is instantiated in the constructor of the MainScreen. If I load it after my json callback, or choose another month to load the gridfield again, I get the columns and rows being larger that I had set it.
theCalendar = new GridFieldManager(rows,7,GridFieldManager.FIXED_SIZE);
int column_width = Display.getWidth() / 7;
for(int y=0;y<7;y++){
theCalendar.setColumnProperty(y, GridFieldManager.FIXED_SIZE, column_width);
System.out.println("column width:"+theCalendar.getColumnWidth(y));
}
for(int o=0;o<rows;o++){
theCalendar.setRowProperty(o, GridFieldManager.FIXED_SIZE, 40);
}
Like I said, if I fire this code in something other than the constructor, the rows and columns are larger than what I had set it as. I even checked getColumnWidth() and it comes up as 68, which is right ( 480 / 7 ). Is there something wrong with my labelfields that I'm adding to it? I feel like it is a bug.
I had a similar problem with the width of columns. I could solve it using GridFieldManager.PREFERRED_SIZE_WITH_MAXIMUM instead of GridFieldManager.FIXED_SIZE.
Below code defines a horizontal field manager with two fields. How can I amend the code so that the background is just set on the two fields being added not on the whole manager. Note, im not attempting to add an individual background image to each of the fields, instead a shared background image that spans behind the two fields.
LabelField label = new LabelField("name");
TextField e = new TextField(Field.FOCUSABLE);
final Bitmap b = Constants.SETTINGS;
final Background bg = BackgroundFactory.createBitmapBackground(Constants.SETTINGS);
HorizontalFieldManager manager = new HorizontalFieldManager()
{
public void sublayout (int width, int height)
{
Field field;
int x = 0;
super.sublayout(b.getWidth(), height);
super.setExtent(b.getWidth(), height);
for (int i = 0; i < getFieldCount(); i++)
{
field = getField(i);
layoutChild(field, Display.getWidth()/2, height);
setPositionChild(field, x, 10);
x += Display.getWidth()/2;
}
}
};
manager.add (label);
manager.add (e);
add (manager);
Rather than putting them in a custom Manager, it may be easier to just override the Fields' layout() calls to be
protected void layout(int width, int height) {
super.layout(width, height);
setExtent(Display.getWidth()/2, this.getHeight());
}
and then you can just use a normal HorizontalFieldManager you can set a background on and a padding (hfm.setPadding(10, 10, 10, 10);). Adding a padding will reduce the available width for your Fields, so you should decrease their widths in the layout() calls.
You can offset each of their individual backgrounds with some fancy, expensive Bitmap footwork (math) to appear to "share" one image using setBackGround(), or you can override their draw methods to achieve the same effect with the ability to "move" across the bitmap according to their relative position...
That what you're after? :)
edit:
create a custom field to use your bitmap and feed it whatever content you would like, then override the paint to draw what you like where you like it...
protected void paint(Graphics g){
// conditionals, etc
g.drawBitmap(x, y, width, height, bitmap, left, top);
// color changes, etc
g.drawText(yourText);
// clean up
}
For the sake of this question, let us suppose that I want a row of buttons. I want to put as many buttons in that row as I can fit on the screen, but no more. In other words, as long as a prospective button will not be cut off or have its text shortened, add it.
It seems that I should be able to do something like:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
int i =0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
remainingWidth -= bf.getWidth();
if(remainingWidth<0)
break;
hfm.add(bf);
i++;
}
add(hfm);
But this doesn't work. bf.getWidth() is always 0. I suspect that this is because the button has not yet been laid out when I query for the width.
So, perhaps I could just make sure the buttons are always the same size. But this won't work for a few reasons:
Different BB platforms have different looks for buttons and text that will fit on a button on a Curve won't fit on a button on a Storm.
3rd party themes may change the look of buttons, so I can't even count on buttons being a certain size on a certain platform.
Is there no way for me to actually check the remaining space before adding a button? It feels like a fairly useful feature; am I just missing something?
Try to draw the Manager before adding component to it.
You should probably add the padding too, if the button has one.
public class Main extends UiApplication{
public static void main(String[] args) {
Main main = new Main();
main.enterEventDispatcher();
}
public Main() {
Screen main = new Screen();
this.pushScreen(main);
main.init();
}
}
public class Screen extends MainScreen {
HorizontalFieldManager hfm;
public Screen() {
hfm = new HorizontalFieldManager();
this.add(hfm);
}
public void init() {
int remainingWidth = Display.getWidth();
int i = 0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
hfm.add(bf);
remainingWidth -= bf.getWidth();
System.out.println(remainingWidth);
if(remainingWidth <= 0) {
hfm.delete(bf);
break;
}
i++;
}
}
You're correct that getWidth() returns the width of the field after it's been laid out (that's why it's 0), but getPreferredWidth() will return the value that's given to the Layout Manager, and is probably what you want.
The other problem you have is that you're comparing remainingWidth to 0. If a button's preferred width is, say, 36 pixels, and the remaining width is 20 pixels, you'll draw a button in a spot where you don't have enough room to display it.
Try something like this:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
for (int i; true; i++ ) {
ButtonField bf = new ButtonField("B " + i);
int preferredWidth = bf.getPreferredWidth();
if ( remainingWidth < preferredWidth )
break;
remainingWidth -= preferredWidth;
hfm.add(bf);
}
add(hfm);
Try to make your own HorizontalFiledManager (extends Manager) and override
protected void sublayout( int maxWidth, int maxHeight ){
setExtent(maxWidth,maxHeight);
}
Get the number of childs, and with a for loop go trough and start to lay out the childs (setPosition, layoutChild).
When you lay out the child, count his width and always check if the counted width + the next child`s width is greater then maxWidth then do not lay out and break the for loop.
Hopefully this help you.