I have game field which is a Group with Actors. The Group locketed in Table wich is locketed in ScrollPane. I have two Buttons to zoom in and zoom out the game field. Here is my code how I do it:
TextButton zoomInBtn = new TextButton("+", menuBtnStyle);
zoomInBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width + width * 0.1f;
if (newWidth > myWorld.getMaxWidth()) {
newWidth = myWorld.getMaxWidth();
}
float newHeight = height * newWidth / width;
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
TextButton zoomOutBtn = new TextButton("-", menuBtnStyle);
zoomOutBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width - width * 0.1f;
if (newWidth < myWorld.getMinWidth()) {
newWidth = myWorld.getMinWidth();
}
float newHeight = height * newWidth / width;
Actor widget = scrollPane.getWidget();
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
I change the size of my fieldGroup with Image Actors in it. And then readding it to Table.
The problem is: when I zoom with buttons it always zoom around left corner. I want it to zoom from canter of ScrollPane. I know that I can do it with Ortographic Camera, but it would be difficult, I think, to make it movements so smooth as ScrollPane. So maybe there is some way to do it with ScrollPane.
Every time you zoom in or out you would have to change the x and y position of the group relative to the zoom. You can do this using the setScrollx (and y) of your scroll pane.
You need to set so that the middle of the part you are viewing stays in the middle of the scrollpane. You can work out middle of newWidth by dividing it by 2. If the scrollPane is fullScreen you can half the value obtained by Gdx.graphics.getwidth();. The difference between these two is then the value which will keep the middles aligned.
newXvalue =(newWidth/2) - ((Gdx.graphics.getWidth())/2);
scrollPane.setScrollx(newXvalue);
The same should be done for y.
Note: if you want to zoom and move at same time, this will not work, I would recommend using orthographic camera with a gesture listener for that.
Related
I have a ViewPager that look like this:
I need the previews on the sides to ZoomOut when you swipe and only maintain the size of the center view, something like this:
I already tried some examples with ViewPager.PageTransformer() but those examples are always with a fade animation(That I don't need) and not showing previews(Which I need).
final int paddingPx = 300;
final float MIN_SCALE = 0.8f;
final float MAX_SCALE = 1f;
viewPager.setClipToPadding(false);
viewPager.setPadding(paddingPx, 0, paddingPx, 0);
viewPager.setPageTransformer(false, transformer);
This makes pages smaller than viewpager width:
Here is my Transformer:
PageTransformer transformer = new PageTransformer() {
#Override
public void transformPage(View page, float position) {
float pagerWidthPx = ((ViewPager) page.getParent()).getWidth();
float pageWidthPx = pagerWidthPx - 2 * paddingPx;
float maxVisiblePages = pagerWidthPx / pageWidthPx;
float center = maxVisiblePages / 2f;
float scale;
if (position + 0.5f < center - 0.5f || position > center) {
scale = MIN_SCALE;
} else {
float coef;
if (position + 0.5f < center) {
coef = (position + 1 - center) / 0.5f;
} else {
coef = (center - position) / 0.5f;
}
scale = coef * (MAX_SCALE - MIN_SCALE) + MIN_SCALE;
}
page.setScaleX(scale);
page.setScaleY(scale);
}
};
The difficult thing about this is position parameter of transformPage method. When pages are fill viewpagers width this param changes from 0 to 1. But when pages are smaller this param is tricky.
position is value of left side of the view. Page width equals to 1. position equals to 0 when pages left side is on the left side of viewpager. So position can be more than 1, depends on page width.
First you need to find viewpagers center (relatively to position parameter). Then scale down page if page moves to left or to rigth from center. In my implementation I scale down view on half page distance from center.
Here is result:
Page width can be controlled via padding.
Extra margin between pages can be added throught viewPager.setPageMargin(value) method.
I have used this PhotoView library for custom ImageView. I want to scale the image at particular point. Here is the method I found is setScale(float scale, float focalX, float focalY, boolean animate)
I am wondering what can I pass a value of focalX and focalY , I have X and Y coordinate which I am passing currently and it scales to very different position.
Here is a snippet,
intResultX = intTotalX / intArraySize;
intResultY = intTotalY / intArraySize;
mMap.setScale(5, intResultX, intResultY, true);
To zoom at particular XY coordinate in Imageview you can pass a value of focalX and focalY along with scale (must be between max scale an min scale of PhotoView) and boolean value to set animation.
Code to get max-min scales:
mPhotoView.getMinimumScale();
mPhotoView.getMaximumScale();
focalX and focalY It can be any points on screen, here I have taken two examples one is center of the screen and other is top-left corner. following is the code for both cases.
Code:
Random r = new Random();
float minScale = mPhotoView.getMinimumScale();
float maxScale = mPhotoView.getMaximumScale();
float randomScale = minScale + (r.nextFloat() * (maxScale - minScale));
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
int centerX=width/2;
int centerY =height/2;
/*pass a value of focalX and focalY to scale image to center*/
//mPhotoView.setScale(randomScale, centerX, centerY, true);
/*pass a value of focalX and focalY to scale image to top left corner*/
mPhotoView.setScale(randomScale, 0, 0, true);
Set zoom to the specified scale. Image will be centered around the point
(focusX, focusY). These floats range from 0 to 1 and denote the focus point
as a fraction from the left and top of the view. For example, the top left
corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
/*setZoom can be called before the image is on the screen, but at this point,
image and view sizes have not yet been calculated in onMeasure. Thus, we should
delay calling setZoom until the view has been measured.*/
if (!onDrawReady) {
delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
return;
}
if (scaleType != mScaleType) {
setScaleType(scaleType);
}
resetZoom();
scaleImage(scale, viewWidth / 2, viewHeight / 2, true);
matrix.getValues(m);
m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
matrix.setValues(m);
fixTrans();
setImageMatrix(matrix);
}
Hope this helps. Happy coding.
I'm looking for to some way for make a scroll, or scan of a panoramic image in Android. With this I mean show a part of my image adjusting the height of the image (or width in vertical) and automatically moving slowly the view scrolling all the image. For example:
this image its so big to try to see it in a 16:9 device(with a bit of detail i mean), so i want to do something like:
Just show that part in the screen and move it slowly to the right till the end of the image. Achieving a "scroll" effect across the image.
I have been looking in the website and internet last days and I just found the library of PanoramicGL or some ways to watch 360ยบ images.
This Is For Scroll Click Here
You must use PanoramaClient (which is part of Google Play Services) to open PhotoSphere photos.
An example of how to do this can be found on this Android developer blog post:
// This listener will be called with information about the given panorama.
OnPanoramaInfoLoadedListener infoLoadedListener =
new OnPanoramaInfoLoadedListener() {
#Override
public void onPanoramaInfoLoaded(ConnectionResult result,
Intent viewerIntent) {
if (result.isSuccess()) {
// If the intent is not null, the image can be shown as a
// panorama.
if (viewerIntent != null) {
// Use the given intent to start the panorama viewer.
startActivity(viewerIntent);
}
}
// If viewerIntent is null, the image is not a viewable panorama.
}
};
// Create client instance and connect to it.
PanoramaClient client = ...
...
// Once connected to the client, initiate the asynchronous check on whether
//the image is a viewable panorama.
client.loadPanoramaInfo(infoLoadedListener, panoramaUri);
Thanks to kishu, I made my own method to animate a panoramic image dynamically depending if its in landscape or portrait mode.
You can ignore the orientation value of the method, I only use it to change the animation of the video from X to Y if I want to see the image in landscape.
public void animatePanorama(int orientation) {
int duration;
// The milisecons that we will use dinamically for the animation, been this is 1,31milisecons for pixel
float miliseconsPixel = 1.31f;
float imageWidth;
//Delta X and Y values for the animation
float deltaX = 0f;
float deltaY = 0f;
float aspectRatio;
//We get the drawable from the container to calcule his real Width and Height
final Drawable d = mImageContainer.getDrawable();
final int origWidth = d.getIntrinsicWidth();
final int origHeight = d.getIntrinsicHeight();
//With that we get the real aspect ratio and a duration
if (origWidth > origHeight) {
aspectRatio = (float) origWidth / (float) origHeight;
duration = (int) (miliseconsPixel * origWidth);
imageWidth = mImageContainer.getMeasuredHeight() * aspectRatio;
} else {
aspectRatio = (float) origHeight / (float) origWidth;
duration = (int) (miliseconsPixel * origHeight);
imageWidth = mImageContainer.getMeasuredWidth() * aspectRatio;
}
//Set if the animation will be horizontal(Portrait) or Vertical(landscape)
if (orientation == 0 || orientation == 180)
deltaX = imageWidth / 2f - mImageContainer.getMeasuredWidth() / 2;
else
deltaY = imageWidth / 2f - mImageContainer.getMeasuredHeight() / 2;
//New Animation
Animation animation = new TranslateAnimation(deltaX, -deltaX, deltaY, -deltaY);
//Add Duration
animation.setDuration(duration);
animation.setFillAfter(true);
//Add cycle for repeating mode
animation.setRepeatCount(-1);
animation.setRepeatMode(Animation.REVERSE);
mImageContainer.startAnimation(animation);
}
I use this method to add a Sprite to the screen randomly.
private void addFace() {
Random rand = new Random();
float x = (int) mCamera.getHeight() + mBallTextureRegion.getHeight();
float minY = mBallTextureRegion.getHeight();
float maxY = (int)(mCamera.getWidth() - mBallTextureRegion.getWidth());
float rangeY = maxY - minY;
float y = rand.nextInt((int)rangeY) + minY;
this.mFaceCount++;
Log.e("Faces: ", "Face" + this.mFaceCount);
Sprite face = null;
Body body = null;
The only problem is i would like for the sprites to be added at the top of the screen(which is the camera) but instead they are added on the side of the screen.
Any suggestions on how to do this?
First of all, the (x,y) coordinates of an entity are on it's top left. So, minY should be 0, or you could just do:
float y = rand.nextFloat(maxY);
You don't need all of these (int) casts, you can delete them.
In order to make to position random, get a random X too:
float maxX = this.mCamera.getWidth() - this.mBallTextureRegion.getWidth();
float x = rand.nextFloat(maxX);
This should work.
On the android screen, the origin of the coordinate system is in the upper left corner.
(0,0)......(1,0)
(0,1)......(1,1)
So if you want something to always spawn at the top of the screen, then Y will need to be 0. Always. the X value can be anything from 0 to the width, randomly. This will place the object at the top, and randomly in the X (left-right) direction
So I have an ImageView using a Matrix to scale the Bitmap I'm displaying. I can double-tap to zoom to full-size, and my ScaleAnimation handles animating the zoom-in, it all works fine.
Now I want to double-tap again to zoom out, but when I animate this with ScaleAnimation, the ImageView does not draw the newly exposed areas of the image (as the current viewport shrinks), instead you see the portion of visible image shrinking in. I have tried using ViewGroup.setClipChildren(false), but this only leaves the last-drawn artifacts from the previous frame - leading to an trippy telescoping effect, but not quite what I was after.
I know there are many zoom-related questions, but none cover my situation - specifically animating the zoom-out operation. I do have the mechanics working - ie aside from the zoom-out animation, double-tapping to zoom in and out works fine.
Any suggestions?
In the end I decided to stop using the Animation classes offered by Android, because the ScaleAnimation applies a scale to the ImageView as a whole which then combines with the scale of the ImageView's image Matrix, making it complicated to work with (aside from the clipping issues I was having).
Since all I really need is to animate the changes made to the ImageView's Matrix, I implemented the OnDoubleTapListener (at the end of this post - I leave it as an "exercise to the reader" to add the missing fields and methods - I use a few PointF and Matrix fields to avoid excess garbage creation). Basically the animation itself is implemented by using View.post to keep posting a Runnable that incrementally changes the ImageView's image Matrix:
public boolean onDoubleTap(MotionEvent e) {
final float x = e.getX();
final float y = e.getY();
matrix.reset();
matrix.set(imageView.getImageMatrix());
matrix.getValues(matrixValues);
matrix.invert(inverseMatrix);
doubleTapImagePoint[0] = x;
doubleTapImagePoint[1] = y;
inverseMatrix.mapPoints(doubleTapImagePoint);
final float scale = matrixValues[Matrix.MSCALE_X];
final float targetScale = scale < 1.0f ? 1.0f : calculateFitToScreenScale();
final float finalX;
final float finalY;
// assumption: if targetScale is less than 1, we're zooming out to fit the screen
if (targetScale < 1.0f) {
// scaling the image to fit the screen, we want the resulting image to be centred. We need to take
// into account the shift that is applied to zoom on the tapped point, easiest way is to reuse
// the transformation matrix.
RectF imageBounds = new RectF(imageView.getDrawable().getBounds());
// set up matrix for target
matrix.reset();
matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
matrix.postScale(targetScale, targetScale);
matrix.mapRect(imageBounds);
finalX = ((imageView.getWidth() - imageBounds.width()) / 2.0f) - imageBounds.left;
finalY = ((imageView.getHeight() - imageBounds.height()) / 2.0f) - imageBounds.top;
}
// else zoom around the double-tap point
else {
finalX = x;
finalY = y;
}
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final long startTime = System.currentTimeMillis();
final long duration = 800;
imageView.post(new Runnable() {
#Override
public void run() {
float t = (float) (System.currentTimeMillis() - startTime) / duration;
t = t > 1.0f ? 1.0f : t;
float interpolatedRatio = interpolator.getInterpolation(t);
float tempScale = scale + interpolatedRatio * (targetScale - scale);
float tempX = x + interpolatedRatio * (finalX - x);
float tempY = y + interpolatedRatio * (finalY - y);
matrix.reset();
// translate initialPoint to 0,0 before applying zoom
matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
// zoom
matrix.postScale(tempScale, tempScale);
// translate back to equivalent point
matrix.postTranslate(tempX, tempY);
imageView.setImageMatrix(matrix);
if (t < 1f) {
imageView.post(this);
}
}
});
return false;
}