I'm using the following code in my resize method to maintain aspect ratio across multiple screen sizes:
#Override
public void resize(int width, int height)
{
float aspectRatio = (float)width / (float)height;
float scale = 1f;
Vector2 crop = new Vector2(0, 0);
if (aspectRatio > globals.ASPECT_RATIO)
{
scale = (float)height / (float)globals.VIRTUAL_HEIGHT;
crop.x = (width - globals.VIRTUAL_WIDTH * scale) / 2f;
}
else if (aspectRatio < globals.ASPECT_RATIO)
{
scale = (float)width / (float)globals.VIRTUAL_WIDTH;
crop.y = (height - globals.VIRTUAL_HEIGHT * scale) / 2f;
}
else
{
scale = (float)width / (float)globals.VIRTUAL_WIDTH;
}
float w = (float)globals.VIRTUAL_WIDTH * scale;
float h = (float)globals.VIRTUAL_HEIGHT * scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
}
VIRTUAL_WIDTH, VIRTUAL_HEIGHT and ASPECT_RATIO are set as follows:
public final int VIRTUAL_WIDTH = 800;
public final int VIRTUAL_HEIGHT = 480;
public final float ASPECT_RATIO = (float)VIRTUAL_WIDTH / (float)VIRTUAL_HEIGHT;
This works perfectly with regards to maintaining the correct ratio when the screen size changes. However, camera.uproject (which I call before all touch events) doesn't work properly - touch positions are not correct when the resize code changes the screen size to anything other than 800x480.
Here's how I setup my camera in my create() method:
camera = new OrthographicCamera(globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
camera.setToOrtho(true, globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
And this is the start of my render() method:
camera.update();
Gdx.graphics.getGL20().glViewport((int)viewport.x, (int)viewport.y,
(int)viewport.width, (int)viewport.height);
Gdx.graphics.getGL20().glClearColor(0, 0, 0, 1);
Gdx.graphics.getGL20().glClear(GL20.GL_COLOR_BUFFER_BIT);
If I ignore the resize code and set the glViewport method call to Gdx.graphics.getWidth() and Gdx.graphics.getHeight(), camera.unproject works, but I obviously lose the maintaining of the aspect ratio. Anyone have any ideas?
Here's how I perform the unproject on my touch events:
private Vector3 touchPos = new Vector3(0, 0, 0);
public boolean touchDown (int x, int y, int pointer, int newParam)
{
touchPos.x = x;
touchPos.y = y;
touchPos.z = 0;
camera.unproject(touchPos);
//touch event handling goes here...
}
UPDATE
I've made a little more progress with this by implementing a Stage object, but it's still not working perfectly.
Here's how I now setup the stage and camera in my create method:
stage = new Stage();
camera = new OrthographicCamera(globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
camera.setToOrtho(true, globals.VIRTUAL_WIDTH, globals.VIRTUAL_HEIGHT);
stage.setCamera(camera);
Here's my resize code:
public void resize(int width, int height)
{
Vector2 size = Scaling.fit.apply(800, 480, width, height);
int viewportX = (int)(width - size.x) / 2;
int viewportY = (int)(height - size.y) / 2;
int viewportWidth = (int)size.x;
int viewportHeight = (int)size.y;
Gdx.gl.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
stage.setViewport(800, 480, true, viewportX, viewportY, viewportWidth, viewportHeight);
}
Here's the start of my render code:
stage.getCamera().update();
Gdx.graphics.getGL20().glClearColor(0, 0, 0, 1);
Gdx.graphics.getGL20().glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.setProjectionMatrix(stage.getCamera().combined);
And here's how I handle touch events:
private Vector3 touchPos = new Vector3(0, 0, 0);
public boolean touchDown (int x, int y, int pointer, int newParam)
{
touchPos.x = x;
touchPos.y = y;
touchPos.z = 0;
stage.getCamera().unproject(touchPos);
//touch event handling goes here...
}
This now works when the screen is at default size and when the screen is enlarged. However, when the screen size is reduced the touch points become more and more inaccurate the smaller the screen gets.
Managed to find the answer to this. I simply needed to use the camera.unproject method call which takes Vector3, viewportX, viewportY, viewportWidth and viewportHeight parameters. The required viewport params are those calculated by the resize code shown above.
To solve your issue there is already a implemented solution inside of the new Stage system. Please take a look at the wiki scene2d #viewport. To have a fixed aspec ratio you do not need to resize and fit in manually. Here is the example with blackbars from the wiki:
public void resize (int width, int height) {
Vector2 size = Scaling.fit.apply(800, 480, width, height);
int viewportX = (int)(width - size.x) / 2;
int viewportY = (int)(height - size.y) / 2;
int viewportWidth = (int)size.x;
int viewportHeight = (int)size.y;
Gdx.gl.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
stage.setViewport(800, 480, true, viewportX, viewportY, viewportWidth, viewportHeight);
}
Related
I have an rotated textview and I want to drag and drop this view.
The problem is that the drag shadow has no rotation.
I found a solution for android in java but this does not work for me.
Maybe I translate the code wrong
How to drag a rotated DragShadow?
class CustomDragShdowBuilder : View.DragShadowBuilder
{
private View _view;
public CustomDragShdowBuilder(View view)
{
_view = view;
}
public override void OnDrawShadow(Canvas canvas)
{
double rotationRad = Math.ToRadians(_view.Rotation);
int w = (int)(_view.Width * _view.ScaleX);
int h = (int)(_view.Height * _view.ScaleY);
double s = Math.Abs(Math.Sin(rotationRad));
double c = Math.Abs(Math.Cos(rotationRad));
int width = (int)(w * c + h * s);
int height = (int)(w * s + h * c);
canvas.Scale(_view.ScaleX, _view.ScaleY, width / 2, height / 2);
canvas.Rotate(_view.Rotation, width / 2, height / 2);
canvas.Translate((width - _view.Width) / 2, (height - _view.Height) / 2);
base.OnDrawShadow(canvas);
}
public override void OnProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
shadowTouchPoint.Set(shadowSize.X / 2, shadowSize.Y / 2);
base.OnProvideShadowMetrics(shadowSize, shadowTouchPoint);
}
}
I found a solution for android in java but this does not work for me. Maybe I translate the code wrong
Yes, you are translating it wrong, you did change the codes of OnDrawShadow but you didn't pay attention to OnProvideShadowMetrics, which aims at changing the size of the canvas drawing area, so you need to pass the same width and height that has been calculated by codes:
Here is the modified version of DragShdowBuilder:
public class MyDragShadowBuilder : DragShadowBuilder
{
private int width, height;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) : base(v)
{
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the system.
public override void OnProvideShadowMetrics(Android.Graphics.Point outShadowSize, Android.Graphics.Point outShadowTouchPoint)
{
double rotationRad = Java.Lang.Math.ToRadians(View.Rotation);
int w = (int)(View.Width * View.ScaleX);
int h = (int)(View.Height * View.ScaleY);
double s = Java.Lang.Math.Abs(Java.Lang.Math.Sin(rotationRad));
double c = Java.Lang.Math.Abs(Java.Lang.Math.Cos(rotationRad));
//calculate the size of the canvas
//width = view's width*cos(rad)+height*sin(rad)
width = (int)(w * c + h * s);
//height = view's width*sin(rad)+height*cos(rad)
height = (int)(w * s + h * c);
outShadowSize.Set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
outShadowTouchPoint.Set(outShadowSize.X / 2, outShadowSize.Y / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
public override void OnDrawShadow(Canvas canvas)
{
canvas.Scale(View.ScaleX, View.ScaleY, width/2 , height/2);
//canvas.DrawColor(Android.Graphics.Color.White);
canvas.Rotate(View.Rotation,width/2, height / 2);
canvas.Translate((width - View.Width)/2, (height - View.Height) / 2);
base.OnDrawShadow(canvas);
}
}
And here is the complete sample:RotatedTextViewSample
In my android app.I want to make image Straightening edit feature using android-gpuimage library but GPUImageView doesn't give feature of getBitmap() or setMatrix() then how is it possible please let me know? here is the code to review :
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(isStraightenEffectEnabled){
float angle = (progress - 45);
float width = mGPUImageView.getWidth();
float height = mGPUImageView.getHeight();
if (width > height) {
width = mGPUImageView.getHeight();
height = mGPUImageView.getWidth();
}
float a = (float) Math.atan(height/width);
// the length from the center to the corner of the green
float len1 = (width / 2) / (float) Math.cos(a - Math.abs(Math.toRadians(angle)));
// the length from the center to the corner of the black
float len2 = (float) Math.sqrt(Math.pow(width/2,2) + Math.pow(height/2,2));
// compute the scaling factor
float scale = len2 / len1;
Matrix matrix = mGPUImageView.getMatrix();
if (mMatrix == null) {
mMatrix = new Matrix(matrix);
}
matrix = new Matrix(mMatrix);
float newX = (mGPUImageView.getWidth() / 2) * (1 - scale);
float newY = (mGPUImageView.getHeight() / 2) * (1 - scale);
matrix.postScale(scale, scale);
matrix.postTranslate(newX, newY);
matrix.postRotate(angle, mGPUImageView.getWidth() / 2, mGPUImageView.getHeight() / 2);
Is it possible to get mGPUImageView.setMatrix(matrix);
NOW HERE getMatrix() is a method of GPUImageView but setMatrix() or getBitmap() is not method available with GPUImageView class. Any workarounds if possible ?
add getGPUImage in GPUImageView class
public GPUImage getGPUImage() {
return mGPUImage;
}
then get you can get bitmap like this:
mGPUImageView.getGPUImage().getBitmapWithFilterApplied();
You can also get bitmap like this:
Bitmap bitmap = mGPUImageView.capture();
You can get filtered bitmap renderer and Pixelbuffer. This might be helpful for you.
GPUImageLookupFilter amatorka = new GPUImageLookupFilter();
amatorka.setBitmap(BitmapFactory.decodeResource(getResources(), getResources().getIdentifier("fil_" + position, "drawable", getPackageName())));
GPUImageRenderer renderer = new GPUImageRenderer(amatorka);
renderer.setImageBitmap(bitmap, false);
PixelBuffer buffer = new PixelBuffer(80, 80);
buffer.setRenderer(renderer);
buffer.getBitmap();
I wants to swipe the ViewPager. I used following code for page swipe. But unfortunately is not working.
Solo solo = new Solo(getInstrumentation(), getActivity());
int screenHeight = activityUtils.getCurrentActivity().getWindowManager().getDefaultDisplay()
.getHeight();
int screenWidth = activityUtils.getCurrentActivity(false).getWindowManager().getDefaultDisplay()
.getWidth();
float x = screenWidth / 3.0f * 2.0f;
float y = screenHeight / 3.0f * 2.0f;
Try: 1
solo.swipe(x, 0, y, y, 40);
Try: 2
if (side == Side.LEFT)
drag(0, x, y, y, 40);
else if (side == Side.RIGHT)
drag(x, 0, y, y, 40);
Try: 3
Following method working but it's moving only single page and also it's moving very slowly.
solo1.scrollToSide(Solo.RIGHT);
Is there any option for fast swipe? Which one is best? Kindly share your ideas.
My code with Robotium 5.1
for (int count = 0; count < noOfPages; count++)
solo.swipeToRight(count);
Methods for view pager swipes
private void swipeToLeft(int stepCount) {
Display display = solo.getCurrentActivity().getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
float xStart = width - 10 ;
float xEnd = 10;
solo.drag(xStart, xEnd, height / 2, height / 2, stepCount);
}
private void swipeToRight(int stepCount) {
Display display = solo.getCurrentActivity().getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
float xStart = 10 ;
float xEnd = width - 10;
solo.drag(xStart, xEnd, height / 2, height / 2, stepCount);
}
solo.scrollToSide(Solo.RIGHT);
This implementation is also fast enough...
This issue has been fixed in Robotium 5.1. You can download it from here:
https://code.google.com/p/robotium/wiki/Downloads?tm=2
Well the code in the robotium repo for scroll to side is:
public void scrollToSide(int side) {
switch (side){
case RIGHT: scroller.scrollToSide(Scroller.Side.RIGHT, 0.70F); break;
case LEFT: scroller.scrollToSide(Scroller.Side.LEFT, 0.70F); break;
}
}
#SuppressWarnings("deprecation")
public void scrollToSide(Side side, float scrollPosition) {
int screenHeight = activityUtils.getCurrentActivity().getWindowManager().getDefaultDisplay()
.getHeight();
int screenWidth = activityUtils.getCurrentActivity(false).getWindowManager().getDefaultDisplay()
.getWidth();
float x = screenWidth * scrollPosition;
float y = screenHeight / 2.0f;
if (side == Side.LEFT)
drag(0, x, y, y, 40);
else if (side == Side.RIGHT)
drag(x, 0, y, y, 40);
}
public void drag(float fromX, float toX, float fromY, float toY,
int stepCount) {
dialogUtils.hideSoftKeyboard(null, false, true);
scroller.drag(fromX, toX, fromY, toY, stepCount);
}
So it looks like you must be close, my guess is the numbers you have used in your drag call are not able to make the view pager work. I would suggest trying th enumbers used by robotium EXCEPT for the last paramater which if you change should make the swipe happen quicker as this number is the ampount of events required to complete the swipe. You may need to experiment with the number.
I'm trying the moving limit after zoom with canvas scale, but new coordinates does not match.
my width 480, after 1.5f zooming my width will be 720... but when I set translate to -480, I'm seeing more space on the right.
float zoom = 0.5f;
PointF translate = new PointF(0, 0);
canvas.scale(zoom, zoom);
canvas.translate(translate.x, translate.y);
//...
canvas.drawRect(0, 0, width, height, paint);
sorry my bad English and explanation, but I want to ask in summary;
what is the true width/height limit for translate after zooming for moving the canvas?
I solved this problem as follows;
(screen resolution: 800x480 [landscape])
int screenWidth = 800;
int gameLimitX = 1600;
int cameraPositionX = 0;
float zoom = 1.0;
if((screenWidth / zoom) + cameraPositionX > gameLimitX) {
cameraPositionX = (screenWidth / zoom) + cameraPositionX;
}
(do same for y/height)
I hope you can understand.
You can save your current zoom and then multiply translations like this:
canvas.scale(_factorScale, _factorScale);
int wGameView = (int) (_wGameView/_factorScale);
int hGameView = (int) (_hGameView/_factorScale);
int xCam = (int) (ptCentre.x-(wGameView>>>1));
int yCam = (int) (ptCentre.y-(hGameView>>>1));
//move camera now!
canvas.translate(-xCam,-yCam);
//here goes drawing
I am detecting a face in my app. I use two ImageView, the first for the face and the second for a mask. I need set the mask position over the calculated face position and then can move the mask with touch event. For the touch event I did use of this tutorial: http://blahti.wordpress.com/2011/01/17/moving-views-part-2/
The method for mask scalling can return the face position. I tried set the XY position to the mask Imageview but ever is showed in the position 0.0. over the face. Then I can move the mask ImageView over the face. "view" is my mask. Ideally I need initialize the mask imageview over the face position.
PD: Sorry my english is bad.
protected Bitmap draw(int mode) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.c10);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Point position = new Point();
xRatio = anchoCara * 1.0f / anchoCara;
yRatio = altoCara * 1.0f / altoCara;
Bitmap resizedBitmap = null;
float factor = 9.6f;//9.6
for (int i = 0; i < eyesMidPts.length; i++) {
if (eyesMidPts[i] != null) {
pOuterBullsEye.setStrokeWidth(eyesDistance[i] / 6);
float newWidth = eyesDistance[i] * factor;
float newHeight = eyesDistance[i] * factor;
float scaleWidth = (newWidth) / width;
float scaleHeight = (newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, true);
position.set(
(int) ((eyesMidPts[i].x * xRatio) - (eyesDistance[i] * factor) ),//2
(int) ((eyesMidPts[i].y * yRatio) - (eyesDistance[i] * factor) ));
Log.e("Face", "positio x : " + position.x);
Log.e("Face", "positio y : " + position.y);
Log.e("Face", "mascara width : " + resizedBitmap.getWidth());
Log.e("Face", "mascara heigth : " + resizedBitmap.getHeight());
// view.setBackgroundDrawable(new BitmapDrawable(resizedBitmap));
// view.setAdjustViewBounds(true);
// Log.i("Face", "positio x : " + view.getScrollX());
// Log.i("Face", "positio y : " + view.getScrollY());
}
}
return resizedBitmap;
}
Finally I did using the next code:
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
faceDetect(MODO_VIEW);
Display m=getWindowManager().getDefaultDisplay();
anchoPant=m.getWidth();
Log.i("Face","separacion derecha: "+((anchoPant-cara.getWidth())/2)+"px" );
MyAbsoluteLayout.LayoutParams mp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, MyAbsoluteLayout.LayoutParams.WRAP_CONTENT, (anchoPant-cara.getWidth())/2, 0);
cara.setLayoutParams(mp);
//view.setBackgroundDrawable(new BitmapDrawable(draw(MODO_VIEW)));
view.setImageBitmap(draw(MODO_VIEW));
MyAbsoluteLayout.LayoutParams lp=new MyAbsoluteLayout.LayoutParams(MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,MyAbsoluteLayout.LayoutParams.WRAP_CONTENT,position.x+((anchoPant-cara.getWidth())/2),position.y);
view.setLayoutParams(lp);
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
In my question, the method can return the position, Then using MyAbsoluteLayout.LayoutParams over OnGlobalLayout() I can set XY Position without problems.