I've an large image in my android app, which represents a real pictured device.
Now I would like to know, whats the best way to do the following:
Mark some specific image sectors as clickable
Visual preview the click event (like a normal android button)
I've thought about something like the "imagemap" similar solution of
http://catchthecows.com/?p=113.
But it doesn't highlight the clicked section (neccessary for me).
It's not possible, or lets say very complicated, to create "n" different pictures which handle the highlighting effect (for example: section 1 clicked - so highlighting it, section 2 clicked - so higlighting it ....).
Any suggestions would be great.
Thanks a lot.
Btw:
My image looks like the following (the red sections should be clickable).
I had quick look at the ImageMap source you provided.
Here's the onDraw() method for ImageMap:
/**
* Paint the view
* image first, location decorations next, bubbles on top
*/
#Override
protected void onDraw(Canvas canvas) {
drawMap(canvas);
drawLocations(canvas);
drawBubbles(canvas);
}
So, as the comment states, the full image is drawn first, followed by the 'location decorations' (which would seem to be an area of particular interest), and finally the area 'bubbles', which basically seem to be tooltips for the selectable areas and might not be applicable to you.
Taking a look at the drawing method for the 'location decorations':
protected void drawLocations(Canvas canvas) {
for (Area a : mAreaList) {
a.onDraw(canvas);
}
}
And finally the draw method for the Area objects (which are the definable, selectable image areas):
// an onDraw is set up to provide an extensible way to
// decorate an area. When drawing remember to take the
// scaling and translation into account
public void onDraw(Canvas canvas) {
if (_decoration != null) {
float x = (getOriginX() * mResizeFactorX) + mScrollLeft - 17;
float y = (getOriginY() * mResizeFactorY) + mScrollTop - 17;
canvas.drawBitmap(_decoration, x, y, null);
}
}
I would say your best bet would be to use the ImageMap class to take care of most of the required functionality. You can use the supplied Area.onDraw() method to take care of the area 'highlighting'. Instead of drawing a 'decoration bitmap', use the shape information defined in the Area class or subclasses and use Canvas methods to draw a transparent highlight mask over the selected areas.
EDIT: You would need a seperate onDraw() implementation for each of the Area subclasses, as the area bounds information is stored in the subclasses.
Related
I'm using a custom View to draw rectangles(which will have text inside them at a certain point) and I want to highlight each rectangle when selected, which will happen every few seconds when user selects a rectangle. Should I implement highlighted rectangle in onDraw or is there a way just to redraw each rectangle without redrawing the whole View? I was thinking of using "invalidate(rect)" but it's been deprecated.
I'm trying to be considerate of the cost of invalidating the whole View compared to just redrawing a rectangle.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for(CellCoordinates cellCoordinate : mCoordinateCells) {
canvas.drawText(" ", cellCoordinate.getRect().exactCenterX(), cellCoordinate.getRect().exactCenterY(), cellPaint);
}
}
Using invalidate() and onDraw() is fine. Dirty rect is not really have effect on API21+
... In API 21 the given rectangle is ignored entirely in favor of an internally-calculated area instead. ...
Dirty rect is deprecated because of different drawing model in hardware accelerated views. Checkout this link for more information
Also, it seems your rectangles can be implemented as custom Drawables with states (selected and normal). It will not give you extra performance, but might help to divide and structure code for drawing. This might help
Suppose I have six points drawn in a Canvas along the circumference of a circle, in the pattern of a hexagon.
I want to simultaneously move and re-draw each of these six points in a small circular path - for example, each point moves along the circumference of a circle with 1/10th the radius of the larger circle.
I'm a little lost on how to do this with Canvas and onDraw and I don't know if better solutions exist. How would you do this?
Some answers have at least pointed me toward this which shows how a point might move along a circular path, but I don't know how to implement it for this situation:
for (double t = 0; t < 2*Pi; t += 0.01)
{
x = R*cos(t) + x_0;
y = R*sin(t) + y_0;
}
Thank you!
Here's one approach:
Start with a custom view that extends View and overrides onDraw. You are on the right track with using the drawing functions of Canvas.
Give your custom view a field to hold the current angle:
private float mTheta;
Then add a method like:
public void setTheta(float radians) {
mTheta = radians;
invalidate();
}
Then in onDraw, use the current value of mTheta to calculate the position of your points.
Of course, with the custom view you might need to handle sizing with an onMeasure override and possibly some layout with an onLayout override. If you set the dimensions of the view to an absolute dp value, the default behavior should work for you.
Doing it this way will set you up to override onTouch and allow user interaction to move the graphics, or use a ValueAnimator to cycle from 0 to 2π calling setTheta from within your AnimatorUpdateListener.
Put some code together and when you get stuck, post another question. If you add a comment to this answer with a link to the new question, I'll take a look at it.
I am creating an Android application that allows free drawing.
The drawing area is represented by a FrameLayout that contains a set of DrawingView (derived from View).
Each DawingView has a transparent background and their size match the size of the FrameLayout.
The DrawingView draws a Path (built by calling path.moveTo followed by multiple path.lineTo)
Path _penPath;
Paint _paint;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(_penPath, _paint);
}
I need to perform hit test that is true only if the touch pointer is on the actual path and false if it is on the transparent background.
Currently the onTouch function gets invoked for every DrawingView in the Z order sequence.
I know I could calculate the distance between the touch pointer and each point in the path and compare it with a threshold value to implement hit test but I hope there is something in the framework that I can use.
For example in C# WPF transparent areas don't raise touch events if not configured explicitly.
Thanks.
Right now, I have it so that my layout consists of a Fragment that takes up the entire screen and displays an image. I want to make it so that an additional View exists on top of it, also taking up the entire screen. On that top layer, I want to be able to color it all black initially, and then create certain spots that are transparent (alpha?) and reveal the image displayed on the fragment behind it. So basically the screen will be all black except for a few spots where the image behind is showing through, which I would determine programmatically. I've looked into a bunch of the graphics and views that Android provides, but have no clue where to start. Is this suited for a SurfaceView if I just want it to be all black with some spots of alpha?
Once I select the correct view to use, I'm assuming that I just override the onDraw() method and then do something like canvas.setBody(black) and then add shapes of alpha to it? Will the shapes correctly affect the background color?
For your masking View, you can make a custom view that can keep track of which areas to unmask (as Rects or something similar) and then draw them in onDraw(Canvas) like this:
public class MaskView extends View {
private Set<Rect> mRects = new HashSet<Rect>();
private Paint mUnmaskPaint = new Paint();
{
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
/**
* Add an unmasking rectangle to this view's background.
*
* #param rect
* a rectangle used to unmask the background
*/
public void addUnmaskRect(Rect rect) {
mRects.add(rect);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (Rect r : mRects) {
canvas.drawRect(r, mUnmaskPaint);
}
}
}
Your Fragment (or whatever is keeping track of the unmask areas) passes Rects to MaskView via addUnmaskRect(Rect). Whenever the view is redrawn (remember to call invalidate() each time you are done passing Rects) it is first filled with black, and then has any rectangles unmask the black background. The coordinates for your rectangles must be set to the coordinate space of the view, but if the underlying image occupies the exact same area it should be relatively simple (you can also look at the View.getLocationInWindow(int[]) method to help you with this as well).
I would like to display an image (including scroll and zoom functionality).
Additionally, I need to add some overlays to the image dynamically. The overlays are in a relationship to the content within the image.
If the user scrolls, the overlays should move too
If the user zooms,
the overlays should not be zoomed but hold their relative position to
the image
In other words: I'd like to implement a Map with some markers, but I provide my own map material.
My main question is: How could I do this?
What I have tried so far
I tried to implement it within a WebView as it provided the basic zooming and scaling functionality. However, I did not succeed in adding overlays without using Javascript and HTML (what I thought was not a very convenient way of solving the problem)
There are projects (i.e. TouchImageView) which are implementing the scroll/zoom functionality but they make it even harder to add own overlay functionality from my point of view.
Questions
Is there a simple solution or approach to my problem?
What is the right way of adding and moving views at runtime in Android?
(Usually, I would use a basic layout and play with the margin - would that be best practice?)
Could somebody point me to the right direction, please.
Min. API Level: 10
I would suggest extending the View class and overriding onDraw method.
Take a look at the Canvas API.
You'll probably have an onDraw method that looks like :
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY); //position of your actual data
canvas.scale(mScaleFactor, mScaleFactor); // zoom factor
map.draw(canvas); //you draw your map here
canvas.restore(); //go back to saved state
//for each marker you have, do the same "save - translate - scale - draw" loop
}
Since you don't need your markers to be zoomable, you'll probably wont need scale step but you need to calculate their position properly.
Depending on your # of objects etc, you may need to have a more optimized way of drawing your markers (e.g. re-drawing only changed rectangular areas). see View.invalidate(Rect)
I Think this one can help you
Refer this project Link
it Has a Automatic Scrolling of Image and Zoom a Image When you click On it.
Have you tried subclassing View and handling yourself user gestures? It is my first choice for complex behaviours as such. It should look something like this:
In your class extending View you should instantiate the required image and overlays at creation time as Bitmap objects
OnScroll event you will calculate how the image and overlays chould be after such scroll, refresh the Bitmaps and invalidate the current view
Using a GestureDetector, you can handle pinch events and treat zoom in/zoom out events just as scroll events
Remember to always call invalidate after changing your Bitmaps
Draw the bitmaps during the onDraw method
Plenty of material about this individual tasks on StackOverflow, let me know if you need adittional help in any of these.
Checkout this example project https://github.com/jzafrilla/AndroidImageHostpot
After a long time, I found the exact answer to my solution: TileView
Setting up the map view:
#Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
TileView tileView = new TileView( this );
tileView.setSize( 2000, 3000 ); // the original size of the untiled image
tileView.addDetailLevel( 1f, "tile-%d-%d.png");
setContentView( tileView );
}
Adding the marker:
tileView.addMarker( someView, 250, 500, -0.5f, -1.0f );
Code from the documentation.