I have written a custom renderer for iOS to create a chatbubble programmatically. The main chat bubble inherits from ContentView, this is done to allow me to add a Label as it's sole child. For the most part it works well but as soon as there are multiple lines of text it behaves very strangely, namely the text overflows and doesn't respect the parents boundaries(reminds me of when I was a teenager).
When I replace this custom control with a regular frame everything seems to work well. I am calculating the required height by overriding the Draw(CGRect ) call
Code here :
ppublic class ChatBubbleRenderer : VisualElementRenderer<ChatBubbleFrame>
{
public ChatBubbleRenderer()
{
this.ClipsToBounds = false;
}
public override void Draw(CGRect rect1)
{
var rect = Frame;
var currentContext = UIGraphics.GetCurrentContext();
var properRect = AdjustForThickness(rect);
HandleShapeDraw(currentContext, properRect);
}
protected RectangleF AdjustForThickness(CGRect rect)
{
var x = rect.X + Element.Padding.Left;
var y = rect.Y + Element.Padding.Top;
var width = rect.Width - ((Element.Padding.Left + (int)Element.StrokeWidth) * 2);
var height = rect.Height - ((Element.Padding.Top + (int)Element.StrokeWidth) * 2);
return new RectangleF((float)x, (float)y, (float)width, (float)height);
}
protected void HandleShapeDraw(CGContext currentContext, RectangleF rect)
{
var path = new CGPath();
var cr = (float)Element.CornerRadius;
var hl = (float)Element.HandleLength;
var x = rect.Left+hl;
var y = (float)rect.Top;
var startLeft = x;
var startTop = y;
var startRight = rect.Right - hl;
var startBottom = rect.Bottom ;
var centerX = x + cr;
var centerY = y + cr;
var width = (float)rect.Width - hl - hl;
var height = (float)rect.Height ;
if (Element.HandleIsBottomRight)
{
path.AddArc(centerX, centerY, cr, (float)Math.PI, (float)Math.PI * 3f / 2f, false);
x = startRight - cr;
//line to top right arc
path.AddLineToPoint(x, y);
//top right arc
centerX = x;
path.AddArc(centerX, centerY, cr, (float)Math.PI * 3f / 2f, 0, false);
x = x + cr;
//bring y down to top of triangle
y = y + height;
path.AddLineToPoint(x, y);
//chat handle
x = x + hl;
y = y + hl;
path.AddLineToPoint(x, y);
//to bottom left arc
x = startLeft + cr;
path.AddLineToPoint(x, y);
//bottom left arc
centerX = x;
centerY = y - cr;
path.AddArc(centerX, centerY, cr, (float)Math.PI / 2f, (float)Math.PI, false); //false to rotate clockwise
}
else
{
//line to top right arc
path.MoveToPoint(x, y);
x = startRight - cr;
path.AddLineToPoint(x, y);
//top right arc
centerX = x;
path.AddArc(centerX, centerY, cr, (float)Math.PI * 3f / 2f, 0, false);
//line to bottom right arc
x = x + cr;
y = y + height - cr;
path.AddLineToPoint(x, y);
//bottom right arc
centerX = x - cr;
centerY = y;
path.AddArc(centerX, centerY, cr, 0f, (float)Math.PI / 2f, false);
//line to bottom left arc
x = startLeft + cr;
y = y + cr;
path.AddLineToPoint(x, y);
//bottom left arc
centerX = x;
centerY = y - cr;
path.AddArc(centerX, centerY, cr, (float)Math.PI / 2f, (float)Math.PI, false); //false to rotate clockwise
//line to handle
x = startLeft;
y = startTop + hl;
path.AddLineToPoint(x, y);
x = x - hl;
y = startTop;
path.AddLineToPoint(x, y);
}
path.CloseSubpath();
HandleStandardDraw(currentContext, rect, () => currentContext.AddPath(path));
}
/// <summary>
/// A simple method for handling our drawing of the shape. This method is called differently for each type of shape
/// </summary>
/// <param name="currentContext">Current context.</param>
/// <param name="rect">Rect.</param>
/// <param name="createPathForShape">Create path for shape.</param>
/// <param name="lineWidth">Line width.</param>
protected virtual void HandleStandardDraw(CGContext currentContext, RectangleF rect, Action createPathForShape, float? lineWidth = null)
{
currentContext.SetLineWidth(lineWidth ?? Element.StrokeWidth);
currentContext.SetFillColor(Element.InnerColor.ToCGColor());
currentContext.SetStrokeColor(Element.StrokeColor.ToCGColor());
createPathForShape();
currentContext.DrawPath(CoreGraphics.CGPathDrawingMode.FillStroke);
}
}
Chat bubble Code
public class ChatBubbleFrame : ContentView
{
public ChatBubbleFrame()
{
}
public static readonly BindableProperty HandleIsBottomRightProperty = BindableProperty.Create(
propertyName: "HandleIsBottomRight",
returnType: typeof(bool),
declaringType: typeof(ChatBubbleFrame),
defaultValue: true
);
public static readonly BindableProperty InnerColorProperty = BindableProperty.Create(
propertyName: "InnerColor",
returnType: typeof(Color),
declaringType: typeof(ChatBubbleFrame),
defaultValue: Color.Default
);
public static readonly BindableProperty HandleLengthProperty = BindableProperty.Create(
propertyName: "HandleLength",
returnType: typeof(float),
declaringType: typeof(ChatBubbleFrame),
defaultValue: 2f
);
public static readonly BindableProperty StrokeColorProperty = BindableProperty.Create(
propertyName: "StrokeColor",
returnType: typeof(Color),
declaringType: typeof(ChatBubbleFrame),
defaultValue: Color.Default
);
public static readonly BindableProperty StrokeWidthProperty = BindableProperty.Create(
propertyName: "StrokeWidth",
returnType: typeof(float),
declaringType: typeof(ChatBubbleFrame),
defaultValue: 1f
);
public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(
propertyName: "CornerRadius",
returnType: typeof(float),
declaringType: typeof(ChatBubbleFrame),
defaultValue: 0f
);
public bool HandleIsBottomRight
{
get { return (bool)GetValue(HandleIsBottomRightProperty); }
set { SetValue(HandleIsBottomRightProperty, value); }
}
public float HandleLength
{
get { return (float)GetValue(HandleLengthProperty); }
set { SetValue(HandleLengthProperty, value); }
}
public Color StrokeColor
{
get { return (Color)GetValue(StrokeColorProperty); }
set { SetValue(StrokeColorProperty, value); }
}
public Color InnerColor
{
get { return (Color)GetValue(InnerColorProperty); }
set { SetValue(InnerColorProperty, value); }
}
public float StrokeWidth
{
get { return (float)GetValue(StrokeWidthProperty); }
set { SetValue(StrokeWidthProperty, value); }
}
public float CornerRadius
{
get
{
return (float)GetValue(CornerRadiusProperty);
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
}
Xaml snippet
<controls:ChatBubbleFrame x:Name="OtherUserBubble"
HandleIsBottomRight="False"
StrokeColor="{StaticResource Color13}"
InnerColor="White"
StrokeWidth="1"
CornerRadius="10"
HandleLength="7"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="-1,10,10,10"
Margin="35,0,10,0"
Grid.Column="0">
<StackLayout Margin="17,10,12,18">
<Label Text="{Binding Entity.CommentText }"
Style="{StaticResource Typo23}"
LineBreakMode="WordWrap"
Margin="0,6,10,0" />
</StackLayout>
</controls:ChatBubbleFrame>
Screenshots of a good message bubble and a terrible message bubble
Related
I want the green dot to follow the touch point in a circular path, but it doesn't seem to be doing it right.
It seems like there is an unwanted offset somewhere but I can't find it on my own for quite some time.
Here is my code:
#Preview
#Composable
fun Test() {
val touchPoint = remember { mutableStateOf(Offset.Zero) }
Scaffold {
Column() {
Box(Modifier.height(100.dp).fillMaxWidth().background(Color.Blue))
Layout(
modifier = Modifier.aspectRatio(1f).fillMaxSize(),
content = {
Box(
Modifier
.size(48.dp)
.clip(CircleShape)
.background(Color.Green)
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consumeAllChanges()
touchPoint.value += dragAmount
}
)
}
)
}
) { measurables, constraints ->
val dot = measurables.first().measure(constraints.copy(minHeight = 0, minWidth = 0))
val width = constraints.maxWidth
val height = constraints.maxHeight
val centerX = width / 2
val centerY = height / 2
val lengthFromCenter = width / 2 - dot.width / 2
val touchX = touchPoint.value.x
val touchY = touchPoint.value.y
layout(width, height) {
// I planned to achieve the desired behaviour with the following steps:
// 1. Convert cartesian coordinates to polar ones
val r = sqrt(touchX.pow(2) + touchY.pow(2))
val angle = atan2(touchY.toDouble(), touchX.toDouble())
// 2. Use fixed polar radius
val rFixed = lengthFromCenter
// 3. Convert it back to cartesian coordinates
val x = rFixed * cos(angle)
val y = rFixed * sin(angle)
// 4. Layout on screen
dot.place(
x = (x + centerX - dot.width / 2).roundToInt(),
y = (y + centerY - dot.height / 2).roundToInt()
)
}
}
Box(Modifier.fillMaxSize().background(Color.Blue))
}
}
}
I'm definitely missing something but don't know what exactly. What am I doing wrong?
touchPoint.value += dragAmount
Is in pixel values, and you're updating the position of the dot with pixel values, where it requires dp values. If you update that with
private fun Float.pxToDp(context: Context): Dp = // Float or Int, depends on the value you have, or Double
(this / context.resources.displayMetrics.density).dp
The amount with which it will be moved, will be smaller and reflect the dragging made by the user
You can easily achieve this by using some math:
#Composable
fun CircularView(
content: #Composable () -> Unit
) {
var middle by remember {
mutableStateOf(Offset.Zero)
}
var size by remember {
mutableStateOf(0.dp)
}
var dragAngle by remember {
mutableStateOf(0f)
}
Canvas(modifier = Modifier.size(size)) {
drawCircle(
color = Color.Red,
center = middle,
style = Stroke(1.dp.toPx())
)
}
Layout(
content = content,
modifier = Modifier.pointerInput(true) {
detectDragGestures(
onDrag = { change, _ ->
change.consumeAllChanges()
val positionOfDrag = change.position
val previousPosition = change.previousPosition
dragAngle += atan2(
positionOfDrag.x - middle.x,
positionOfDrag.y - middle.y
) - atan2(
previousPosition.x - middle.x,
previousPosition.y - middle.y
)
}
)
}
) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
val layoutWidth = constraints.maxWidth
val layoutHeight = constraints.maxHeight
layout(layoutWidth, layoutHeight) {
val childCount = placeables.size
if (childCount == 0) return#layout
val middleX = layoutWidth / 2f
val middleY = layoutHeight / 2f
middle = Offset(middleX, middleY)
val angleBetween = 2 * PI / childCount
val radius =
min(
layoutWidth - (placeables.maxByOrNull { it.width }?.width ?: 0),
layoutHeight - (placeables.maxByOrNull { it.height }?.height ?: 0)
) / 2
size = (radius * 2).toDp()
placeables.forEachIndexed { index, placeable ->
val angle = index * angleBetween - PI / 2 - dragAngle
val x = middleX + (radius) * cos(angle) - placeable.width / 2f
val y = middleY + (radius) * sin(angle) - placeable.height / 2f
placeable.placeRelative(x = x.toInt(), y = y.toInt())
}
}
}
}
On the calling side:
CircularView {
repeat(10) {
Box(
modifier = Modifier
.background(
Color(
red = random.nextInt(255),
green = random.nextInt(255),
blue = random.nextInt(255)
), shape = CircleShape
)
.size(50.dp),
contentAlignment = Alignment.Center
) {
Text(text = it.toString(), fontSize = 12.sp, color = Color.White)
}
}
}
Currently, I'm using offset and rotation to position elements in KonvaJS in a circle. Is there another method that would still layout the elements in a circle without rotating the text (eg like a clock.)
Output looks like this:
Code looks like this:
function drawNumber(radius, number, step) {
var segmentDegree = 360/16
var rotation = -90 + step * segmentDegree
var label = new Konva.Text({
x: patternOriginX,
y: patternOriginY,
text: number.toString(),
fontSize: 12,
fill: '#636363',
rotation: rotation
});
label.offsetX(-radius)
return label
}
You can use trigonometry to find the position of the text on its angle:
var centerX = stage.width() / 2;
var centerY = stage.height() / 2;
var QUANTITY = 10;
var RADIUS = 50;
var dAlhpa = Math.PI * 2 / QUANTITY;
for (var i = 0; i < QUANTITY; i++) {
var alpha = dAlhpa * i;
var dx = Math.cos(alpha) * RADIUS;
var dy = Math.sin(alpha) * RADIUS;
layer.add(new Konva.Text({
x: centerX + dx,
y: centerY + dy,
text: i.toString()
}))
}
Demo: https://jsbin.com/fizucotaxe/1/edit?html,js,output
I used that examples
http://developer.xamarin.com/recipes/ios/graphics_and_drawing/core_text/draw_unicode_text_with_coretext/
Using MonoTouch.CoreText to draw text fragments at specific coordinaates
to draw a text line over UIView.
Now I need to extend it to draw multi-line text. Basically is simple. In the Draw() method I split the multi-line string Text on "\n" and than I call DrawTextLine() for any single line adding a newLineDY to Y.
The only problem is that any new line draw starts after the X draw end of the previous one:
aaa
bbb
ccc
How to avoid the X displacement? Can be reset? How? I try appling a negative DX for any line, but I don't know the right value to apply.
private const float newLineDY = 40;
public override void Draw()
{
string[] lines = Text.Split("\n".ToCharArray());
float lx = X;
float ly = Y;
foreach (string line in lines)
{
DrawTextLine(line, lx, ly);
//lx -= 100; // negative DX
ly += newLineDY;
}
}
private void DrawTextLine(string text, float x, float y)
{
CGContext gctx = UIGraphics.GetCurrentContext();
gctx.SaveState();
gctx.TranslateCTM(x, y);
//gctx.TextPosition = new CGPoint(x, y);
gctx.ScaleCTM(1, -1);
//gctx.RotateCTM((float)Math.PI * 315 / 180);
gctx.SetFillColor(UIColor.Black.CGColor);
var attributedString = new NSAttributedString(text,
new CTStringAttributes
{
ForegroundColorFromContext = true,
Font = new CTFont("Arial", 24)
});
using (CTLine textLine = new CTLine(attributedString))
{
textLine.Draw(gctx);
}
gctx.RestoreState();
}
Thaks!
I have solved using attributedString.DrawString(new CGPoint(x, y)), a much simpler API, as suggested here
http://monotouch.2284126.n4.nabble.com/Using-MonoTouch-CoreText-to-draw-text-fragments-at-specific-coordinates-td4658531.html
So my code became:
private const float newLineDY = 40;
public override void Draw()
{
string[] lines = Text.Split("\n".ToCharArray());
float lx = X;
float ly = Y;
foreach (string line in lines)
{
DrawTextLine(line, lx, ly);
ly += newLineDY;
}
}
private void DrawTextLine(string text, float x, float y)
{
NSAttributedString attributedString = new NSAttributedString(
text,
new CTStringAttributes
{
ForegroundColorFromContext = true,
Font = new CTFont("Arial", 24)
});
attributedString.DrawString(new CGPoint(x, y));
}
Ok, so I'm working with my first serious experience in XNA. Here's my current task: Download a file. From that file parse a list of "shapes." Each "shape" contains a list of points. These points are the vertexes of lines to be drawn on a map. In fact, it is a county map of the entire United States, so the number of "shapes" is not trivial. We need to be able to zoom in and out of this map, so it needs to be a 2d rendering in 3d space. I want to know what the best strategy for this would be.
I've tried simply using DrawUserIndexedPrimitives, but the draw function takes way too long to process this.
So then I thought I'd try drawing to a series of RenderTarget2D's in LoadContent, and just saving those textures for drawing in the Draw function. But so far all I can seem to get is a series of purple boxes.
EDIT:
Here's my current code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Runtime.InteropServices;
namespace First
{
class FirstShape : DrawableGameComponent
{
const int kSizeMultiplier = 10;
public FirstShape(string inFilePath, Game inGame) : base(inGame)
{
DrawOrder = 1000;
mFile = inFilePath;
}
protected override void LoadContent()
{
mSpriteBatch = new SpriteBatch(GraphicsDevice);
FirstMemoryStream stream = FirstMemoryStream.MakeSeekableStream(File.OpenRead(mFile));
// skip headers
stream.Seek(100, 0);
for (int i = 0; stream.Position != stream.Length; i++)
{
stream.Seek(stream.Position + 12, 0);
double minX = stream.ReadDouble();
double minY = stream.ReadDouble();
double maxX = stream.ReadDouble();
double maxY = stream.ReadDouble();
int numParts = stream.ReadInt();
int numPoints = stream.ReadInt();
VertexPositionColor[] points = new VertexPositionColor[numPoints];
stream.Seek(stream.Position + (4 * numParts), 0);
int top, bottom, left, right;
float x2, y2;
x2 = (float)stream.ReadDouble();
y2 = (float)stream.ReadDouble();
Vector2 projectedPoint = Vertex(x2, y2);
left = right = (int)Math.Round(projectedPoint.X * kSizeMultiplier);
top = bottom = (int)Math.Round(maxY - (projectedPoint.Y * kSizeMultiplier));
points[0].Position.X = left;
points[0].Position.Y = top;
for (int j = 1; j < points.Length; j++)
{
float x1 = x2;
float y1 = y2;
x2 = (float)stream.ReadDouble();
y2 = (float)stream.ReadDouble();
Vector2 p1 = Vertex(x1, y1);
Vector2 p2 = Vertex(x2, y2);
p1.X *= kSizeMultiplier;
p1.Y *= kSizeMultiplier;
p2.X *= kSizeMultiplier;
p2.Y *= kSizeMultiplier;
points[j].Position.X = (int)Math.Round(p2.X);
points[j].Position.Y = (int)Math.Round(maxY - p2.Y);
if (points[j].Position.X < left)
left = (int)points[j].Position.X;
if (points[j].Position.X > right)
right = (int)points[j].Position.X;
if (points[j].Position.Y < top)
top = (int)points[j].Position.Y;
if (points[j].Position.Y > bottom)
bottom = (int)points[j].Position.Y;
}
if (mTopLeft.X == 0 || mTopLeft.X > left)
mTopLeft.X = left;
if (mTopLeft.Y == 0 || mTopLeft.Y > top)
mTopLeft.Y = top;
for (int j = 0; j < points.Length; j++)
{
points[j].Color = Color;
}
int width = (right - left) + 1;
int height = (bottom - top) + 1;
mTextures.Add(new FirstImage(GraphicsDevice, width, height, new Vector2(left, top)));
GraphicsDevice.SetRenderTarget(mTextures.Last());
GraphicsDevice.Indices = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, points.Length, BufferUsage.None);
GraphicsDevice.SetVertexBuffer(new VertexBuffer(GraphicsDevice, VertexPositionColor.VertexDeclaration, points.Length, BufferUsage.None));
BasicEffect basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.LightingEnabled = false;
basicEffect.VertexColorEnabled = true;
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineStrip, 0, 0, points.Length, 0, points.Length - 1);
}
}
GraphicsDevice.SetRenderTarget(null);
stream.Close();
for (int i = 0; i < mTextures.Count; i++)
{
mTextures[i].Position -= mTopLeft;
}
}
public override void Draw(GameTime inTime)
{
mSpriteBatch.Begin();
for(int i = 0; i < mTextures.Count; i++)
{
mSpriteBatch.Draw(mTextures[i], mTextures[i].Position, Color.White);
}
mSpriteBatch.End();
}
private Vector2 Vertex(float inX, float inY)
{
return FirstProjector.Project(new Vector2(inX, inY));
}
public Color Color { get; set; }
private string mFile;
private List<FirstImage> mTextures = new List<FirstImage>();
private SpriteBatch mSpriteBatch;
private Vector2 mTopLeft = new Vector2(0.0f, 0.0f);
private Vector2 mBottomRight = new Vector2(0.0f, 0.0f);
}
class FirstImage : RenderTarget2D
{
public FirstImage(GraphicsDevice inDevice, int inWidth, int inHeight, Vector2 inPosition) : base(inDevice, inWidth, inHeight, false, SurfaceFormat.Color, DepthFormat.None)
{
Position = inPosition;
}
public Vector2 Position {get; set;}
}
}
Could someone help me figure out how to draw a route on RichMapField,
i am able to draw on a MapField.
I want to use RichMapField, because i can use the MapDataModel to add more than one marker dynamically.
Updated Code:
This is my attempt at writing code to display route from A to B on a RichMapField, all I am getting is a dot on the map. Could someone please help me with this:
class MapPathScreen extends MainScreen {
MapControl map;
Road mRoad = new Road();
RichMapField mapField = MapFactory.getInstance().generateRichMapField();
public MapPathScreen() {
double fromLat = 47.67, fromLon = 9.38, toLat =47.12, toLon = 9.47;
/* double fromLat = 49.85, fromLon = 24.016667;
double toLat = 50.45, toLon = 30.523333;
*/
String url = RoadProvider.getUrl(fromLat, fromLon, toLat, toLon);
InputStream is = getConnection(url);
mRoad = RoadProvider.getRoute(is);
map = new MapControl(mapField);
add(new LabelField(mRoad.mName));
add(new LabelField(mRoad.mDescription));
add(map);
}
protected void onUiEngineAttached(boolean attached) {
super.onUiEngineAttached(attached);
if (attached) {
map.drawPath(mRoad);
}
}
private InputStream getConnection(String url) {
HttpConnection urlConnection = null;
InputStream is = null;
try {
urlConnection = (HttpConnection) Connector.open(url);
urlConnection.setRequestMethod("GET");
is = urlConnection.openInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return is;
}
protected boolean keyDown(int keycode, int time)
{
MapAction action=mapField.getAction();
StringBuffer sb = new StringBuffer();
// Retrieve the characters mapped to the keycode for the current keyboard layout
Keypad.getKeyChars(keycode, sb);
// Zoom in
if(sb.toString().indexOf('i') != -1)
{
action.zoomIn();
return true;
}
// Zoom out
else if(sb.toString().indexOf('o') != -1)
{
action.zoomOut();
return true;
}
return super.keyDown(keycode, time);
}
}
class MapControl extends net.rim.device.api.lbs.maps.ui.MapField {
Bitmap bmp = null;
MapAction action;
MapField map = new MapField();
RichMapField mapRich;
Road road;
public MapControl(RichMapField mapRich)
{
this.mapRich = mapRich;
}
public void drawPath(Road road) {
if (road.mRoute.length > 0) {
Coordinates[] mPoints = new Coordinates[] {};
for (int i = 0; i < road.mRoute.length; i++) {
Arrays.add(mPoints, new Coordinates(road.mRoute[i][1],
road.mRoute[i][0], 0));
}
double moveToLat = mPoints[0].getLatitude()
+ (mPoints[mPoints.length - 1].getLatitude() - mPoints[0].getLatitude()) / 2;
double moveToLong = mPoints[0].getLongitude()
+ (mPoints[mPoints.length - 1].getLongitude() - mPoints[0].getLongitude()) / 2;
Coordinates moveTo = new Coordinates(moveToLat, moveToLong, 0);
action = this.getAction();
action.setZoom(15);
action.setCentreAndZoom(new MapPoint(moveToLat,moveToLong), 15);
bmp = new Bitmap(500, 500);
bmp.createAlpha(Bitmap.ALPHA_BITDEPTH_8BPP);
Graphics g = Graphics.create(bmp);
int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
XYPoint point = new XYPoint();
Coordinates c = new Coordinates(mPoints[0].getLatitude(),mPoints[0].getLongitude(),0);
map.convertWorldToField(c, point);
x1=point.x;
y1 = point.y;
g.fillEllipse(x1, y1, x1, y1 + 1, x1 + 1, y1, 0, 360);
for (int i = 1; i < mPoints.length; i++) {
XYPoint point1 = new XYPoint();
Coordinates c1 = new Coordinates(mPoints[i].getLatitude(),mPoints[i].getLongitude(),0);
map.convertWorldToField(c1, point1);
x2 = point1.x;
y2 = point1.y;
g.setColor(Color.GREEN);
//g.fillEllipse(x1, y1, x1, y1 + 1, x1 + 1, y1, 0, 360);
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
}
}
protected void paint(Graphics g) {
super.paint(g);
if (bmp != null) {
g.setGlobalAlpha(100);
g.drawBitmap(0, 0, bmp.getWidth(), bmp.getHeight(), bmp, 0, 0);
}
}