Zooming and Panning of view in android - android

Hi friends
I am creating one app where i put two views. first is imageview and the other one is my customeview.
i am drawing image on my custom view. my custom view code is as follow
public class DrawView extends ImageView{
#SuppressWarnings("unused")
private static final String TAG = "DrawView";
Painter painter = new Painter();
PathStack pathStack;
private DrawApplication drawApplication;
public DrawView(Context context, AttributeSet attr) {
super(context,attr);
setFocusable(true);
setFocusableInTouchMode(true);
drawApplication=(DrawApplication)context.getApplicationContext();
pathStack=drawApplication.getPainter().getPathStack();
painter=drawApplication.getPainter();
setScaleType(ScaleType.MATRIX);
}
#Override
public void onDraw(Canvas canvas) {
float scaleFactor=drawApplication.getScaleFactor();
canvas.scale(scaleFactor, scaleFactor,(float)getWidth()/2,(float)getHeight()/2);
List<DrawingPath> currentStack=pathStack.getCurrentStack();
for (DrawingPath drawPath : currentStack) {
drawPath.draw(canvas, painter);
}
}
}
many things clear from code like storing path on drawapplication etc.
now we have decided to provide zooming and panning to this view. and my drawing should also be zoomed accordingly along with imageview which in back.
also i should be able to draw correctly.
I tried canvas scaling but that is worst, because after scaling i am not able to draw to the touched point and also less drawing to the screen resulting in more drawing to the view.
drawing on widget is also very lengthy task results in performance slow down.
plz provide any way.
and also i am using api level 10.
Edit1
Well friends. i got one idea.
i have to draw path on different zoom level. so if we can calculate path ratio in different zoom level than it will solve my problem. plz provide help accordingly
Edit2
if we will be able to scale path then it can solve my problem
Edit3
i think if we draw it on bitmap than i can achieve my goal, but my problem is resizing bitmap and providing scrolling to bitmap on different zoom level

Similar questions on SO:
android pinch zoom
Pinch zoom for custom view
Can be done on Honeycomb (API lvl 11) but not properly before.

I don't think a normal view can be zoomed. You could capture the touch events and scale the view widgets correspondingly.

Related

How to draw over small portions of custom view?

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

Osmdroid custom marker drawables can't draw text or shapes

I'm currently using the latest osmdroid library (3.0.10) to show my custom markers on a map with an ItemizedOverlay. I've extended the Drawable class to implement my own drawing. However my drawables are not drawn correctly. I draw a bitmap image, a circle and a text on top of each other. The bitmap is drawn, but the circle and the text is not visible. When I use the same drawable in an imageview, everything is OK.
Here is the code of my drawable's onDraw method:
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.translate(getBounds().left, getBounds().top);
//this draws fine
canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()), new Rect(0,0,mWidth,mWidth), mPaint);
//this is not
canvas.drawCircle(mHeight/2, mHeight/2, mHeight/2, mPaint);
//neither
canvas.drawText("X", mHeight/2, mHeight/2, mPaint2);
canvas.restore();
}
I've tried drawing the circle and the text on a bitmap, and drawing that on the provided canvas. It's working but that kills the whole point.
Any help is appreciated.
After a day of going trough the osmdroid sources, i managed to resolve my problems, but haven't found why are they there in the first place. Osmdroid uses an ISafeCanvas interface (and SafeTranslatedCanvas implementation) to wrap a canvas to deal with some high zoom level translate glitches. While I haven't found any straightforward errors in the code, I bypassed the mechanics when drawing my overlays by overriding the ItemizedOverlay's draw method and making my own ISafeCanvas implementation, which only returns the encapsulated canvas in it's getSafeCanvas method, and now everything looks fine.
#Override
protected void draw(Canvas canvas, MapView mv, boolean shadow) {
drawSafe(new MyCanvas(canvas),mv,shadow);
}
This is far from a best solution, the SafeCanvas is there for a reason, so I suggest a thorough testing if using this method.
Note: I used only Android 4.1.1, and don't know if this issue is present on other devices.
Please take a look at Issue 427. Essentially you want to use:
canvas.getUnsafeCanvas(new UnsafeCanvasHandler() {
// Draw circle and text here
// (but not drawBitmap)
});
Keep an eye on the ticket for a more permanent solution.

Android - scaling a viewgroup (layout) at runtime

I've got a bunch of tiles that make up a single image, that are rendered on request (by what's portion of the total image is visible on screen). The composite image is significantly larger than the screen. Each tile is positioned tightly next to its neighbors to create the illusion of a single image (the single image, if not tiled, would be too large and have OOM errors).
These are ImageViews inside a FrameLayout, positioned with top/leftMargin.
Targetting API 2.2 / SDK 8
I'm trying to scale the total image in response to user events (button presses, pinch, etc).
Apparently setScale isn't available until API 11. I tried using canvas.scale in onDraw of the FrameLayout with setWillNotDraw(false), and onDraw is being called, but no scaling is being applied (I assume because the layout itself doesn't have any graphic information? I had assumed it'd render it's children as well, but I guess not unless I'm doing something wrong here also).
I guess I could iterate through each tile and scale it independently, but would need to reposition each one as well, each time.
I have to think there's a way to scale a parent View (ViewGroup?) so that all of its children are also scaled (like every other language responsible for managing a GUI). What am I missing?
Thanks
For reference, the approach mentioned above (using onDraw of a parent ViewGroup) does in fact work. I had been using canvas.save() before the transformation and canvas.restore() after (based on some examples), which was incorrect.
The following is a generic subclass of FrameLayout that will scale it's children
package com.whatever.layouts;
import android.content.Context;
import android.graphics.Canvas;
import android.widget.FrameLayout;
public class ScalingFrameLayout extends FrameLayout {
private float scale = 1;
public ScalingFrameLayout(Context context) {
super(context);
setWillNotDraw(false);
}
public void setScale(float factor){
scale = factor;
invalidate();
}
public float getScale(){
return scale;
}
#Override
public void onDraw(Canvas canvas){
canvas.scale(scale, scale);
super.onDraw(canvas);
}
}

Android Gauge Animation Question

Okay so i've been trying to do this for a couple of days and i am getting no where. So i have the following two images:
The First is a RPM Gauge
The Second image is a full white graphic representing rpm gauge being full:
I want to do the following:
ask the user for an RPM input, if for example they enter 1.2 the gauge will fill up as follows:
I have the user input working i need help with the animation. Here is what i have tried:
I have tried using PorterDuff but it also clips the gauge in the background not just the white bar
I've tried splitting the image into little bitmaps and store them into arrays so that i can recall parts but this was slow and often crashed
I made some progress by applying the Gauge first to the canvas then saving the canvas: canvas.save(); then clipping a path on the white image then restoring the canvas. However i do not know how to clip in a circular fashion starting from bottom left to a 180 degress to the bottom right (CW). Is this the best way?
I know there is probably an easier or more efficient way of doing this i just don't have a clue. Anyone with any good ideas?
*Note all images are PNG's
Thanks in advance!
As you already found, i would use clip:
draw background image
set clip
draw foreground image
I would use
Canvas.clipPath()
with path looking like pie slice starting in the center of circle, like this:
To create clip path use something like:
public class PieView extends View {
private int width = 200;
private int angleStart = 135;
private int sweep = 270;
private Path p;
private Paint paint = new Paint();
public PieView(Context context, AttributeSet attrs) {
super(context, attrs);
p = new Path();
//move into center of the circle
p.setLastPoint(width/2, width/2);
//add line from the center to arc at specified angle
p.lineTo(width/2+(float)Math.cos(Math.toRadians(angleStart))*(width/2),
width/2+(float)Math.sin(Math.toRadians(angleStart))*(width/2));
//add arc from start angle with specified sweep
p.addArc(new RectF(0, 0, width, width), angleStart, sweep);
//from end of arc return to the center of circle
p.lineTo(width/2, width/2);
paint.setColor(Color.RED);
paint.setStrokeWidth(1);
paint.setStyle(Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,width,width, paint);
canvas.drawPath(p,paint);
}
}
This is how to draw arcs, from Android ApiDemos: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/Arcs.html
Then you need to use xfermode to remove a part of the top image by using a canvas derived from a bitmap. You can see one example of this approach here: Make certain area of bitmap transparent on touch

Dynamically create / draw images to put in android view

I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.

Categories

Resources