Binding TextArea height to its content - layout

JavaFX: Is it possible to bind TextArea height (row count) to the height of its content?
I would like to dynamically change height of TextArea while writing the text.

Have a look at JavaFX utility class. Although this is not a solution using binding, computeTextHeight(Font font, String text, double wrappingWidth) method can help you.

This is an exact, simple & working solution:
SimpleIntegerProperty count = new SimpleIntegerProperty(20);
int rowHeight = 10;
txtArea.prefHeightProperty().bindBidirectional(count);
txtArea.minHeightProperty().bindBidirectional(count);
txtArea.scrollTopProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newVal) {
if(newVal.intValue() > rowHeight){
count.setValue(count.get() + newVal.intValue());
}
}
});
Alternatively you can use lambdas to simply the syntax even further:
SimpleIntegerProperty count=new SimpleIntegerProperty(20);
int rowHeight = 10;
textArea.prefHeightProperty().bindBidirectional(count);
textArea.minHeightProperty().bindBidirectional(count);
textArea.scrollTopProperty().addListener((ov, oldVal, newVal) -> {
if(newVal.intValue() > rowHeight){
count.setValue(count.get() + newVal.intValue());
}
});

A solution that workq fine in javafx8 (hiding toolbar is inspired from JavaFX TextArea Hiding Scroll Bars):
class MyTextArea extends TextArea {
#Override
protected void layoutChildren() {
super.layoutChildren();
ScrollBar scrollBarv = (ScrollBar) this.lookup(".scroll-bar:vertical");
if (scrollBarv != null) {
System.out.println("hiding vbar");
((ScrollPane) scrollBarv.getParent()).setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
}
ScrollBar scrollBarh = (ScrollBar) this.lookup(".scroll-bar:horizontal");
if (scrollBarh != null) {
System.out.println("hiding hbar");
((ScrollPane) scrollBarh.getParent()).setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
}
}
#Override
protected double computePrefWidth(double width) {
Bounds bounds = getTextBounds();
Insets insets = getInsets();
double w = Math.ceil(bounds.getWidth() + insets.getLeft() + insets.getRight());
return w;
}
#Override
protected double computePrefHeight(double height) {
Bounds bounds = getTextBounds();
Insets insets = getInsets();
double h = Math.ceil(bounds.getHeight() + insets.getLeft() + insets.getRight());
return h;
}
//from https://stackoverflow.com/questions/15593287/binding-textarea-height-to-its-content/19717901#19717901
public Bounds getTextBounds() {
//String text = (textArea.getText().equals("")) ? textArea.getPromptText() : textArea.getText();
String text = "";
text = this.getParagraphs().stream().map((p) -> p + "W\n").reduce(text, String::concat);
text += "W";
helper.setText(text);
helper.setFont(this.getFont());
// Note that the wrapping width needs to be set to zero before
// getting the text's real preferred width.
helper.setWrappingWidth(0);
return helper.getLayoutBounds();
}
}

Related

How to animate a filled triangle (drawn by a path) in Android Studio

I'm trying to build a jigsaw-like app for Android that involves the user moving polygonal pieces around the screen by touch event.
I can draw - for example - a triangle, and fill it. I can also move it smoothly round the screen but it leaves a trace - so that the screen rapidly fills up :-(
So I am wondering how to adjust the onDraw method so that the previous positions of the triangle are not included. Or is there some other technique? The question has been asked once before but did not get a satisfactory answer. I am quite new to Android work so I am sure a kind expert will be able to point me in the right direction!
The view:
public class GameView extends View {
public Triangle T1;
Paint paint = new Paint();
private Path path;
public GameView(Context context) {
super(context);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
initTriangle();
}
public void initTriangle() {
int T1Points[][] = new int[3][2];
T1Points[0][0] = -200;
T1Points[0][1] = -100;
T1Points[1][0] = 200;
T1Points[1][1] = -100;
T1Points[2][0] = 0;
T1Points[2][1] = 100;
float[] position = new float[2];
position[0] = (float) 200.0;
position[1] = (float) 100.0;
T1 = new Triangle("T1", T1Points, position);
path = T1.getPath();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float[] pos = new float[2];
pos[0] = event.getX(); //nextx;
pos[1] = event.getY(); //nexty;
T1.setPosition(pos );
Path path = new Path();
path = T1.getPath();
postInvalidate();
return true;
}
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
}
Triangle class:
public class Triangle {
public int[][] myPoints;
public Path path;
public String myname;
public float[] position;
public Triangle (String name, int[][] newpoints, float[] posn) {
myPoints = new int[4][2];
int i;
for (i = 0; i < 3; i++) {
myPoints[i][0] = newpoints[i][0];
myPoints[i][1] = newpoints[i][1];
}
myPoints[3][0] = newpoints[0][0]; // closed circuit for future needs
myPoints[3][1] = newpoints[0][1];
path = new Path();
position = new float[2];
position[0] = posn[0];
position[1] = posn[1];
myname = name;
updatePath();
}
public void setPosition(float[] newPosition){
position[0] = newPosition[0];
position[1] = newPosition[1];
updatePath();
}
public void updatePath(){
int startx = myPoints[0][0] + Math.round(position[0]);
int starty = myPoints[0][1] + Math.round(position[1]);
path.moveTo(startx,starty);
for (int i = 1; i < myPoints.length; i++)
{
int newx = myPoints[i][0] + Math.round(position[0]);
int newy = myPoints[i][1] + Math.round(position[1]);
path.lineTo(newx,newy);
}
path.close();
}
public Path getPath() {
return path;
}
}
Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uk.mydomain.animatedtriangle.GameView gameView = new uk.mydomain.animatedtriangle.GameView(this);
RelativeLayout relativeLayout = new RelativeLayout(this);
RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
relativeLayout.setLayoutParams(relativeParams);
setContentView(relativeLayout);
relativeLayout.addView(gameView);
}
Sorted! Just needed to add a path.reset() at the end of onDraw.

How to position text over resizable imageview?

It has a stackpane and few childs inside it:
public class SlideFromText {
private StackPane sp = new StackPane();
private ImageView iv = new ImageView();
private int lines_count = 0;
private Group text_group = new Group();
public SlideFromText(Image img, String t) {
iv.setImage(img);
iv.setPreserveRatio(true);
iv.fitWidthProperty().bind(sp.widthProperty());
iv.fitHeightProperty().bind(sp.heightProperty());
sp.setStyle("-fx-border-color : red; -fx-border-width: 1; -fx-border-style: solid;");
sp.setAlignment(Pos.CENTER);
sp.setMinSize(0, 0);
sp.getChildren().add(iv);
String lines[] = t.split("\\r?\\n");
double height = 0;
for (String line : lines) {
Text text = new Text(line);
text.setFill(Color.BLACK);
text.setY(height);
text_group.getChildren().add(text);
lines_count++;
height += text.getBoundsInLocal().getHeight();
System.out.println("Height: " + height);
}
sp.getChildren().add(text_group);
iv.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
Node node = sp.getChildren().get(1);
if (node.getClass() != Group.class) {
return;
}
Group group_node = (Group) node;
double scale_x = iv.getBoundsInParent().getWidth() / group_node.getBoundsInParent().getWidth();
double scale_y = iv.getBoundsInParent().getHeight() / group_node.getBoundsInParent().getHeight();
double scale = Math.min(scale_x, scale_y);
group_node.setScaleX(group_node.getScaleX() * scale);
group_node.setScaleY(group_node.getScaleY() * scale);
System.out.println("IVB: " + iv.getBoundsInParent());
System.out.println("GVB: " + group_node.getBoundsInParent());
}
});
}
public Pane getPane() {
return sp;
}
}
I need to have multi-line text inside group. I want to resize my window and keep image aspect ratio and text proportions and position inside image.
In my example I can't set my text on the top of image.
Maybe it has more easy way.

How to draw multiline Edittext in notepad?

I am creating a notepad and I want to have edit text with multiple horizontal lines on it. As I have done with some but It is displaying line when I click on next or enter to the next line. I want that lines already be there. When I am using layour with android:lines="5". It is displaying picture as I have attached.
Please suggest on the same. thanks
EDITED: My EditText is showing like this!!!! A Huge gap on top. Please suggest what to do?
This is the code, based on google's note editorThe output will be shown in the image.When you press the enter,new lines will be added.
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(R.color.edit_note_line); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
For more info,Refer this link.
Simple add this line in your XML
android:gravity="top|left"
Here , my code will draw 15 lines by default and you can get more lines by pressing Enter :-
package com.wysiwyg.main;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLUE); //SET YOUR OWN COLOR HERE
setMinLines(15);
}
#Override
protected void onDraw(Canvas canvas) {
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if(getLineCount() > count){
count = getLineCount();
}
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}

BlackBerry 6/7 Table Layout with ColSpan - Number Selection Widget

Trying to make a simple number clicker control for BlackBerry 6/7, like this:
At heart it's just a text field and two buttons, with a Manager to space them out.
I know about the unsupported add-on TableManager, but it doesn't support column scans. And, the notion of using deeply-nested Managers I find... disturbing.
And, this will come up multiple times, so I wanted a simple, reusable component.
So, I built a simple Manager to contain these three components, even allowing you to provide your own textfield or buttons for stylistic reasons. The code is attached below. Obviously fancier than it needs to be but the work is all done in sublayout.
What actually happens is that the upper right of each of the 3 components appears in the correct place, but the 3 components are "shrink wrapped" to the minimum size needed to display their contents, ignoring the requested USE_ALL_WIDTH and USE_ALL_HEIGHT. This is probably a minor goofup, but how can I make these components actually USE ALL WIDTH and USE ALL HEIGHT? I have tried several variations on USE_ALL_* but not found the winning one yet. Of course any other improvements would also be welcome.
Thanks.
package layout;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.EditField;
/**
* XXX BROKEN DO NOT USE YET - layout fail, components get shrink-wrapped.
*
* NumberClicker Makes a layout with three components, like this:
* <pre>
* +-------------------+ +-------------------+
* | | | + |
* | 3 | |-------------------|
* | | |-------------------|
* | | | - |
* |-------------------| |-------------------|
* </pre>
* Note that by default, the buttons are set to increment and decrement the number in the textfield!
* #author Ian Darwin
*/
public class NumberClicker extends Manager {
private static final long SUBCOMPONENT_STYLE = Field.USE_ALL_HEIGHT | Field.USE_ALL_WIDTH;
private static final long MANAGER_STYLE = Field.FIELD_HCENTER | Field.FIELD_VCENTER;
final XYEdges MARGINS = new XYEdges(10,10,10,10);
EditField number = new EditField(SUBCOMPONENT_STYLE);
ButtonField plus = new ButtonField("+", SUBCOMPONENT_STYLE);
ButtonField minus = new ButtonField("-", SUBCOMPONENT_STYLE);
public NumberClicker() {
this(MANAGER_STYLE);
}
public NumberClicker(long style)
{
this(null, null, null, style);
}
/** Constructor allows you to provide your own three fields */
public NumberClicker(EditField number, ButtonField plus, ButtonField minus) {
this(number, plus, minus, MANAGER_STYLE);
}
/** Constructor allows you to provide your own three fields ANd override style.
* If any of the fields is null, the default value is used.
*/
public NumberClicker(EditField number, ButtonField plus, ButtonField minus, long style) {
super(style);
if (number != null) {
this.number = number;
} else {
this.number.setMargin(MARGINS); // set margins on our default, constructed above.
}
setValue(1);
add(this.number); // Nulls allowed, so must be careful to use "this." throughout this method.
if (plus != null) {
this.plus = plus;
} else {
this.plus.setMargin(MARGINS);
}
add(this.plus);
if (minus != null) {
this.minus = minus;
} else {
this.minus.setMargin(MARGINS);
}
add(this.minus);
this.plus.setRunnable(new Runnable() {
public void run() {
increment();
}
});
this.minus.setRunnable(new Runnable() {
public void run() {
decrement();
}
});
}
public void increment() {
number.setText(Integer.toString(Integer.parseInt(number.getText().trim()) + 1));
}
public void decrement() {
number.setText(Integer.toString(Integer.parseInt(number.getText().trim()) - 1));
}
/** Return the integer value of the clicker. Do not call if you are re-using this as a three-component layout manager! */
public int getValue() {
return Integer.parseInt(number.getText().trim());
}
public void setValue(int value) {
number.setText(Integer.toString(value));
}
/**
* Compute sizes and positions of subfields.
*
* Required by Manager
*/
public void sublayout(int width, int height) {
int layoutWidth = width;
int layoutHeight = Math.min(height, Display.getHeight()); // no scrolling here
System.err.println("Display:" + Display.getWidth() + "x" + Display.getHeight());
int halfX = layoutWidth / 2;
int halfY = layoutHeight / 2;
System.err.println("sublayout:" + width + "," + height + "; " + halfX + "," + halfY);
int numberWidth = halfX - number.getMarginLeft() - number.getMarginRight();
int numberHeight = layoutHeight - number.getMarginTop() - number.getMarginBottom();
layoutChild(number, numberWidth, numberHeight);
setPositionChild(number, 0 + number.getMarginLeft(), 0 + number.getMarginTop());
System.err.println(number + " " + numberWidth + "," + numberHeight + " " +number.getMarginLeft());
int plusWidth = halfX - plus.getMarginLeft() - plus.getMarginRight();
int plusHeight = halfY - plus.getMarginTop() - plus.getMarginBottom();
layoutChild(plus, plusWidth, plusHeight);
setPositionChild( plus, halfX + plus.getMarginLeft(), plus.getMarginTop());
int minusWidth = halfX - minus.getMarginLeft() - minus.getMarginRight();
int minusHeight = halfY - minus.getMarginTop() - minus.getMarginBottom();
layoutChild(minus, minusWidth, minusHeight);
// Use plus.getMarginHeight() for better alignment.
setPositionChild( minus, halfX + plus.getMarginLeft(), halfY + minus.getMarginTop() );
//setVirtualExtent(layoutWidth, height);
setExtent(layoutWidth, height);
}
public EditField getNumberField() {
return number;
}
public void setNumberField(EditField number) {
this.number = number;
}
public ButtonField getPlusField() {
return plus;
}
public void setPlusField(ButtonField plus) {
this.plus = plus;
}
public Field getMinusField() {
return minus;
}
public void setMinusField(ButtonField minus) {
this.minus = minus;
}
}
The closest thing to what you are trying to achieve is
Few notes:
EditField always use USE_ALL_WIDTH. It doesn't matter if you requested it or not. Therefore, if you want to limit its width you have override its layout() method. In my code snippet, its width is limited by the maximum chars allowed for this field's value (see CustomEditField).
ButtonField ignores USE_ALL_WIDTH and USE_ALL_HEIGHT. Its extent depends only on the text within the button. In order to achieve the effect of USE_ALL_WIDTH, you have to add horizontal padding to it.
Unfortunately, the padding trick won't work if you want to achieve the USE_ALL_HEIGHT effect. When you add vertical padding to a button, at some stage it will repeat its background vertically. If it is required, you will have to write a custom button field for it.
Also check BlackBerry's advanced UI components at this page.
Here is the code:
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.FontMetrics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.decor.Border;
import net.rim.device.api.ui.decor.BorderFactory;
import net.rim.device.api.ui.text.NumericTextFilter;
public class NumberClicker extends Manager {
private class CustomEditField extends EditField {
public int getPreferredWidth() {
FontMetrics fontMetrics = new FontMetrics();
getFont().getMetrics(fontMetrics);
return getMaxSize()*fontMetrics.getMaxCharWidth();
};
public int getPreferredHeight() {
// forcing the field to be single lined
return getFont().getHeight();
}
protected void layout(int width, int height) {
super.layout(
Math.min(width, getPreferredWidth()),
Math.min(height, getPreferredHeight())
);
}
}
final XYEdges MARGINS = new XYEdges(2,2,2,2);
EditField _number;
Manager _numberManager;
ButtonField _plus;
ButtonField _minus;
public NumberClicker() {
super(0);
Font font = getFont();
font = font.derive(Font.BOLD, font.getHeight() + 10);
_number = new CustomEditField();
_number.setFilter(new NumericTextFilter());
_number.setMaxSize(1);
_number.setFont(font);
setValue(1);
_numberManager = new Manager(0) {
protected void sublayout(int width, int height) {
layoutChild(_number, width, height);
setPositionChild(_number,
Math.max(0, (width - _number.getWidth())/2),
Math.max(0, (height - _number.getHeight())/2)
);
setExtent(width, height);
}
};
_numberManager.setBorder(BorderFactory.createRoundedBorder(new XYEdges()));
_numberManager.setMargin(MARGINS);
_numberManager.add(_number);
add(_numberManager);
_plus = new ButtonField("+", 0);
_plus.setMargin(MARGINS);
add(_plus);
_minus = new ButtonField("-");
_minus.setMargin(MARGINS);
add(_minus);
_plus.setRunnable(new Runnable() {
public void run() {
increment();
}
});
_minus.setRunnable(new Runnable() {
public void run() {
decrement();
}
});
}
private void increment() {
synchronized (UiApplication.getEventLock()) { //probably not needed here. overkill.
_number.setText(Integer.toString(Integer.parseInt(_number.getText().trim()) + 1));
}
}
private void decrement() {
if (Integer.parseInt(_number.getText()) <= 0) {
return;
}
synchronized (UiApplication.getEventLock()) { //probably not needed here. overkill.
_number.setText(Integer.toString(Integer.parseInt(_number.getText().trim()) - 1));
}
}
public void setValue(int value) {
if (value < 0) {
return;
}
synchronized (UiApplication.getEventLock()) { // MUST. can be called from non UI thread.
_number.setText(Integer.toString(value));
}
}
/**
* Compute sizes and positions of subfields.
*/
public void sublayout(int width, int height) {
int heightUsed = 0;
int halfX = width / 2;
Border border = _plus.getBorder();
int plusWidth = halfX - _plus.getMarginLeft() - _plus.getMarginRight();
int plusHeight = height - _plus.getMarginTop() - _plus.getMarginBottom();
// calculate horizontal padding so the button will look like USE_ALL_WIDTH
int plusHPadding = (Math.max(0, plusWidth - _plus.getPreferredWidth() - border.getLeft() - border.getRight()))/2;
_plus.setPadding(0, plusHPadding, 0, plusHPadding);
layoutChild(_plus, plusWidth, plusHeight);
setPositionChild( _plus, halfX + _plus.getMarginLeft(), _plus.getMarginTop());
heightUsed += _plus.getHeight() + _plus.getMarginTop() + _plus.getMarginBottom();
border = _minus.getBorder();
int minusWidth = halfX - _minus.getMarginLeft() - _minus.getMarginRight();
int minusHeight = height - _plus.getHeight() - _minus.getMarginTop() - _minus.getMarginBottom();
// calculate horizontal padding so the button will look like USE_ALL_WIDTH
int minusHPadding = (Math.max(0, minusWidth - _minus.getPreferredWidth() - border.getLeft() - border.getRight()))/2;
_minus.setPadding(0, minusHPadding, 0, minusHPadding);
layoutChild(_minus, minusWidth, minusHeight);
setPositionChild( _minus, halfX + _plus.getMarginLeft(), heightUsed + _minus.getMarginTop());
heightUsed += _minus.getHeight() + _minus.getMarginTop() + _minus.getMarginBottom();
int numberWidth = halfX - _numberManager.getMarginLeft() - _numberManager.getMarginRight();
int numberHeight = heightUsed - _numberManager.getMarginTop() - _numberManager.getMarginBottom();
layoutChild(_numberManager, numberWidth, numberHeight);
setPositionChild(_numberManager, _numberManager.getMarginLeft(), _numberManager.getMarginTop());
setExtent(width, heightUsed);
}
}

Creating Tabmenu in j2me

Is there any way to create a Tab Menu in j2me?
I found a code but I am unable to understand it
In this code there is Tab Menu created which is in Canvas class and then Tab menu is created which is totally done in Canvas or painted. The only part I found difficult to grasp was the void go() method and then
When I try to draw anything above and below this code using paint method, it doesn't work - what's the problem?
Below is the code
// Tab Menu CANVAS class
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
public class TabMenuCanvas extends Canvas
{
TabMenu menu = null;
public TabMenuCanvas()
{
menu = new TabMenu(
new String[]{"Home", "News", "Community", "Your files", "Credits", "Events", "Blog", "Upload", "Forum Nokia"},
getWidth() - 20
);
}
protected void keyPressed(int key)
{
int gameAction = getGameAction(key);
if(gameAction == Canvas.RIGHT)
{
menu.goRight();
repaint();
}
else if(gameAction == Canvas.LEFT)
{
menu.goLeft();
repaint();
}
}
protected void paint(Graphics g)
{
g.translate(10, 30);
menu.paint(g);
g.translate(- 10, - 30);
}
}
// Tab Menu Class
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
public class TabMenu
{
int background = 0xffffff;
int bgColor = 0xcccccc;
int bgFocusedColor = 0x0000ff;
int foreColor = 0x000000;
int foreFocusedColor = 0xffffff;
int cornerRadius = 6;
int padding = 2;
int margin = 2;
Font font = Font.getDefaultFont();
int scrollStep = 20;
int selectedTab = 0; //selected tab index
int[] tabsWidth = null; //width of single tabs
int[] tabsLeft = null; //left X coordinate of single tabs
int tabHeight = 0; //height of tabs (equal for all tabs)
String[] tabs = null; //tab labels
int menuWidth = 0; //total menu width
int viewportWidth = 0; //visible viewport width
int viewportX = 0; //current viewport X coordinate
public TabMenu(String[] tabs, int width)
{
this.tabs = tabs;
this.viewportWidth = width;
initialize();
}
void initialize()
{
tabHeight = font.getHeight() + cornerRadius + 2 * padding; //[ same for all tabs]
menuWidth = 0;
tabsWidth = new int[tabs.length];
tabsLeft = new int[tabs.length];
for(int i = 0; i < tabsWidth.length; i++)
{
tabsWidth[i] = font.stringWidth(tabs[i]) + 2 * padding + 2 * cornerRadius;
tabsLeft[i] = menuWidth;
menuWidth += tabsWidth[i];
if(i > 0)
{
menuWidth += margin;
}
}
}
public void goRight()
{
go(+1);
}
public void goLeft()
{
go(-1);
}
private void go(int delta)
{
int newTab = Math.max(0, Math.min(tabs.length - 1, selectedTab + delta));
boolean scroll = true;
if(newTab != selectedTab && isTabVisible(newTab))
{
selectedTab = newTab;
if( (delta > 0 && tabsLeft[selectedTab] + tabsWidth[selectedTab] > viewportX + viewportWidth) ||
(delta < 0 && tabsLeft[selectedTab] < viewportX))
{
scroll = true;
}
else
{
scroll = false;
}
}
if(scroll)
{
viewportX = Math.max(0, Math.min(menuWidth - viewportWidth, viewportX + delta * scrollStep));
}
}
private boolean isTabVisible(int tabIndex)
{
return tabsLeft[tabIndex] < viewportX + viewportWidth &&
tabsLeft[tabIndex] + tabsWidth[tabIndex] >= viewportX;
}
public void paint(Graphics g)
{
int currentX = - viewportX;
g.setClip(0, 0, viewportWidth, tabHeight);
g.setColor(background);
g.fillRect(0, 0, viewportWidth, tabHeight);
for(int i = 0; i < tabs.length; i++)
{
g.setColor(i == selectedTab ? bgFocusedColor : bgColor);
g.fillRoundRect(currentX, 0, tabsWidth[i], tabHeight + cornerRadius, 2 * cornerRadius, 2 * cornerRadius);
g.setColor(i == selectedTab ? foreFocusedColor : foreColor);
g.drawString(tabs[i], currentX + cornerRadius + padding, cornerRadius + padding, Graphics.LEFT | Graphics.TOP);
currentX += tabsWidth[i] + margin;
}
}
}
When I try to draw anything above and below this code using paint method, it doesn't work
what of the paint methods you use to draw above and below? Pay attention that there are two methods named that way - first is in TabMenuCanvas, second is in TabMenu (second method is invoked from TabMenuCanvas#repaint).
whatever you would try to draw in TabMenuCanvas#paint will most likely be overwritten by setClip and fillRect when TabMenu#paint is invoked following repaint request
The only place where one can expect to be able to draw something visible seems to be in TabMenu#paint method, inside the clip area that is set there.
You can use GUI Libraries for J2ME,for example Lightweight User Interface Toolkit (LWUIT),Flemil have "tab menu".You can see list of GUI Libraries here.

Resources