Android : place views along circle in a layout at runtime - android

What i want to do is place view along circle whose center is a parent layout.
I tried my best, I can't see anything. Below is my code.(alphabetView is just simple extends from textview. for debugging purpose i commented placing along circle,and change it to placing along simple line)
When i placed it at a constructor, i couldn't get width and height, but i could see child views. so far as i know, i can only get 0 width and height in constructor. So I moved code to onSizeChanged.
But now, after place it on onSizeChanged, i could get center of parent layout, but i can't see any child.
Am i misunderstand something? Is there any better way to achieve placing view along ring?
public class RingUIView extends RelativeLayout {
private String[] _keySet = {"A", "B", "C", "D", "E", "F", "G"};
private double UI_SIZE = 200;
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
float centerX = getX() + getWidth() / 2;
float centerY = getY() + getHeight() / 2;
Log.e("ASDF", String.format("%f, %f", centerX, centerY));
double radian = 0;
for (int i = 0; i < _keySet.length; i++) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.alphabet_view, null);
AlphabetView alphabetView = (AlphabetView) view;
alphabetView.setText(_keySet[i]);
this.addView(alphabetView);
/*float childX = (float)(centerX+UI_SIZE*Math.cos(radian) - alphabetView.getWidth());
float childY = (float)(centerY+UI_SIZE*Math.sin(radian) - alphabetView.getHeight());*/
float childX = centerX + 20 * i;
float childY = centerY + 20 * i;
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
params.leftMargin = (int) childX;
params.topMargin = (int) childY;
radian += Math.PI / 4;
}
}
public RingUIView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}

Related

What is causing the item(View) in the Container (ViewGroup) class to move quickly and randomly after it has been rotated 90 degrees?

Description:
I have a class called Container which can hold items. In the code below I added only one item to the container. The user can move this item by dragging it and rotating it by 90 degrees by double-clicking on it.
Before double-clicking on it (before applying the rotation), it moves as expected when the user drags the item.
If the user double-clicks the item (applies 90 degrees rotation) and then tries to move it by dragging it, it unexpectedly moves so quickly out of the window and in a random direction.
Demo:
Code:
Container.java
public class Container extends ViewGroup {
public Container(Context context) {
super(context);
}
public Container(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Container(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//Layout all children used fixed position
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i<count; i++) {
View child = getChildAt(i);
int left = child.getLeft();
int top = child.getTop();
int right = child.getRight();
int bottom = child.getBottom();
child.layout(left, top, right, bottom);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i<count; i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Item.java
public class Item extends View {
// default width and height for this item
private static final int WIDTH = 200;
private static final int HEIGHT = 80;
private final Paint mPaint;
private final static int OFFSET = 1;
//Store the click position
private float mStartX = 0;
private float mStartY = 0;
//Used to detect double-click on this item
private long mStartTime = 0;
private static final int DOUBLE_TAP_THRESHOLD = 200;
public Item(Context context, int x, int y) {
super(context);
// Set the size of the item,
// No need to override onMeasure()
setLeft(x);
setTop(y);
setRight(x + WIDTH);
setBottom(y + HEIGHT);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
canvas.drawRect(OFFSET, OFFSET, width-OFFSET, height-OFFSET, mPaint);
}
// Rotate the item if the user double click on it
// And move the item if the user drag it
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
long currentTime = System.currentTimeMillis();
if(currentTime - mStartTime < DOUBLE_TAP_THRESHOLD) {
//Double click detected
// Apply rotation
setRotation(getRotation() + 90); // rotation that cause the problem
return true;
}
mStartTime = currentTime;
mStartX = event.getX();
mStartY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
// compute the new position for the item
float deltaX = event.getX() - mStartX;
float deltaY = event.getY() - mStartY;
float newX = getX() + deltaX;
float newY = getY() + deltaY;
//move the item
setX(newX);
setY(newY);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Container container = findViewById(R.id.container);
container.addView(new Item(this, 100, 200));
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.package.name.Container
android:id="#+id/container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Question: Why does the item move so quickly when rotating it? and how to fix this issue.
Your problem is different frames of reference. X and Y are relative to the view's upper left hand corner. When you rotate and move the view, you're changing the relative coordinates of the view, which is going to lead to some funnky math.
There's 2 solutions to this. The one that changes your code the least is to convert all touch coordienates to a fixed coordinate system first. For example, the parent view's coordinate system, or the screens. view.getX is relative to its parent already.
The second and probably better way to do it- don't have the individual views control that. Do it all in a touch handler of the parent. The parent knows where all its children are and is responsible for laying them out, so this not only is more aligned with how Android works, but it avoids the problem of changing coordinate systems. All you have to do to make that work is make the views ignore all touches (their default behavior) and touches on them will fall through to their parent.
Here is how I fixed this issue. You need to take into account the rotation angle by using the rotation matrix:
x' = x*cos(theta) - y*sin(theta)
y' = x*sin(theta) - y*cos(theta)
There are two ways to implement this:
By using the direct formula
double rotation = Math.toRadians(getRotation());
float newX = (float) (getX() + deltaX*Math.cos(rotation) - deltaY*Math.sin(rotation));
float newY = (float) (getY() + deltaX*Math.sin(rotation) + deltaY*Math.cos(rotation));
//move the item
setX(newX);
setY(newY);
By using the Matrix class
float[] point = new float[] {deltaX, deltaY};
mapPoint(point, getRotation());
float newX = getX() + point[0];
float newY = getY() + point[1];
//move the item
setX(newX);
setY(newY);
Here is the helper method mapPoint:
private void mapPoint(float[] point, float angle) {
Matrix matrix = new Matrix();
matrix.setRotate(angle);
matrix.mapPoints(point);
}

Semi circular menu with elements facing towards center

I need a semi circular menu for my app .
I have been using this library to achieve the same :-
https://github.com/xresco/CircularLayout
I am able to achieve something like this:-
As you can see the Library works well for circular layout , however for rectangular layouts , not so good.
The code which handles the rotation of the rectangles is as follows:-
#Override
#SuppressWarnings("deprecation")
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int childs = getChildCount();
float totalWeight = 0f;
for(int i=0; i<childs; i++) {
final View child = getChildAt(i);
LayoutParams lp = layoutParams(child);
totalWeight += lp.weight;
}
shiftAngle= mAngleRange/totalWeight;
final int width = getWidth();
final int height = getHeight();
final float minDimen = width > height ? height : width;
float radius = (minDimen )/2f;
radius=(float) (width/2)+radius_parameter;
float startAngle = mAngleOffset+270;
for(int i=0; i<childs; i++) {
final View child = getChildAt(i);
final LayoutParams lp = layoutParams(child);
final float angle = mAngleRange/totalWeight * lp.weight;
final float centerAngle = startAngle ;
final int x;
final int y;
Log.e("CenterAngle", String.valueOf(centerAngle));
if(child instanceof CircularLayoutItem)
{
CircularLayoutItem civ=(CircularLayoutItem)child;
civ.setRotationParameters((int)(centerAngle)+90);
}
if(childs > 1) {
x = (int) (radius * Math.cos(Math.toRadians(centerAngle))) + width/2+center_width_parameter;
y = (int) (radius * Math.sin(Math.toRadians(centerAngle))) + height/3+(int)radius+center_height_parameter;
} else {
x = width/2;
y = height/2;
}
final int halfChildWidth = child.getMeasuredWidth()/2;
final int halfChildHeight = child.getMeasuredHeight()/2;
final int left = lp.width != LayoutParams.FILL_PARENT ? x - halfChildWidth : 0;
final int top = lp.height != LayoutParams.FILL_PARENT ? y - halfChildHeight : 0;
final int right = lp.width != LayoutParams.FILL_PARENT ? x + halfChildWidth : width;
final int bottom = lp.height != LayoutParams.FILL_PARENT ? y + halfChildHeight : height;
Log.e("Coords", String.valueOf(left)+"/"+right+"/"+top+"/"+bottom);
child.layout(left, top, right, bottom);
startAngle += angle;
}
invalidate();
}
Is there a way so that the rectangles appear as whole and still facing to the center?
I have come here from here:-
Horizontal Curved Sliding menu
Any help is appreciated.Thank you.

How to get the position of a picture inside an ImageView?

I have a gallery of images with different sizes. Each image is displayed inside an ImageView sequentially (through OnTouchListener). I need to know the position of the frame of the picture I'm showing relatives to the ImageView but with the testing I've done I've only gotten the coordinates of ImageView. Any idea?
I need the values of (x1, y1) and (x2, y2).
Thanks in advance.
This is my class:
public class PuzzleView extends ImageView {
protected Paint currentPaint;
protected boolean drawRect = false;
protected float left;
protected float top;
protected float right;
protected float bottom;
protected float pixelX;
protected float pixelY;
protected int nChunksX = 5;
protected int nChunksY = 5;
protected int currentWidth = 0;
protected int currentHeight = 0;
public PuzzleView(Context context, AttributeSet attrs) {
super(context, attrs);
currentPaint = new Paint();
currentPaint.setDither(true);
currentPaint.setColor(0xFF00CC00);
currentPaint.setStyle(Paint.Style.STROKE);
currentPaint.setStrokeJoin(Paint.Join.ROUND);
currentPaint.setStrokeCap(Paint.Cap.ROUND);
currentPaint.setStrokeWidth(2);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float chunkWidth = currentWidth / nChunksX;
float chunkHeight = currentHeight / nChunksY;
float posX = ((int)(pixelX / chunkWidth)) * chunkWidth;
float posY = ((int)(pixelY / chunkHeight)) * chunkHeight;
canvas.drawRect(posX, posY, posX + chunkWidth, posY + chunkHeight, currentPaint);
Rect rect = this.getDrawable().getBounds();
canvas.drawRect(rect, currentPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Get image matrix values and place them in an array
float[] f = new float[9];
getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
currentWidth = actW;
currentHeight = actH;
}
public boolean isDrawRect() {
return drawRect;
}
public void setDrawRect(boolean drawRect) {
this.drawRect = drawRect;
}
public float getLeftRect() {
return left;
}
public void setLeftRect(float left) {
this.left = left;
}
public float getTopRect() {
return top;
}
public void setTopRect(float top) {
this.top = top;
}
public float getRightRect() {
return right;
}
public void setRightRect(float right) {
this.right = right;
}
public float getBottomRect() {
return bottom;
}
public void setBottomRect(float bottom) {
this.bottom = bottom;
}
public float getPixelX() {
return pixelX;
}
public void setPixelX(float pixelX) {
this.pixelX = pixelX;
}
public float getPixelY() {
return pixelY;
}
public void setPixelY(float pixelY) {
this.pixelY = pixelY;
}
public int getChunksX() {
return nChunksX;
}
public void setChunksX(int nChunksX) {
this.nChunksX = nChunksX;
}
public int getChunksY() {
return nChunksY;
}
public void setChunksY(int nChunksY) {
this.nChunksY = nChunksY;
}
}
For now, the source image is defined in XML file:
<com.jocajica.shakepic.PuzzleView
android:id="#+id/imageViewSelected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:contentDescription="#string/image_selected"
android:src="#android:drawable/progress_indeterminate_horizontal" />
I need to draw a grid over the image.
According to Jacob Nordfalk's link, I was able to produce a static method allowing you to get the image position and dimensions from an ImageView.
/**
* Returns the bitmap position inside an imageView.
* #param imageView source ImageView
* #return 0: left, 1: top, 2: width, 3: height
*/
public static int[] getBitmapPositionInsideImageView(ImageView imageView) {
int[] ret = new int[4];
if (imageView == null || imageView.getDrawable() == null)
return ret;
// Get image dimensions
// Get image matrix values and place them in an array
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView.getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
ret[2] = actW;
ret[3] = actH;
// Get image position
// We assume that the image is centered into ImageView
int imgViewW = imageView.getWidth();
int imgViewH = imageView.getHeight();
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
ret[0] = left;
ret[1] = top;
return ret;
}
You should use getImageMatrix():
float[] imageMatrix = new float[9];
getImageMatrix().getValues(imageMatrix);
scale = imageMatrix[Matrix.MSCALE_X];
transX = imageMatrix[Matrix.MTRANS_X];
See also
Trying to get the display size of an image in an ImageView
Thank you Quentis S. and Jacob Nordfalk for the very usefull routine.
I took the liberty of changing the return value from an array to a Rect object.
/**
* Returns the bitmap position inside an imageView.
*
* #param imageView source ImageView
* #return Rect position of the bitmap in the ImageView
*/
public static final Rect getBitmapPositionInsideImageView(ImageView imageView)
{
Rect rect = new Rect();
if (imageView == null || imageView.getDrawable() == null)
{
return rect;
}
// Get image dimensions
// Get image matrix values and place them in an array
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView.getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
// Get image position
// We assume that the image is centered into ImageView
int imgViewW = imageView.getWidth();
int imgViewH = imageView.getHeight();
rect.top = (int) (imgViewH - actH) / 2;
rect.left = (int) (imgViewW - actW) / 2;
rect.bottom = rect.top + actH;
rect.right = rect.left + actW;
return rect;
}
Override the onLayout method
//inside puzzleView
float[] matrix = new float[9];
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
matrix = getMatrix();
}
public void getMatrix(){
return matrix;
}
private float[] getMatrix() {
final float[] matrix = new float[9];
getImageMatrix().getValues(matrix);
return matrix;
}
and use this to get values
// Extract the scale and translation values from the matrix.
float scaleX = matrix[Matrix.MSCALE_X];
float scaleY = matrix[Matrix.MSCALE_Y];
float transX = matrix[Matrix.MTRANS_X];
float transY = matrix[Matrix.MTRANS_Y];
I hope that this help u

Rotated OpenStreetMap view - how to Swipe map in direction of finger move after rotation in Android?

I am using the OSM for a mapping application where I
rotate the map in direction of travel as explained here Android Rotating MapView . This works well.
However, I haven't yet managed to adjust the dispatchTouchEvent code
to counter the map rotation effect for the user touches (right now
when the map is rotated 90 degrees a user's horizontal sweep will move
the map vertically etc). The sample code only offers the teaser:
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO: rotate events too
return super.dispatchTouchEvent(ev);
}
Any guidance would be appreciated.
And while I am at it - Is it still possible to position the zoom
controls separately, so that they do NOT rotate when the map rotates?
I read that the getZoomControls() is deprecated. (Why ?)
I know it's too late but it may help someone...
import org.osmdroid.views.overlay.MyLocationOverlay;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Matrix ;
import android.hardware.Sensor;
import android.os.BatteryManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
/**
* Rotate the Map in accordance to the movement of user and always point to North
*
*/
public class RotatingRelativeLayout extends RelativeLayout
{
private Matrix mMatrix = new Matrix();
private float[] mTemp = new float[2];
private Context context;
private static final float SQ2 = 1.414213562373095f;
public RotatingRelativeLayout(final Context pContext,
final AttributeSet pAttrs)
{
super(pContext, pAttrs);
this.context=pContext;
}
#Override
protected void dispatchDraw(Canvas canvas) {
long rotateTime = MyLocationOverlay.getTimeOfMovement();
float overlayBearing = MyLocationOverlay.getBearing();//this method returns current bearing from OSM
long currentTime = System.currentTimeMillis();
long diffTime = currentTime-rotateTime;
/*
* Here we rotate map in accordance with Compass to point always North
*
*/
if(diffTime >= (40*1000 )){
//isBearing=false;
overlayBearing=0;
canvas.rotate(overlayBearing, getWidth() * 0.5f, getHeight() * 0.5f);
}
else
/*
* Rotate Map According to the user movement
*/
canvas.rotate(-overlayBearing, getWidth() * 0.5f, getHeight() * 0.5f);
canvas.getMatrix().invert(mMatrix);
final float w = this.getWidth();
final float h = this.getHeight();
final float scaleFactor = (float)(Math.sqrt(h * h + w * w) / Math.min(w, h));
canvas.scale(scaleFactor, scaleFactor, getWidth() * 0.5f, getHeight() * 0.5f);
super.dispatchDraw(canvas);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.restore();
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = getWidth();
final int height = getHeight();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View view = getChildAt(i);
final int childWidth = view.getMeasuredWidth();
final int childHeight = view.getMeasuredHeight();
final int childLeft = (width - childWidth) / 2;
final int childTop = (height - childHeight) / 2;
view.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int h = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int sizeSpec;
if (w > h) {
sizeSpec = MeasureSpec.makeMeasureSpec((int) (w*SQ2), MeasureSpec.EXACTLY);
}
else {
sizeSpec = MeasureSpec.makeMeasureSpec((int) (h*SQ2), MeasureSpec.EXACTLY);
}
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(sizeSpec, sizeSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
final float[] temp = mTemp;
temp[0] = event.getX();
temp[1] = event.getY();
mMatrix.mapPoints(temp);
event.setLocation(temp[0], temp[1]);
return super.dispatchTouchEvent(event);
}
}
now just use this Relative Layout in your xml like this:-
<YourPackageName.RotatingRelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="#+id/rotating_layout" android:layout_marginBottom="40dip"/>
And add Map View Programatially like this:-
// Find target container
final RelativeLayout rl=(RelativeLayout)mParent.findViewById(R.id.rotating_layout);
// Create rotator
mRotator=new RotatingRelativeLayout(mParent, null);
// Add map to the rotating layout
// Add rotator to the screen
rl.addView(mMap,new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
here mParent is Context. And one more thing if you encounter Image Pixelation prob you just have to use it
//Paint distortion handling..
p.setFilterBitmap(true);
Hope i explained it as good as i could.... feel free to ask if you find problem understanding it.
Thanks.

android Gallery with arbitrary aspect ratios

i wish to have an android gallery that will host images of varying aspect ratios. what i'd like is something like CENTER_CROP for the images in the gallery. however, when i set the image scale type to this, the images overrun the gallery image border.
of course, FIT_XY results in squished / flattened images. CENTER results in horizontal or vertical black space inside the gallery image border.
any ideas how to accomplish this? every example i can find uses FIT_XY with fixed size images. i suppose i could crop the images myself but i'd rather not.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView iv = (ImageView) convertView;
if (iv == null) {
iv = new ImageView(context);
iv.setScaleType(ImageView.ScaleType.FIT_XY);
iv.setBackgroundResource(galleryItemBackground);
iv.setLayoutParams(new Gallery.LayoutParams(200, 200));
}
InputStream is;
try {
is = getInputStream(position);
} catch (IOException ioe) {
// TODO?
throw new RuntimeException(ioe);
}
Bitmap bm = BitmapFactory.decodeStream(is);
iv.setImageBitmap(bm);
/*
* if (bitmaps[position] != null) { bitmaps[position].recycle();
* bitmaps[position] = null; } bitmaps[position] = bm;
*/
return iv;
}
I had the same problem as you and looking at ScaleType documentation I found it could be done using ScaleType.MATRIX, for example:
int w = 1000;
int h = 1000;
Paint p = new Paint();
p.setShader(new LinearGradient(0, 0, w, h, Color.BLACK, Color.RED, Shader.TileMode.CLAMP));
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas g = new Canvas(bmp);
g.drawRect(new Rect(0, 0, w, h), p);
ImageView i3 = new ImageView(context);
i3.setImageBitmap(bmp);
i3.setScaleType(ScaleType.MATRIX);
int viewWidth = 300;
int viewHeight = 300;
Matrix matrix = new Matrix();
matrix.postScale(bmp.getWidth() / viewWidth, bmp.getHeight() / viewHeight, bmp.getWidth() / 2, bmp.getHeight() / 2);
i3.setImageMatrix(matrix);
LayoutParams lp2 = new LayoutParams(viewWidth, viewHeight);
lp2.leftMargin = 100;
lp2.topMargin = 400;
lp2.gravity = 0;
this.addView(i3, lp2);
This solution complicates things a little bit too much though. If you want to scroll and zoom the ImageView you need to use matrix scaling as well. So I'd be interested in knowing any possible alternative.
For this kind of tasks I use this simple class. It fits height or width scaling the image properly (it depends on which is the smaller dimension) . After this operation it centers the image in the ImageView bounds.
public class FixedCenterCrop extends ImageView {
public FixedCenterCrop(Context context) {
super(context);
setScaleType(ScaleType.MATRIX);
}
public FixedCenterCrop(Context context, AttributeSet attrs) {
super(context, attrs);
setScaleType(ScaleType.MATRIX);
}
public FixedCenterCrop(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setScaleType(ScaleType.MATRIX);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
recomputeImgMatrix();
}
#Override
protected boolean setFrame(int l, int t, int r, int b) {
recomputeImgMatrix();
return super.setFrame(l, t, r, b);
}
private void recomputeImgMatrix() {
final Matrix matrix = getImageMatrix();
float scale;
final int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight();
final int viewHeight = getHeight() - getPaddingTop() - getPaddingBottom();
final int drawableWidth = getDrawable().getIntrinsicWidth();
final int drawableHeight = getDrawable().getIntrinsicHeight();
if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
scale = (float) viewHeight / (float) drawableHeight;
} else {
scale = (float) viewWidth / (float) drawableWidth;
}
matrix.setScale(scale, scale);
matrix.postTranslate((viewWidth - drawableWidth * scale) / 2, (viewHeight - drawableHeight*scale)/2);
setImageMatrix(matrix);
}
}
I ended up just trimming the bitmap to a square myself, as,
Bitmap bm = BitmapFactory.decodeStream(is);
// make it square
int w = bm.getWidth();
int h = bm.getHeight();
if (w > h) {
// trim width
bm = Bitmap.createBitmap(bm, (w - h) / 2, 0, h, h);
} else {
bm = Bitmap.createBitmap(bm, 0, (h - w) / 2, w, w);
}

Categories

Resources