I am trying to find the text which is being clicked in a canvas displayed on a imageview. I've overridden the on touch on imageview.So this is the approach I used. I've stored all the x,y coordinates on the canvas where I've displayed text along with the text and Paint. when a Motion_event.ACTION_DOWN is triggered. I get the event coordinates and check if the event coordinates are with in the text bounds. Here is the code snippet.
float xRatio=(float)mTempBitmap.getWidth()/(float)mEditImageView.getWidth();
float yRatio= (float)mTempBitmap.getHeight()/(float)mEditImageView.getHeight();
mEditImageView.setImageBitmap(mTempBitmap);
for (TextItem textItem : mTextItemStack) {
Rect tempRect = new Rect();
textItem.getPaint().getTextBounds(textItem.getTextContent(), 0, textItem.getTextContent().length() - 1, tempRect);
tempRect.offset(textItem.getXCoordinate(), textItem.getYCoordinate());
if (tempRect.contains((int)(event.getX()*xRatio), (int) (event.getY()*yRatio))) {
mtextItemInFocus = textItem;
break;
}
}
But the problem arises in the getTextBounds() call.Here is the Doc for getTextBounds.
Return in bounds (allocated by the caller) the smallest rectangle that encloses all of the characters, with an implied origin at (0,0).
But I get negative bounds as a result. Clearly my understanding of the getTextBounds is wrong. Please correct me. I welcome alternate approach for doing the same.
Related
I'm trying to draw a text to the screen and then have a rectangle in the same spot to account for knowing when the user clicks within it (and clicks the text). Problem is when I put the text in one spot on the screen and the rectangle in the same spot, it apparently isn't in the same spot. Is there some setting I need to set somewhere?
private final String[] options = {"Start", "High Scores", "Help", "Quit"};
public void draw(final Canvas g)
{
for (int i = 0; i < options.length; i++)
{
width = HORIZONTALOFFSET * 3 + spaceInvadersTitle.getWidth();
height = GamePanel.getScreenHeight() / 4 - (75 * 2 + TEXT_SIZE) / 2 + i * 75;
rect[i] = new Rect(width, height, width + 325, height + TEXT_SIZE);
g.drawRect(rect[i], paint);
g.drawText(options[i], width, height, paint);
}
}
FYI "width" and "height" are more like x- and y-coords. The horizontal alignment is not the problem - just the vertical. If you'll notice from the code, I'm setting the starting x- and y-coordinates for the drawText and drawRect the same exact position, but that's not how they're showing on the screen. Instead it seems the drawText is using that position as a lowerleft anchorpoint or something like that. Is that how it works? If so, how do I change that?
Also, if you have any suggestions on how to approach listening for when the user clicks on a drawn text, I'm all ears. This is the easiest way I could think of, and how I do it in regular desktop Java.
I want to set a vertical line in center of LineChart like this:
When scrolling to each point, it can notify to change the date below (the orange date field). And it can move left or right programmatically by click on arrow button.
Currently, I can set viewport and allow moving to center with this code:
LineData data = new LineData(xVals, dataSets);
mChart.setScaleMinima((float) data.getXValCount() / 7f, 1f);
mChart.moveViewTo(0, 7, YAxis.AxisDependency.LEFT);
And get the result:
How can I draw and set a vertical line like above?
Update:
For the listener, I think OnChartGestureListener onChartTranslate(MotionEvent me, float dX, float dY) may help. What I need is the distance between 2 points and how to calculate how many points are in current view port. Does anyone know that?
Have you tried using getEntryByTouchPoint on your chart supplying the x and y coordinates of the center of the chart?
public Entry getEntryByTouchPoint(float x, float y)
returns the Entry object displayed at the touched position of the chart
Take a look at the method
protected void drawGridBackground(Canvas c) {
in the BarLineChartBase class (parent for a LineChart). In that method you have all data to draw your line right in the middle.
Something like this
RectF rectF = mViewPortHandler.getContentRect();
float xMiddle = (rectF.right - rectF.left)/2;
Paint p = new Paint();
p.setColor(Color.BLACK);
c.drawLine(xMiddle, rectF.bottom, xMiddle, rectF.top, p);
Maybe it's too late but here is my answer. It's encoded in Swift using Charts (MPAndroidCharts port for iOS) but API is 99% the same ;)
let verticalPointEntry = ChartDataEntry(x: xValue, y: yValue)
let dataSet = LineChartDataSet(values: [verticalPointEntry], label: "")
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
dataSet.setDrawHighlightIndicators(true)
dataSet.drawHorizontalHighlightIndicatorEnabled = false
dataSet.highlightColor = UIColor.white
dataSet.highlightLineWidth = 1
let highlightPoint = Highlight(x: xValue, y: yValue, dataSetIndex: datasetIndex)
self.highlightValues([highlightPoint])
// "yourNormalDataSet" is your regular dataSet in which you want to display vertical line over it
let chartData = LineChartData(dataSets: [yourNormalDataSet, dataSet])
self.data = chartData
self.data?.notifiyDataChanged()
self.notifyDataSetChanged
This will display a vercital line over the point defined by your xValue variable.
Hope it helps!
I am trying to gain some more familiarity with the Android SurfaceView class, and in doing so am attempting to create a simple application that allows a user to move a Bitmap around the screen. The troublesome part of this implementation is that I am also including the functionality that the user may drag the image again after it has been placed. In order to do this, I am mapping the bitmap to a simple set of coordinates that define the Bitmap's current location. The region I am mapping the image to, however, does not match up with the image.
The Problem
After placing an image on the SurfaceView using canvas.drawBitmap(), and recording the coordinates of the placed image, the mapping system that I have set up misinterprets the Bitmap's coordinates somehow and does not display correctly. As you can see in this image, I have simply used canvas.drawLine() to draw lines representing the space of my touch region, and the image is always off and to the right:
The Code
Here, I shall provide the relevant code excerpts to help answer my question.
CustomSurface.java
This method encapsulates the drawing of the objects onto the canvas. The comments clarify each element:
public void onDraw(Canvas c){
//Simple black paint
Paint paint = new Paint();
//Draw a white background
c.drawColor(Color.WHITE);
//Draw the bitmap at the coordinates
c.drawBitmap(g.getResource(), g.getCenterX(), g.getCenterY(), null);
//Draws the actual surface that is receiving touch input
c.drawLine(g.left, g.top, g.right, g.top, paint);
c.drawLine(g.right, g.top, g.right, g.bottom, paint);
c.drawLine(g.right, g.bottom, g.left, g.bottom, paint);
c.drawLine(g.left, g.bottom, g.left, g.top, paint);
}
This method encapsulates how I capture touch events:
public boolean onTouchEvent(MotionEvent e){
switch(e.getAction()){
case MotionEvent.ACTION_DOWN:{
if(g.contains((int) e.getX(), (int) e.getY()))
item_selected = true;
break;
}
case MotionEvent.ACTION_MOVE:{
if(item_selected)
g.move((int) e.getX(), (int) e.getY());
break;
}
case MotionEvent.ACTION_UP:{
item_selected = false;
break;
}
default:{
//Do nothing
break;
}
}
return true;
}
Graphic.java
This method is used to construct the Graphic:
//Initializes the graphic assuming the coordinate is in the upper left corner
public Graphic(Bitmap image, int start_x, int start_y){
resource = image;
left = start_x;
top = start_y;
right = start_x + image.getWidth();
bottom = start_y + image.getHeight();
}
This method detects if a user is clicking inside the image:
public boolean contains(int x, int y){
if(x >= left && x <= right){
if(y >= top && y <= bottom){
return true;
}
}
return false;
}
This method is used to move the graphic:
public void move(int x, int y){
left = x;
top = y;
right = x + resource.getWidth();
bottom = y + resource.getHeight();
}
I also have 2 methods that determine the center of the region (used for redrawing):
public int getCenterX(){
return (right - left) / 2 + left;
}
public int getCenterY(){
return (bottom - top) / 2 + top;
}
Any help would be greatly appreciated, I feel as though many other StackOverflow users could really benefit from a solution to this issue.
There's a very nice and thorough explanation of touch/multitouch/gestures on Android Developers blog, that includes free and open source code example at google code.
Please, take a look. If you don't need gestures -- just skip that part, read about touch events only.
This issue ended up being much simpler than I had thought, and after some tweaking I realized that this was an issue of image width compensation.
This line in the above code is where the error stems from:
c.drawBitmap(g.getResource(), g.getCenterX(), g.getCenterY(), null);
As you can tell, I manipulated the coordinates from within the Graphic class to produce the center of the bitmap, and then called canvas.drawBitmap() assuming that it would draw from the center outward.
Obviously, this would not work because the canvas always drops from the top left of an image downwards and to the right, so the solution was simple.
The Solution
Create the touch region with regards to the touch location, but draw it relative to a distance equal to the image width subtracted from the center location in the x and y directions. I basically changed the architecture of the Graphic class to implement a getDrawX() and getDrawY() method that would return the modified x and y coordinates of where it should be drawn in order to have the center_x and center_y values (determined in the constructor) actually appear to be at the center of the region.
It all comes down to the fact that in an attempt to compensate for the way the canvas draws bitmaps, I unfortunately incorporated some bad behaviors and in the end had to handle the offset in a completely different way.
I am writing an app that will show a map with a bunch of markers on it. I want the markers to be dynamic (showing dynamic content on them). Because the markers content is dynamic the size of each marker is different.
I found this in the documentation saying that "Different markers can be returned for different states. The different markers can have different bounds." here: https://developers.google.com/maps/documentation/android/reference/com/google/android/maps/OverlayItem#getMarker(int)
I assume that means that multiple markers on the same overlay can be different sizes. Is that correct?
I have an ArrayList on my overlay (extends BalloonItemizedOverlay). The overlay's createItem() method looks like this:
#Override
protected OverlayItem createItem(final int index)
{
final Person person = people.get(index);
final GeoPoint gp = new GeoPoint(person.getLat(), person.getLng());
final OverlayItem oi = new OverlayItem(gp, person.getName(), person.getName());
oi.setMarker(new PersonDrawable(defaultMarker, person));
return oi;
}
Then in my PersonDrawable there is a drawable that has a 9 patch as a background image, and then drawing text on top of it:
public PersonDrawable(final Drawable iBackground, final Person person)
{
background = iBackground;
text = person.getName();
padding = new Rect();
if (background instanceof NinePatchDrawable)
{
// This is getting hit, and just sets the value of the padding
final NinePatchDrawable ninePatch = (NinePatchDrawable) background;
ninePatch.getPadding(padding);
}
// set the bounds of the marker
bounds = background.getBounds();
paint = new Paint();
paint.setColor(Color.rgb(255, 255, 255));
paint.setFakeBoldText(true);
paint.setTextSize(30);
// find the bounds of the text
final Rect textBounds = new Rect();
paint.getTextBounds(text, 0, text.length(), textBounds);
// set the left and right bounds to that of the text plus the padding (then center it)
bounds.left = 0 - (textBounds.width() / 2) - padding.left;
bounds.right = 0 + (textBounds.width() / 2) + padding.right;
// set the bounds of the drawable
setBounds(bounds);
}
#Override
public void draw(final Canvas canvas)
{
// when it's time to draw, draw the background
background.draw(canvas);
// then draw the text within the padding
canvas.drawText(text, bounds.left + padding.left, bounds.bottom - padding.bottom, paint);
}
Each marker has a distinct bounds set, even after it's drawn on the overlay its bounds are distinct. The MapView (or Overlay) is not reusing the objects from createItem(). However the Overlay seems to always pick one marker and assume that is the size for all the markers. Is that just the behavior? Is there something I can do to make it respect the bounds differently for each marker?
Here are some pictures, when it decides to pick a larger marker things turn out ok:
However when it chooses to use the smaller marker as the size:
The problem is that when a Drawable is loaded from resources, it is treated as a shareable resources. From the Drawable documentation:
By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification.
In the OverlayItem, it is assuming that the Drawable is going to be the same size throughout. The effect you are seeing is that the last modification to the Drawable is overriding all previous modifications, so all displays of the Drawable are showing up exactly the same.
The solution is to prevent sharing of the Drawable's modifications. You can do this using the mutate() method in conjunction with multiple loads of the Drawable from a resource. You should do this in conjunction with the PersonDrawable constructor you have outlined above.
I've drawn 5 bitmaps from .png files on a canvas - a head, a body and two arms and legs.
How can I detect which of these has been touched on an OnTouch? And, more specifically, can I detect if the OnTouch was within the actual shape of the body part touched?
What I mean is, obviously, the .pngs themselves are rectangular, but does Android know, or can I tell it, to ignore the transparency within the image?
You could get the colour of pixel touched and compare it to the colour of pixel on the background at those co-ords.
EDIT: ok, ignore that, you can't get the colour of a pixel on the canvas, so instead, get the x,y of the touch, check if any of the body part images have been touched, if so, take the x,y of the image from the touch x,y, then get the pixel of the image, which should be transparent or colour.
public boolean onTouchEvent(MotionEvent event)
{
int x = (int) event.getX();
int y = (int) event.getY();
int offsetx, offsety;
for(int i = 0;i<NUM_OF_BODY_PARTS;i++)
{
if(bodyPartRect[i].intersects(x,y,x+1,y+1))
{
offsetx = x - bodyPartRect[i].left;
offsety = y - bodyPartRect[i].top;
if(bodyPartBMP[i].getPixel(offsetx,offsety) == TRANSPARENT)
{
//whatever
}
}
}
}