I have a very strange issue. In the following code at 261 in the TouchActioNTypeCancelled i want to call a Method which should set the CroppingRect.Rect to my bindable Property testImage. The Strange thing: When testIamge is a normal Property, not bindable, everything works fine. I can move the croppign rectangle with all corners in the view. If i now change the Proeprty to a bindable property when i start movign the rectangle on one corner in the app i can move the rectangle only from the opposite corners again and if i press the opposite corner, the rectangle buggs back into its original position. I don't understand how this can happen. I did not yet consume the Property in the view since i wanted to check first if it works without consuming it.
This is the way it works with a npormal property. In fact, this is, how i want it to work with a bindable proeprty as well:
Workign example(only with normal property)
This, however, is how it works with the bindable property currently:
Not working example(Bindable property)
public static readonly BindableProperty testImageProperty =
BindableProperty.Create(nameof(testImage), typeof(SKRect), typeof(SKRect));
public SKRect testImage
{
get
{
return (SKRect)GetValue(testImageProperty);
}
set
{
SetValue(testImageProperty, value);
}
}
//public SKRect testImage { get; set; }
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
int i = 0;
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
SetMap();
touchPoints.Remove(args.Id);
}
break;
}
}
private void SetMap()
{
testImage = croppingRect.Rect;
Console.WriteLine("test");
}
Related
I followed this tutorial to create an Image cropping page https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/bitmaps/cropping. However i now bound two views of the PhotoCropperCanvasView to an carousel view an wanted to Bind the CroppedBitMap so that i can access this property directl in the viewmodel. I just cannot figure ouit how i would achieve that. I f i just make this a Bindable Property the property does not change when i make a new rectangle. So i think i kind of have to exclude the Code of the property but i am very confused.
The whole ocde:
public class PhotoCropperCanvasView : SKCanvasView, INotifyPropertyChanged
{
const int CORNER = 50; // pixel length of cropper corner
const int RADIUS = 100; // pixel radius of touch hit-test
//SKBitmap bitmap;
//CroppingRectangle croppingRect;
SKMatrix inverseBitmapMatrix;
public SKBitmap testmap;
//public SKBitmap bitmap { get; set; }
public Image testImage { get; set; }
public static readonly BindableProperty mapProperty =
BindableProperty.Create(nameof(map), typeof(SKBitmap), typeof(PhotoCropperCanvasView), null);
public SKBitmap map
{
get
{
return (SKBitmap)GetValue(mapProperty);
}
set
{
SetValue(mapProperty, value);
}
}
public CroppingRectangle croppingRect { get; set; }
//public SKMatrix inverseBitmapMatrix { get; set; }
public static readonly BindableProperty bitmapProperty =
BindableProperty.Create(nameof(bitmap), typeof(SKBitmap), typeof(Image),null,propertyChanged: OnbitmapChanged);
static void OnbitmapChanged(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine("test");
}
public SKBitmap bitmap
{
get
{
return (SKBitmap)GetValue(bitmapProperty);
}
set
{
SetValue(bitmapProperty, value);
}
}
public SKBitmap CroppedBitmap
{
get
{
SKRect cropRect = new SKRect(croppingRect.Rect.Left,croppingRect.Rect.Top,croppingRect.Rect.Right,croppingRect.Rect.Bottom);
SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
(int)cropRect.Height);
SKRect dest = new SKRect(0, 0, cropRect.Width, cropRect.Height);
SKRect source = new SKRect(cropRect.Left, cropRect.Top,
cropRect.Right, cropRect.Bottom);
using (SKCanvas canvas = new SKCanvas(croppedBitmap))
{
canvas.DrawBitmap(bitmap, source, dest);
}
return croppedBitmap;
}
}
// Touch tracking
TouchEffect touchEffect = new TouchEffect();
struct TouchPoint
{
public int CornerIndex { set; get; }
public SKPoint Offset { set; get; }
}
Dictionary<long, TouchPoint> touchPoints = new Dictionary<long, TouchPoint>();
// Drawing objects
SKPaint cornerStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 10
};
SKPaint edgeStroke = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 2
};
// this constructor for profile image
public PhotoCropperCanvasView(SKBitmap bitmap, float? aspectRatio = null)
{
this.bitmap = bitmap;
SKRect bitmapRect = new SKRect(0, 0, bitmap.Width, bitmap.Height);
croppingRect = new CroppingRectangle(bitmapRect, aspectRatio);
touchEffect.TouchAction += OnTouchEffectTouchAction;
}
// this constructor for post images
public PhotoCropperCanvasView()
{
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = nameof(bitmap))
{
base.OnPropertyChanged(propertyName);
if (bitmap != null)
{
SKRect bitmapRect = new SKRect(0, 0, bitmap.Width, bitmap.Width);
croppingRect = new CroppingRectangle(bitmapRect, 1);
touchEffect.TouchAction += OnTouchEffectTouchAction;
}
}
protected override void OnParentSet()
{
base.OnParentSet();
// Attach TouchEffect to parent view
Parent.Effects.Add(touchEffect);
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
{
base.OnPaintSurface(args);
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Gray);
// Calculate rectangle for displaying bitmap
float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);
float x = (info.Width - scale * bitmap.Width) / 2;
float y = (info.Height - scale * bitmap.Height) / 2;
SKRect bitmapRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);
canvas.DrawBitmap(bitmap, bitmapRect);
// Calculate a matrix transform for displaying the cropping rectangle
SKMatrix bitmapScaleMatrix = SKMatrix.MakeIdentity();
bitmapScaleMatrix.SetScaleTranslate(scale, scale, x, y);
// Display rectangle
SKRect scaledCropRect = bitmapScaleMatrix.MapRect(croppingRect.Rect);
canvas.DrawRect(scaledCropRect, edgeStroke);
// Display heavier corners
using (SKPath path = new SKPath())
{
path.MoveTo(scaledCropRect.Left, scaledCropRect.Top + CORNER);
path.LineTo(scaledCropRect.Left, scaledCropRect.Top);
path.LineTo(scaledCropRect.Left + CORNER, scaledCropRect.Top);
path.MoveTo(scaledCropRect.Right - CORNER, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top);
path.LineTo(scaledCropRect.Right, scaledCropRect.Top + CORNER);
path.MoveTo(scaledCropRect.Right, scaledCropRect.Bottom - CORNER);
path.LineTo(scaledCropRect.Right, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Right - CORNER, scaledCropRect.Bottom);
path.MoveTo(scaledCropRect.Left + CORNER, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom);
path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom - CORNER);
canvas.DrawPath(path, cornerStroke);
}
// Invert the transform for touch tracking
bitmapScaleMatrix.TryInvert(out inverseBitmapMatrix);
}
void OnTouchEffectTouchAction(object sender, TouchActionEventArgs args)
{
int i = 0;
SKPoint pixelLocation = ConvertToPixel(args.Location);
SKPoint bitmapLocation = inverseBitmapMatrix.MapPoint(pixelLocation);
switch (args.Type)
{
case TouchActionType.Pressed:
// Convert radius to bitmap/cropping scale
float radius = inverseBitmapMatrix.ScaleX * RADIUS;
// Find corner that the finger is touching
int cornerIndex = croppingRect.HitTest(bitmapLocation, radius);
if (cornerIndex != -1 && !touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = new TouchPoint
{
CornerIndex = cornerIndex,
Offset = bitmapLocation - croppingRect.Corners[cornerIndex]
};
touchPoints.Add(args.Id, touchPoint);
}
break;
case TouchActionType.Moved:
if (touchPoints.ContainsKey(args.Id))
{
TouchPoint touchPoint = touchPoints[args.Id];
croppingRect.MoveCorner(touchPoint.CornerIndex,
bitmapLocation - touchPoint.Offset);
InvalidateSurface();
}
break;
case TouchActionType.Released:
case TouchActionType.Cancelled:
if (touchPoints.ContainsKey(args.Id))
{
touchPoints.Remove(args.Id);
//map = CroppedBitmap;
}
break;
}
}
SKPoint ConvertToPixel(Xamarin.Forms.Point pt)
{
return new SKPoint((float)(CanvasSize.Width * pt.X / Width),
(float)(CanvasSize.Height * pt.Y / Height));
}
}
}
Since the case TouchActionType.Cancelled gets only triggerd once everytime the rectangel was moved, i thought i would set thew bindable Proeprty map to the Cropped bitmap property so that i can get the Cropped Image from the view obver a Binding to the viewmodel. This part works, however, when i activate the line map = CroppedBitmap the cropping rectangle can only be moved by opposite corners. So if i start moving it with the bottom right corner i con only use the top left or bottom right. If i leave the line map = CroppedBitman(249) deactivated i can move the rectangle on all corners at every times. I do not understand this behaviour.
the view:
<CarouselView Grid.Row="0"
IsSwipeEnabled="False"
x:Name="carousel"
Margin="0,-40,0,0"
CurrentItem="{Binding CurrentCutImage, Mode=TwoWay}"
CurrentItemChanged="CarouselView_CurrentItemChanged"
HorizontalScrollBarVisibility="Always"
IsScrollAnimated="True"
ItemsSource="{Binding ImageObjects}"
VerticalScrollBarVisibility="Always"
>
<CarouselView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:CutImages">
<Grid>
<bitmaps:PhotoCropperCanvasView bitmap="{Binding ImageSource }" map="{Binding MapSource, Mode=TwoWay}" >
</bitmaps:PhotoCropperCanvasView>
</Grid>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
and the VM:
public partial class CutImagesViewModel : ObservableObject
{
// == observable properties ==
[ObservableProperty]
public Collection<CutImages> imageObjects = new Collection<CutImages>();
[ObservableProperty]
CutImages currentCutImage;
[ObservableProperty]
public SKBitmap maps;
public CutImagesViewModel(Collection<SKBitmapImageSource> images)
{
foreach(var image in images)
{
ImageObjects.Add(new CutImages(image));
}
this.CurrentCutImage = this.ImageObjects.FirstOrDefault();
}
}
public partial class CutImages : ObservableObject
{
[ObservableProperty]
public SKBitmap imageSource;
[ObservableProperty]
public SKBitmap mapSource;
partial void OnMapSourceChanged(SKBitmap value)
{
if(!images.Contains(value))
{
images.Add(value);
}
}
[ObservableProperty]
Collection<SKBitmap> images = new Collection<SKBitmap>();
public CutImages(ImageSource imageSource)
{
SKBitmapImageSource sourceImage = (SKBitmapImageSource)imageSource;
SKBitmap image = sourceImage;
ImageSource = image;
}
}
How can I center the keyboard on the bottom of the screen?
I just have the sizes of the keyboard/PopupWindow after the board is already shown. Before calling show() every function returns 0.0 for the asked width. I could set the position correctly if I just knew the width before.
The keyboard size might change later, that's why I can't do it with a set size.
I am using the fx-onscreen-keyboard
My little service:
public class KeyboardService {
private double screenWidth = Screen.getPrimary().getVisualBounds().getWidth();
private double screenHight = Screen.getPrimary().getVisualBounds().getHeight();
private double keyboardPosX = 0.0;
private double keyboardPosY = 0.0;
private KeyBoardPopup keyboardPopup;
public KeyboardService() {
keyboardPopup = KeyBoardPopupBuilder.create().initLocale(Locale.GERMAN).build();
keyboardPopup.setAutoHide(true);
keyboardPopup.setConsumeAutoHidingEvents(false);
keyboardPopup.getKeyBoard().setScale(2.5);
keyboardPopup.getKeyBoard().setLayer(DefaultLayer.DEFAULT);
keyboardPopup.getKeyBoard().setOnKeyboardCloseButton((e) -> {
keyboardPopup.hide();
});
}
public void showKeyboard(Node node){
keyboardPosX = (screenWidth - keyboardPopup.getWidth())/2;
//keyboardPosX = (screenWidth - keyboardPopup.getKeyBoard().getWidth())/2;
keyboardPosY = screenHight;
keyboardPopup.show(node, keyboardPosX, keyboardPosY);
}}
The width of the keyboard is defined when the popup that holds it is laid out, what happens right after you call show.
The easy way to do this is by listening to the widthProperty of the KeyBoardPopup, to get the new value, and then moving the window of the popup accordingly.
This will do:
public KeyboardService() {
keyboardPopup = KeyBoardPopupBuilder.create().initLocale(Locale.GERMAN).build();
keyboardPopup.setAutoHide(true);
keyboardPopup.setConsumeAutoHidingEvents(false);
keyboardPopup.getKeyBoard().setScale(2.5);
keyboardPopup.getKeyBoard().setLayer(DefaultLayer.DEFAULT);
keyboardPopup.getKeyBoard().setOnKeyboardCloseButton((e) -> {
keyboardPopup.hide();
});
// listen to width changes and center
keyboardPopup.widthProperty().addListener((obs, ov, nv) -> {
keyboardPosX = (screenWidth - nv.doubleValue()) / 2d;
keyboardPopup.getScene().getWindow().setX(keyboardPosX);
});
}
public void showKeyboard(Node node) {
keyboardPosX = (screenWidth - keyboardPopup.getWidth())/2;
keyboardPosY = screenHeight;
keyboardPopup.show(node, keyboardPosX, keyboardPosY);
}
I am developing a program for video annotation. Objects seen in a video may be marked as objects of interest and if they are interacting, the user may draw a line between two annotations. On the visible level, bject annotations are basically transparent Rectangles and relations between them are Lines. At this point it is easy to compute the center of a rectangle, but I am not able to bind the start and end of a Line to the center of the corresponding Rectangle. I have tried the following approaches:
Creating two DoubleBindings inside the rectangle class that computes the center x and y:
private DoubleBinding centerXBinding = new DoubleBinding() {
#Override
protected double computeValue() {
return getX() + getWidth() / 2;
}
};
and then bound it to the newly created Line:
`currentRelation.startXProperty().bind(startShape.centerXBinding());`
in the controller…
The result is ok at first, the line start and end points are exactly where I want to haven them, but when a Rectangle gets dragged to another position, the line end does not move anywhere!
Does anyone see the problem?
UPDATE:
The movement of a Rectangle is done by computing an offset and updating the translation values like translateX:
public class MyRectangle extends Rectangle {
private double orgSceneX;
private double orgSceneY;
private double orgTranslateX;
private double orgTranslateY;
private void initEventHandling() {
this.setOnMousePressed(mousePress -> {
if (mousePress.getButton() == MouseButton.PRIMARY) {
orgSceneX = mousePress.getSceneX();
orgSceneY = mousePress.getSceneY();
orgTranslateX = ((MyRectangle) mousePress.getSource()).getTranslateX();
orgTranslateY = ((MyRectangle) mousePress.getSource()).getTranslateY();
mousePress.consume();
} else if (mousePress.getButton() == MouseButton.SECONDARY) {
System.out.println(LOG_TAG + ": right mouse button PRESS on " + this.getId() + ", event not consumed");
}
});
this.setOnMouseDragged(mouseDrag -> {
if (mouseDrag.getButton() == MouseButton.PRIMARY) {
double offsetX = mouseDrag.getSceneX() - orgSceneX;
double offsetY = mouseDrag.getSceneY() - orgSceneY;
double updateTranslateX = orgTranslateX + offsetX;
double updateTranslateY = orgTranslateY + offsetY;
this.setTranslateX(updateTranslateX);
this.setTranslateY(updateTranslateY);
mouseDrag.consume();
}
});
}
}
Your binding needs to invalidate when either the xProperty or widthProperty are invalidated (so that anything bound to it knows to recompute). You can do this by calling the bind method in the custom binding's constructor:
private DoubleBinding centerXBinding = new DoubleBinding() {
{
bind(xProperty(), widthProperty());
}
#Override
protected double computeValue() {
return getX() + getWidth() / 2;
}
};
Note you can also do
private DoubleBinding centerXBinding = xProperty().add(widthProperty().divide(2));
The choice between the two is really just a matter of which style you prefer.
Binding to the x and width properties assumes, obviously, that you are moving the rectangle by changing one or both of those properties. If you are moving the rectangle by some other means (e.g. by changing one of its translateX or translateY properties, or by altering its list of transformations), then you need to observe the boundsInParentProperty instead:
private DoubleBinding centerXBinding = new DoubleBinding() {
{
bind(boundsInParentProperty());
}
#Override
protected double computeValue() {
Bounds bounds = getBoundsInParent();
return (bounds.getMinX() + bounds.getMaxX()) / 2 ;
}
}
This binding will give the x-coordinate of the center of the rectangle in the parent's coordinate system (which is usually the coordinate system you want).
In an AS3 mobile App, I would like to have a menu of buttons or icons that slides up from the bottom. Using a SlideViewTransition, I can get part of what I want.
var transition:SlideViewTransition = new SlideViewTransition();
transition.direction = ViewTransitionDirection.UP;
transition.mode = SlideViewTransitionMode.COVER;
view.navigator.pushView(ShareView, null, null, transition);
This works, but it does not do two things that I need to do.
1) I want the new transition to only go up 1/2 of the screen so that the top part of the screen displays the view underneath.
2) I want the new view that covers to be partially transparent. By setting the alpha of the incoming view's contentGroup background alpha, the new view is transparent as it comes in. But, once it covers the view underneath the view becomes opaque.
this.contentGroup.setStyle('backgroundAlpha', 0.5);
Does anyone have any ideas of how I would have a view slide up 1/2 way and be transparent? I have no idea where to start, view skinning?, or subclass transition?, or use something in flash namespace instead of a spark view.
I decided it would be simplest to just use the lower level ActionScript and do the animation myself using methods that are normally applied to a Sprite, but in this case use a VGroup so that I could add spark elements to it. The following is the base class I wrote.
public class SlideUpDialog extends VGroup {
private var pctHeight:Number;
private var stopHeight:Number;
private var vertIncrement:Number;
public function SlideUpDialog(pctHeight:Number) {
super();
this.pctHeight = pctHeight;
if (stage) {
addedToStageHandler();
} else {
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
}
private function addedToStageHandler(event:Event=null) : void {
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
graphics.beginFill(0xEEEEEE, 0.8);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight * pctHeight);
graphics.endFill();
var bevel:BevelFilter = new BevelFilter(4, -45);
this.filters = [ bevel ];
x = 0;
y = stage.stageHeight;
stopHeight = y * (1 - pctHeight);
vertIncrement = y / 2 / 24 / 4;
}
public function open() : void {
addEventListener(Event.ENTER_FRAME, openFrameHandler);
}
private function openFrameHandler(event:Event) : void {
if (y > stopHeight) {
y -= vertIncrement;
} else {
removeEventListener(Event.ENTER_FRAME, openFrameHandler);
}
}
public function close() : void {
... the reverse of open
}
}
I want to change the colors of background button and the color of text onfocus
How can I make it?
class RoundedRectField extends Field {
// Layout values
private static final int CURVE_X = 12; // X-axis inset of curve
private static final int CURVE_Y = 12; // Y-axis inset of curve
private static final int MARGIN = 2; // Space within component boundary
// Static colors
private static final int TEXT_COLOR = 0xFFFFFF; // White
private static final int BORDER_COLOR = 0xFF8000; // dark gray
private static final int BACKGROUND_COLOR = 0xFFFFFF; // White
private static final int TEXT_COLOR_selected = 0xFF6DB6;
private static final int BORDER_COLOR_selected = 0xFF8000;
private static final int BACKGROUND_COLOR_selected = 0xCCCCCC;
boolean _focus = false;
private static String text_button;
// Point types array for rounded rectangle. Each point type
// corresponds to one of the colors in the colors array. The
// space marks the division between points on the top half of
// the rectangle and those on the bottom.
private static final byte[] PATH_POINT_TYPES = {
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, };
// Colors array for rounded rectangle gradient. Each color corresponds
// to one of the points in the point types array. Top light, bottom black.
private static final int[] PATH_GRADIENT = { 0xFF8000, 0xFF8000, 0xFF8000,
0xFF8000, 0xFF8000, 0xFF8000,
0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500 };
// Center our readonly field in the space we're given.
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | READONLY);
this.text_button = text_button;
}
// This field in this demo has a fixed height.
public int getPreferredHeight() {
return 70;
}
// This field in this demo has a fixed width.
public int getPreferredWidth() {
return 240;
}
// When layout is requested, return our height and width.
protected void layout(int width, int height) {
setExtent(getPreferredWidth(), getPreferredHeight());
}
// When painting is requested, do it ourselves.
protected void paint(Graphics g) {
// Clear this area to white background, fully opaque.
g.clear();
g.setGlobalAlpha(255);
g.setBackgroundColor(BACKGROUND_COLOR);
// Drawing within our margin.
int width = getPreferredWidth() - (MARGIN * 2);
int height = getPreferredHeight() - (MARGIN * 2);
// Compute paths for the rounded rectangle. The 1st point (0) is on
// the left
// side, right where the curve in the top left corner starts. So the
// top left
// corner is point 1. These points correspond to our static arrays.
int[] xPts = { 0, 0, CURVE_X, width - CURVE_X, width, width, width,
width, width - CURVE_X, CURVE_X, 0, 0 };
int[] yPts = { CURVE_Y, 0, 0, 0, 0, CURVE_Y, height - CURVE_Y,
height, height, height, height, height - CURVE_Y };
// Draw the gradient fill.
g.drawShadedFilledPath(xPts, yPts, PATH_POINT_TYPES, PATH_GRADIENT,
null);
// Draw a rounded rectangle for the outline.
// I think that drawRoundRect looks better than drawPathOutline.
g.setColor(BORDER_COLOR);
g.drawRoundRect(0, 0, width, height, CURVE_X * 2, CURVE_Y * 2);
// Place some text in the center.
Font font = Font.getDefault().derive(Font.PLAIN, 9, Ui.UNITS_pt);
int textWidth = font.getAdvance(text_button);
int textHeight = font.getHeight();
g.setColor(TEXT_COLOR);
g.setFont(font);
g.drawText(text_button, (width / 2) - (textWidth / 2) - MARGIN,
(height / 2) - (textHeight / 2) - MARGIN);
}
protected void onFocus(int direction) {
_focus = true;
Dialog.alert("dcd");
invalidate();
super.onFocus(direction);
}
protected void onUnfocus() {
_focus = false;
invalidate();
super.onUnfocus();
}
}
You can do it several ways. One popular way is to provide custom focus drawing in the paint() method, which you already override.
You should be able to do this (I'm assuming you declared the _selected colors for the focused state):
if (isFocus()) {
g.setBackgroundColor(BACKGROUND_COLOR_selected);
else {
g.setBackgroundColor(BACKGROUND_COLOR);
}
...
if (isFocus()) {
g.setColor(TEXT_COLOR_selected);
} else {
g.setColor(TEXT_COLOR);
}
Those lines go in paint(), right where you are currently calling g.setBackgroundColor and g.setColor(TEXT_COLOR).
Then, you would override drawFocus() and do nothing, since your focus drawing is handled in paint():
protected void drawFocus(Graphics graphics, boolean on) {
// override superclass implementation and do nothing
}
Finally, you need to make your Field focusable, in order to ever receive focus. You can do so like this:
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | FOCUSABLE);
this.text_button = text_button;
}
If you need the field to be dynamically focusable (sometimes focusable, or sometimes not focusable), then you could implement this method:
public boolean isFocusable() {
But, if the field is always focusable, then using the FOCUSABLE flag in your constructor will work. I tested this out, and I saw the text color change with focus (on a OS 5.0 9550).