I am trying to follow the tutorial from https://www.youtube.com/watch?v=X2ls3FTPOAc in order to zoom into my drawable which lies inside an imageview.
I see here that the scaleListener object is called since the Log outputs that it's being called. However, when I use the ImageView.ScaleType.MATRIX scale type for the image-view it doesn't seem to work. I've also added a slight modification in the onScale method. Here's my code:
view1.setScaleType(ImageView.ScaleType.MATRIX); //using CENTER works
Matrix matrix = new Matrix();
view1.setImageMatrix(matrix);
DummyDraw drawD = new DummyDraw(this);
view1.setImageDrawable(drawD);
scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
a few methods down I have:
public boolean onTouchEvent(MotionEvent event){
scaleGestureDetector.onTouchEvent(event);
return true; }
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
public boolean onScale(ScaleGestureDetector detector) {
Log.d("inside1044","inside scale listener onSscale method ");
float scaleFactor = detector.getScaleFactor();
scaleFactor = Math.max(0.1f, Math.min(scaleFactor,5.0f));
scaleFactor2 = scaleFactor;//a modification I made
matrix.setScale(scaleFactor,scaleFactor);
view1.invalidateDrawable(view1.getDrawable());//this is the modification I made
return true;}
}
Now, the DummyDraw class extends Drawable which just draws a pink background and a circle in the middle of the canvas. the variable scaleFactor2 is static and defined in the MainActivity, so everytime the user touches the view the method onScale is called updating the new value for scaleFactor2. I use this scaleFactor2 value somehow to try to scale the canvas of my drawable object. Here's what the code looks like inside the drawable object, inside the onDraw():
//define some variables to get the center of the canvas
canvas.save();
canvas.scale(MainActivity.scaleFactor2,MainActivity.scaleFactor2);
//draw the circle to the screen
canvas.restore();
When I place both fingers on the screen and bring them closer together the circle jumps and moves slightly, however when I move the fingers apart nothing happens. Remember, that this works only when the ImageView.ScaleType is set to CENTER not to MATRIX.
I am trying to accomplish a smooth zoom in and out of the view.
Any ideas would be appreciated.
Related
I want to divide the image into sub images and when I click on a part of the image will give me the name of the region, for example, this is my question how to recognize a region from the image, or how to divide the image into sub-images and use it in imageViews
And thank you in advance
In my opinion #fractalwrench's idea is quite good for your case. Basic steps are listed below.
Subclass Android ImageView. For example, MultiRegionImageView.
Override its onTouchEvent method. (This method gets called whenever user touches the view)
User touches the image and thereby onTouchEvent is called and provides the exact touch point (x, y).
Declare another method or interface which determines at which region a given point is. For example, getRegionByPoint(int x, int y)
If you would like to highlight that region boundaries, you could use paths. First off, you should define paths and save them into a raw file (XML, for example), then using region ID, fetch its path and finally draw that path over the main image.
For drawing a path over the main image, you should also override onDraw method of ImageView class and use canvas.drawPath();
public class MultiRegionImageView extends ImageView {
RegionProvider mRegionProvider;
int mId = -1;
private Paint mPaint;
public MultiRegionImageView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mId = mRegionProvider.getRegionIdByPoint(event.getX(), event.getY());
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mId != -1) {
canvas.drawPath(mRegionProvider.getRegionBoundaryPath(mId), mPaint);
}
}
public interface RegionProvider{
int getRegionIdByPoint(float x, float y);
Path getRegionBoundaryPath(int id);
}
}
You should only need one ImageView to display the map. If you override onTouchEvent(MotionEvent e), you can get the position which is being touched in the View. If you store the position and shape of each region in some sort of List, you can check whether a touch event is within a region (and display whatever text you need to).
I have a class which extends SurfaceView and implements SurfaceHolder.Callback. Using onDraw() method i'm drawing some bitmaps on my canvas. The, after pressing a button I'm adding new image to the canvas calling invalidate(). Is any possibility to save all the changes which I made on every invalidate() which were earlier, so as to after new invalidate() add new image, but not delete the earlier?
From what I have understood you will want to save the image that was drawn and also it's position? By using the code I provided you will have a list that is filled with the image and position. The list is unordered; if you would like an ordered list you can use a LinkedList instead.
Create a new class, you may name it anything.
public class ImageHolder {
private int mX;
private int mY;
private int mDrawableResource;
private String mBitmapFilePath;
public ImageHolder(int x, int y, int drawableResource, String bitmapFilePath) {
mX = x;
mY = y;
mDrawableResource = drawableResource;
mBitmapFilePath = bitmapFilePath;
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public int getDrawableResource() {
return mDrawableResource;
}
public String getBitmapFilePath() {
return mBitmapFilePath;
}
}
Then in your SurfaceView you add it a holder each time you draw to a list. Notice that this is bare minimum code so no synchronization has been added.
private void customDrawMethod() {
mImageHolders.add(new ImageHolder(x, y, drawableResource, bitmapFilePath));
Canvas canvas = getHolder().lockCanvas();
canvas.drawBitmap(bitmap, matrix, paint);
getHolder().unlockCanvasAndPost(canvas);
}
I added both a Bitmap and a Drawable because I am unsure what you use. I could not post more code because I have no idea what you want to do with the saved images. More info could help you further.
This is pretty straightforward case of using Bitmaps to store your previous canvas. Simply attach a bitmap to your canvas. Before drawing, save the old bitmap to something like "prevBitmap" and then draw over it. You can redraw the previous Bitmap by calling canvas.drawBitmap(prevBitmap);
You can save your old drawings on canvas and dont draw them again when adding new picture. Just call invalidate(x, y, x+sizeX, y+sizeY) when you need to add new image with left-top point's coordinates (x, y) and size (sizeX, sizeY) to the View's canvas. About to save all images in array you already got answer.
P.S. sorry for my english, hope it was helpfull
I've been working on an app with complex layouts. I recently realized I need to make parts or all of my layouts zoomable.
One of my main xml files has a linear layout with multiple layouts nested within it to positions views properly. Is there an easy way to make this linear layout and everything within in zoomable? Or would it be easier to make the entire layout file zoomable? What are my options?
First of all extend that class with that specific view
public class MyImageView extends ImageView{
Override following method .
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor, midPoint.x, midPoint.y);
if(appInitialized) {
hsSide.draw(canvas);
scaleA.draw(canvas);
scaleB.draw(canvas);
}
canvas.restore();
}
Create a Gesture Detector that will detect size of zoomed object and you can limit to avoid overlaps.
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
pivotX = detector.getFocusX();
pivotY = detector.getFocusY();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.8f, Math.min(mScaleFactor, 2.0f));
invalidate();
return true;
}
}
At the end initialize the object
ScaleGestureDetector mScaleDetector;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
You might have a look at static transformations. Any ViewGroup or subclass can be customized to apply a transformation to its child views. You enable this by calling setStaticTransformationsEnabled(true), and then overriding the getChildStaticTransformation() callback (docs link) in your custom ViewGroup. You can apply any transformation you like, including a scale to create your zoom effect. This callback will be called any time the view needs to be redrawn or is invalidated.
Also, beware when using this alongside hardware acceleration. Depending on the frequency with which you need to update the transformations you may find hardware doesn't quite work to redraw as you expect. If so, you will need to enable software layers for this view hierarchy.
I have drawn a bitmap image over a canvas.
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.sq);
canvas.drawColor(color.black);
Rect dstRectForRender = new Rect(0,0,320,450);
canvas.drawBitmap(image, null,dstRectForRender,null);
The image gets displayed based on my screnn on a cnavs.
On my touch input, I need to pass the x and y co-ordinate position of the image and fill that pixel with a color to show that the image is painted on a drag event.
How can I pass the x and y coo-ordinate parameters? Which functions should I use to plot the pixels on the image?
I appreciate your help and sweet time.
I'm not sure if this is the best way to do this, but it would be much easier to do this if you defined your own subclass of ImageView and name it something like DrawableImageView. You'd have to make sure you implement all the basic constructors from ImageView, then override the onTouchEvent method. From that event you can get the touch coordinates and store them in an ArrayList<Point> and use that ArrayList by overriding the onDraw method and "painting" the image.
public class DrawableImageView extends ImageView {
ArrayList<Point> list = new ArrayList<Point>();
//constructors..
#Override
public boolean onTouchEvent (MotionEvent event) {
float x = event.getX();
float y = event.getY();
list.add(new Point(x,y));
invalidate();
}
This is just a very brief overview of how to start your class, and may not be the most accurate way of doing things (depending on your specific code). Now, instead of using <ImageView> tags in your xml (or, loading an ImageView programatically), you refer to your subclass like so:
<your.package.name.DrawableImageView
/>
Edit
In response to your comment, there is no predetermined way to draw over an image. You must implement this yourself, which is why I recommended storing Points in an ArrayList. I'm not really sure what you're trying to achieve here, but to draw (for example) black dots over an image you have to override onDraw:
public void onDraw(Canvas c) {
super.onDraw(c);
for(Point p : list) {
//Draw black point at x and y.. I'm posting from my cell so I can't go into much detail
}
}
Also, to force a View to redraw itself you need to use the invalidate() method in your onTouchEvent() (which I've added above).
I am trying to write a small android app that redraws an image everytime the screen is touched.
I expected the image to be redrawn to the new x,y coordinates provided by event.getX() and event.getY() when I override the onTouch(Event) method in an activity.
Can anyone help?
I presume, you'd be having the Bitmap object for the image.
You could call the onDraw() method from the onTouch() method, to draw the bitmap. Here's a code sample which shows the overridden onDraw() method:
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(imgBitmap, x, y, null);
}
x,y are the new co-ordinates of the touch. imgBitmap is the Bitmap object for the image. You can update the instance variables x and y in the onTouch() method.
Hope this helps!!