11-22-2010 12:45 PM
Hi everyone
I am trying to implement a custom button with background image. The button should have different border color for focus (RED) & unFocus(WHITE). I am able to change the border color to blue on focus but it doesn't change back to white on unFocus. I think that unFoucs method is not invoked. But I dont know why it is not invoked. I have included the code below. I will really appreciate if someone can help me with this.
Thank you.
public class BitmapBorderBtnField extends BitmapField {
private Bitmap _bitmap;
private int myBorderColor;
/**
* The Constructor.
*
* @param bitmap the bitmap
*/
public BitmapBorderBtnField(Bitmap bitmap) {
super(bitmap, BitmapField.FOCUSABLE);
this._bitmap = bitmap;
myBorderColor = Color.WHITE;
}
/**
* The Constructor.
*
* @param l the style
* @param bitmap the bitmap
*/
public BitmapBorderBtnField(long l, Bitmap bitmap) {
super(bitmap, l | BitmapField.FOCUSABLE );
this._bitmap = bitmap;
myBorderColor = Color.WHITE;
}
protected void onFocus(int direction){
myBorderColor = Color.RED;
invalidate();
// super.onFocus(direction);
}
protected void onUnFocus(){
myBorderColor = Color.WHITE;
invalidate();
// super.onUnfocus();
}
protected void drawFocus(Graphics graphics, boolean b) {
}
public int getPreferredWidth() {
return _bitmap.getWidth();
}
public int getPreferredHeight() {
return _bitmap.getHeight();
}
protected void layout(int i, int i1) {
super.layout(_bitmap.getWidth(), _bitmap.getHeight() );
setExtent(_bitmap.getWidth(), _bitmap.getHeight() );
}
protected void paint( Graphics g ) {
g.setColor(myBorderColor);
g.drawRoundRect(0, 0, getPreferredWidth(), getPreferredHeight(), 12, 12);
// g.setColor(prevColor);
g.drawBitmap( 0, 0, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0 );
}
public void setBtnPosition(int x, int y){
setPosition(x, y);
}
protected void fieldChangeNotify(int i) {
super.fieldChangeNotify(i);
}
protected boolean keyDown(int i, int i1) {
if (Keypad.key(i) == Keypad.KEY_ENTER && (KeypadListener.STATUS_ALT & Keypad.status(i)) == 0) {
this.getChangeListener().fieldChanged(this, 0);
}
return super.keyDown(i, i1);
}
protected boolean navigationClick(int i, int i1) {
fieldChangeNotify(1);
return true;
}
}
11-22-2010 02:58 PM
Indeed, onUnfocus is flawed and is not always called. The safest approach which, in my experience, works every time is to use FocusChangeListener. Make your BitmapBorderBtnField not only extend BitmapField but also implement FocusChangeListener, include a public void focusChanged(Field field, int reason) method, check the reason parameter and react appropriately to FOCUS_GAINED and FOCUS_LOST. In your constructors, invoke setFocusListener(this) somewhere.
Some general suggestions:
1. Replace the first constructor with
public BitmapBorderBtnField(Bitmap bitmap) {
this(0L, bitmap);
}
2. Remove setExtent from your layout - super.layout already does that. Frankly, I think you don't need that layout override at all - the built-in one will do a better and more efficient job at that.
3. setBtnPosition will throw an exception if called at the wrong time - think twice whether you need it at all. It can be called during the manager's sublayout only where it can be safely substituted with setPositionChild(...).
4. fieldChangeNotify override is superfluous. Remove it.
5. keyDown will throw a NullPointerException if you don't setChangeListener on this field. Change it to something like that:
protected boolean keyDown(int i, int i1) {
if (Keypad.key(i) == Keypad.KEY_ENTER && (KeypadListener.STATUS_ALT & Keypad.status(i)) == 0) {
fieldChangeNotify(0);
}
return super.keyDown(i, i1);
}
fieldChangeNotify knows to check the field change listener by itself. Also consider passing it FieldChangeListener.PROGRAMMATIC rather than 0 - this way the field won't be marked dirty.
6. Override navigationUnclick rather than navigationClick - this will prevent the default menu from popping up.
11-22-2010 11:50 PM - edited 11-22-2010 11:51 PM
@ arkadyz
thank you very much for your response. I really appreciate for pointing out details. I have made the changes as you have suggested.
Before I was trying to draw focus border in paint method. But this time, I am using setBorder with ViSUAL_STATE_FOCUS. It works as expected. It shows the border on focus. The only problem is that when I tried to increase the thickness of the border, the bitmap moves little towards right bottom on focus. It is not centered. How do I stop movement of bitmap?
And between two methods of setting focus border (paint() & setBorder() ), which one is better? (JDE 5.0)
public class BitmapBorderBtnField extends BitmapField {
private Bitmap _bitmap;
Border focusBorder = BorderFactory.createRoundedBorder(new XYEdges(2,2,2,2), Color.RED, Border.STYLE_SOLID);
public BitmapBorderBtnField(Bitmap bitmap) {
this(BitmapField.FOCUSABLE,bitmap);
}
public BitmapBorderBtnField(long l, Bitmap bitmap) {
super(bitmap, l | BitmapField.FOCUSABLE);
this._bitmap = bitmap;
this.setBorder(Field.VISUAL_STATE_FOCUS, focusBorder);
// myBorderColor = Color.WHITE;
// setFocusListener(this);
}
public int getPreferredHeight() {
return _bitmap.getHeight();
}
protected void layout(int i, int i1) {
super.layout(_bitmap.getWidth(), _bitmap.getHeight() );
}
protected boolean keyDown(int i, int i1) {
if (Keypad.key(i) == Keypad.KEY_ENTER && (KeypadListener.STATUS_ALT & Keypad.status(i)) == 0) {
fieldChangeNotify(FieldChangeListener.PROGRAMMATIC );
}
return super.keyDown(i, i1);
}
protected boolean navigationClick(int i, int i1) {
fieldChangeNotify(1);
return true;
}
protected void drawFocus(Graphics graphics, boolean b) {
//Do Nothing
}
}
on your this suggestion
2. Remove setExtent from your layout - super.layout already does that. Frankly, I think you don't need that layout override at all - the built-in one will do a better and more efficient job at that.
when I remove the layout method, it doesn't layout bitmap field.
Thank you.
11-23-2010 12:16 AM
indusBULL wrote:2. Remove setExtent from your layout - super.layout already does that. Frankly, I think you don't need that layout override at all - the built-in one will do a better and more efficient job at that.
when I remove the layout method, it doesn't layout bitmap field.
Thank you.
I think it's something else that doesn't lay out the field - BitmapField is perfectly capable of laying itself out. Anyway, your field is not centered because you forget to add the border to the bitmap dimensions, thus cutting the image on the right and bottom.
So:
1. Try to find out what prevents the built-in layout from doing its job.
2. If you cannot figure it out, at least add border dimensions to your super.layout call.
11-28-2010 11:22 PM
If it helps anyone, here's my ActiveBitmapField (basically a hilghtable, clickable, ENTER-key-aware bitmap), using the 4.5 API:
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.BitmapField;
public class ActiveBitmapField extends BitmapField {
int highLightThickness = 2;
int focusColor = Color.BLUE;
public int getHighLightThickness() {
return highLightThickness;
}
public void setHighLightThickness(int highLightThickness) {
this.highLightThickness = highLightThickness;
}
public int getFocusColor() {
return focusColor;
}
public void setFocusColor(int focusColor) {
this.focusColor = focusColor;
}
ActiveBitmapField( Bitmap bmp, long style ){
super( bmp, style);
}
ActiveBitmapField( Bitmap bmp, long style, int highLightThickness ){
this( bmp, style);
this.highLightThickness = highLightThickness;
}
protected void drawFocus(Graphics g, boolean on) {
if (on) {
XYRect ex = this.getExtent();
g.setColor( focusColor );
for (int i=0; i <= highLightThickness; i++){
g.drawRect(ex.x+i, ex.y+i, ex.width-i*2, ex.height-i*2);
}
}
}
protected boolean navigationClick(int status, int time) {
// PROGRAMMATIC prevents from setting the field to DIRTY,
// (thus not prompting to save the screen on exit):
fieldChangeNotify(FieldChangeListener.PROGRAMMATIC );
return true;
}
protected boolean keyChar(char key, int status, int time) {
if (key == Keypad.KEY_ENTER) {
// PROGRAMMATIC prevents from setting the field to DIRTY,
// (thus not prompting to save the screen on exit):
fieldChangeNotify(FieldChangeListener.PROGRAMMATIC );
return true;
}
return super.keyChar(key, status, time);
}
}
11-29-2010 12:17 AM
Hi. Try to center your bitmap in this line
g.drawBitmap( 0, 0, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0 );
Calculate
otstupX = (getFieldWidth() - _bitmap.getWidth()) / 2;
otstupY = (getFieldHeight() - _bitmap.getHeight()) / 2;
and than
g.drawBitmap(otstupX, otstupY, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0);
Regards, Dmitry.
11-30-2010 12:32 AM
Sorry for replying too late.
You are right. I tried the same code in different manager and it worked perfectly fine without layout method. I still need to figure out why it didn't work with previous manager. Thank you.
@jlbenc
Thank you very much posting your code. It is exactly what I was looking for but much easier implementation.
@dmazgalin
otstupX = (getFieldWidth() - _bitmap.getWidth()) / 2;
otstupY = (getFieldHeight() - _bitmap.getHeight()) / 2;
and than
g.drawBitmap(otstupX, otstupY, _bitmap.getWidth(), _bitmap.getHeight(), _bitmap, 0, 0);
Thanks for reply. I tried out your suggestion. But it didn't solve my problem. When you say getFieldWidth, do you mean getWidth() method of field?
11-13-2011 09:12 AM
_bitmapField = new BitmapField(bitmap,BitmapField.FOCUSABLE|BitmapField.FIELD_HCENTER){ /*protected void layout(int width, int height) { setExtent(bitmap.getWidth()+3, bitmap.getHeight()+3); }*/ Border bF = BorderFactory.createRoundedBorder(new XYEdges(1,1,1,1),Color.AQUA,Border.STYLE_FILLED); Border bUF = BorderFactory.createRoundedBorder(new XYEdges(1,1,1,1), 0x818181,Border.STYLE_FILLED); protected void onFocus(int direction) { this.setBorder(bF); } protected void onUnfocus() { this.setBorder(bUF); } };
Hello all, I am using the above solution, posted the trick just in case it might help someone, I request the author to mark this thread as closed and thanks to all the abpve contributors, this post has helped me cross the hurlde.
A Y