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
Related
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 need to know why this extraspace is added as left margin. Due to this wakllpaper is not setting properly in my app. If i try to set leftmost portion as wallpaper then centre portion is getting set as wallpaper due to this extraspace margin.
cropImageAndSetWallpaper(android.net.Uri uri, com.android.wallpapercropper.WallpaperCropActivity$OnBitmapCroppedHandler onBitmapCroppedHandler, boolean finishActivityWhenDone)
boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
// Get the crop
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
Display d = getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
d.getSize(displaySize);
boolean isPortrait = displaySize.x < displaySize.y;
Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
getWindowManager());
// Get the crop
RectF cropRect = mCropView.getCrop();
Point inSize = mCropView.getSourceDimensions();
int cropRotation = mCropView.getImageRotation();
float cropScale = mCropView.getWidth() / (float) cropRect.width();
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(cropRotation);
float[] rotatedInSize = new float[] { inSize.x, inSize.y };
rotateMatrix.mapPoints(rotatedInSize);
rotatedInSize[0] = Math.abs(rotatedInSize[0]);
rotatedInSize[1] = Math.abs(rotatedInSize[1]);
// Due to rounding errors in the cropview renderer the edges can be slightly offset
// therefore we ensure that the boundaries are sanely defined
cropRect.left = Math.max(0, cropRect.left);
cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
cropRect.top = Math.max(0, cropRect.top);
cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
// ADJUST CROP WIDTH
// Extend the crop all the way to the right, for parallax
// (or all the way to the left, in RTL)
float extraSpace;
if (centerCrop) {
extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
} else {
extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
}
// Cap the amount of extra width
float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
extraSpace = Math.min(extraSpace, maxExtraSpace);
if (centerCrop) {
cropRect.left -= extraSpace / 2f;
cropRect.right += extraSpace / 2f;
} else {
if (ltr) {
cropRect.right += extraSpace;
} else {
cropRect.left -= extraSpace;
}
}
// ADJUST CROP HEIGHT
if (isPortrait) {
cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
} else { // LANDSCAPE
float extraPortraitHeight =
defaultWallpaperSize.y / cropScale - cropRect.height();
float expandHeight =
Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
extraPortraitHeight / 2);
cropRect.top -= expandHeight;
cropRect.bottom += expandHeight;
}
final int outWidth = (int) Math.round(cropRect.width() * cropScale);
final int outHeight = (int) Math.round(cropRect.height() * cropScale);
Runnable onEndCrop = new Runnable() {
public void run() {
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
}
}
};
BitmapCropTask cropTask = new BitmapCropTask(this, uri,
cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
if (onBitmapCroppedHandler != null) {
cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
}
cropTask.execute();
Extra space is added to the wallpaper to allow for a parallax effect. In case your phone uses a right-to-left layout, this space is added to the left of the crop you chose. Google's launcher starts out on the rightmost page in RTL layouts.
If you choose a crop on the left side of the picture, then the expansion will take place on the right side of the crop, an you will see mainly this extension on the main page of the launcher.
Is there any resource on how to use the IndoorAtlas SDK?
I'm getting confused about how to show the floorPlan and getting current location.
Please kindly help me.
Here's very roughly how:
1) Initialize IndoorAtlas instance:
IndoorAtlas ia = IndoorAtlasFactory.createIndoorAtlas(context, listener, apiKey, apiSecret);
2) Obtain instance of FloorPlan:
FutureResult<FloorPlan> result = ia.fetchFloorPlan(floorPlanId);
result.setCallback(new ResultCallback<FloorPlan>() {
#Override
public void onResult(final FloorPlan result) {
mFloorPlan = result;
loadFloorPlanImage(result);
}
// handle error conditions too
}
3) Obtain actual image:
void loadFloorPlanImage(FloorPlan floorPlan) {
BitmapFactory.Options options = createBitmapOptions(floorPlan);
FutureResult<Bitmap> result = ia.fetchFloorPlanImage(floorPlan, options);
result.setCallback(new ResultCallback<Bitmap>() {
#Override
public void onResult(final Bitmap result) {
// now you have floor plan bitmap, do something with it
updateImageViewInUiThread(result);
}
// handle error conditions too
}
}
4) Start positioning:
ia.startPositioning(venueId, floorId, floorPlanId);
5) Show positions on floor plan:
public void onServiceUpdate(ServiceState state) {
// get position on original floor plan image
int i = state.getImagePoint().getI();
int j = state.getImagePoint().getJ();
// take into account how your floor plan image has been scaled
// and draw position
PointF scaledPoint = new PointF();
Util.calculateScaledPoint((int) floorPlan.dimensions[0], (int) floorPlan.dimensions[1], i, j, mImageView, scaledPoint);
drawNewPositionInUiThread(scaledPoint.x, scaledPoint.y);
}
Of course you can start positioning first and then obtain image. You could also cache image locally but like said, this was roughly how.
Util.java:
public class Utils {
/**
* Calculates scaling factor for an image with original dimensions of
* {#code originalWidth x originalHeight} being displayed with {#code imageView}.
*
* The assumption with this example code is that a) layout has been already performed for
* {#code imageView} and that {#link android.widget.ImageView.ScaleType#CENTER_INSIDE} is used.
*
* #param originalWidth height of the original bitmap to be displayed using {#code imageView}
* #param originalHeight width of the original bitmap to be displayed using {#code imageView}
*/
public static float calculateScaleFactor(int originalWidth, int originalHeight,
ImageView imageView) {
if (imageView.getScaleType() != ImageView.ScaleType.CENTER_INSIDE) {
throw new IllegalArgumentException("only scale type of CENTER_INSIDE supported, was: "
+ imageView.getScaleType());
}
final int availableX = imageView.getWidth()
- (imageView.getPaddingLeft() + imageView.getPaddingRight());
final int availableY = imageView.getHeight()
- (imageView.getPaddingTop() + imageView.getPaddingBottom());
if (originalWidth > availableX || originalHeight > availableY) {
// original image would not fit without scaling
return originalWidth > availableX
? availableX / (float) originalWidth
: availableY / (float) originalHeight;
} else {
return 1f; // no scaling required
}
}
/**
* Calculates point where to draw coordinates {#code x} and {#code y} in a bitmap that's
* original dimensions were {#code originalWidth x originalHeight} and may now be scaled down
* as it's been displayed with {#code imageView}.
*
* #param originalWidth width of the original bitmap before any scaling
* #param originalHeight height of the original bitmap before any scaling
* #param x x-coordinate on original bitmap
* #param y y-coordinate on original bitmap
* #param imageView view that will be used to display bitmap
* #param point point where result value is to be stored
* #see #calculateScaleFactor(int, int, ImageView)
*/
public static void calculateScaledPoint(int originalWidth, int originalHeight,
int x, int y,
ImageView imageView,
PointF point) {
final float scale = calculateScaleFactor(originalWidth, originalHeight, imageView);
final float scaledWidth = originalWidth * scale;
final float scaledHeight = originalHeight * scale;
// when image inside view is smaller than the view itself and image is centered (assumption)
// there will be some empty space around the image (here offset)
final float offsetX = Math.max(0, (imageView.getWidth() - scaledWidth) / 2);
final float offsetY = Math.max(0, (imageView.getHeight() - scaledHeight) / 2);
point.x = offsetX + (x * scale);
point.y = offsetY + (y * scale);
}
}
Hii everyone currently iam working on scanning qr code from my app and i have used zxing library and it's working good and my problem is in my galaxy s4 mobile the scanning area is very small
Please help me
thanks in advance
I know it is too much late but help for others
just go to camera manager class and paste this code on replacement of given method
it works for all types of screens
public Rect getFramingRect() {
if (framingRect == null) {
if (camera == null) {
return null;
}
Point screenResolution = configManager.getScreenResolution();
int width = screenResolution.x * 3 / 4;
int height = screenResolution.y * 3 / 4;
Log.v("Framing rect is : ", "width is "+width+" and height is "+height);
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
Log.d(TAG, "Calculated framing rect: " + framingRect);
}
return framingRect;
}
public Rect getFramingRect() {
if (framingRect == null) {
if (camera == null) {
return null;
}
Point screenResolution = configManager.getScreenResolution();
int screenx = screenResolution.x;
int screeny = screenResolution.y;
int width, height, left, top;
if (screenx > screeny) {
width = (int) (screenx * 12.5 / 100);
height = (int) (screeny * 25 / 100);
left = (int) screenx * 83 / 100;
top = (int) screeny * 75 / 100;
} else {
left = (int) (screenx * 12.5 / 100);
top = (int) (screeny * 25 / 100);
width = (int) screenx * 83 / 100;
height = (int) screeny * 75 / 100;
}
framingRect = new Rect(left,top, width, height);
Log.d(TAG, "Calculated framing rect: " + framingRect);
}
return framingRect;
}
Replace the above code in CameraManager.java file
this worked for me try this out
The CameraManager class has two constants defined MIN_FRAME_WIDTH and MIN_FRAME_HEIGHT. You should modify them as desired and everything should work:
private static final int MIN_FRAME_WIDTH = 240; // (your desired value here)
private static final int MIN_FRAME_HEIGHT = 240; // (your desired value here)
If you are calling this from another android app, use intent extras SCAN_WIDTH and SCAN_HEIGHT for this.
If you happen to be using phonegap-plugin-barcodescanner (3.0.0 or later), then passing the same intents like xxxxx.scan(onSuccessFunc,onFailFunc,{SCAN_HEIGHT:111,SCAN_WIDTH:222}) will produce the same result. 111 being the height, and 222 being the width.
If you are using ZxingScannerView, you can override createViewFinderView() and increase or decrease the framing rectangle size via viewFinderView like this :
scannerView = object : ZXingScannerView(requireContext()) {
override fun createViewFinderView(context: Context?): IViewFinder {
val viewfinderView = super.createViewFinderView(context)
viewfinderView.setViewFinderOffset(-90) // increase size of framing rectangle
return viewfinderView;
}
}
yourLayour.addView(scannerView)
scannerView.startCamera()
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);
}