This question already has answers here:
onMeasure custom view explanation
(3 answers)
Closed 7 years ago.
I have a layout with several views and one of these views have to be scaled based on screen size. As of now I have it set to 250dp as it looks good on a 5.1 inch device, but when used on smaller devices it isnt as good any more. I can't use wrap content as it will make the looks of it very bad and the size of buttons too small, so it has to be 1/3rd of the width of the screen. Is it possible to do that in the layout code, or does it have to be doen in java code?
Is it possible to do that in the layout code
Yes it is. It is pretty simple by using android:layout_weight and android:weightSum attributes of LinearLayout :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="3"
android:orientation="horizontal">
<View
android:id="#+id/thirdView"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1" />
</LinearLayout>
You need to override onMeasure(...) then do your view resizing login inside this function. Here is the perfect explanation and solution.
Some reference code from this link goes below...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 100;
int desiredHeight = 100;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}
You could have a look at the PercentRelativeLayout from the Percent Support Library. This is a RelativeLayout in which you can specify it's Views sizes in percentages, e.g. set its width to 33%.
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
app:layout_widthPercent="33%"
app:layout_heightPercent="33%"
app:layout_marginTopPercent="25%"
app:layout_marginLeftPercent="25%" />
</android.support.percent.PercentRelativeLayout>
Related
I am creating a game, where there will be a lot of clickable ImageView(s), and one unclickable imageView, when the clickable ImageView is clicked, it should swap its position with the unclickable ImageView (in this example, its called imageView2).
Everything works fine when I am using px unit for my imageView(s). However, as you know using px units will resulted in different display on different devices. However, the property animations does not work when I use dp units. How do I get it working with dp units ??
MainActivity.java
public class MainActivity extends Activity implements View.OnClickListener {
ImageView imageView1, imageView2;
float x, x_9, y, y_9;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView1 = (ImageView) findViewById(R.id.imageView1);
imageView2 = (ImageView) findViewById(R.id.imageView2);
imageView1.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Get x position of clicked imageView
x = v.getX();
// Get x position of imageView2
x_9 = imageView2.getX();
// Get y position of clicked imageView
y = v.getY();
// Get y position of imageView2
y_9 = imageView2.getY();
// Check if imageViews are align with each other either horizontally or vertically
if((x == x_9 && y + 100 == y_9) || (x == x_9 && y - 100 == y_9) || (x + 100 == x_9 && y == y_9) || (x - 100 == x_9 && y == y_9)) {
// If they are aligned, swap their position with property animation
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", x_9);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", y_9);
PropertyValuesHolder pvhX9 = PropertyValuesHolder.ofFloat("translationX", x);
PropertyValuesHolder pvhY9 = PropertyValuesHolder.ofFloat("translationY", y);
ObjectAnimator blockAnim = ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY);
blockAnim.setDuration(500);
blockAnim.setRepeatCount(0);
blockAnim.start();
ObjectAnimator blockAnim2 = ObjectAnimator.ofPropertyValuesHolder(imageView2, pvhX9, pvhY9);
blockAnim2.setDuration(500);
blockAnim2.setRepeatCount(0);
blockAnim2.start();
}
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity">
<RelativeLayout
android:id="#+id/relative"
android:background="#color/grey"
android:layout_width="400dp"
android:layout_height="400dp"
>
<ImageView
android:id="#+id/imageView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/smiley1"/>
<ImageView
android:id="#+id/imageView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/smiley2"
android:translationX="100dp"/>
</RelativeLayout>
</RelativeLayout>
How do I work around this problem ? All suggestions are greatly welcomed. Thank you.
As suggested by Nikola Milutinovic, I added GlobalLayoutListener like below
this.findViewById(R.id.relative).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
x = v.getX();
x_9 = imageView2.getX();
y = v.getY();
y_9 = imageView2.getY();
}
});
I think the way to do it is to change your hard coded referece to the width and height by the actual value contained by the imageViews. In your if condition change 100 to getHeight() or getWidth();
int height = imageView1.getHeight();
int width = imageView1.getWidth();
if((x == x_9 && y + height == y_9) || (x == x_9 && y - height == y_9) ||
(x + width == x_9 && y == y_9) || (x - width == x_9 && y == y_9)){
}
This way you do not have a reference to px. The "100" value that your were using meant 100px. The real width or height for an hdpi device would of been something like 1.5*100 = 150px. By using getWidth() and getHeight(), you are using the actual value of px in your screen. I hope it is clear. You will need to modify your if condition for more than 2 imageViews, but the principle is the same.
Finally it is better to use AnimatorSet if you want your animations to be synchronized.
ArrayList <Animator> animations = new ArrayList<Animator>();
animations.add(ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY));
animations.add(ObjectAnimator.ofPropertyValuesHolder(imageView2, pvhX9, pvhY9));
AnimatorSet animatoreSet = new AnimatorSet();
animatorSet.setDuration(500);
animatorSet.setRepeatCount(0);
animatorSet.playTogether(animations);
animatorSet.start();
By doing this if you need to add listeners you just add them once.
See documentation for these two methods below and find which one suits you the most. container would be your root layout (this RelativeLayout at the top or this with id relative)
container.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
}
});
container.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
}
});
Here is the example of my implementation of this listener.
I do all sorts of stuff inside. Please take a look.
public class CustomOnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private Context context;
private View fragment;
private float x1;
private float y1;
private float x2;
private float y2;
public CustomOnGlobalLayoutListener(Context context, View view, float x1, float y1, float x2, float y2) {
this.context = context;
this.fragment = view;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
#Override
public void onGlobalLayout() {
fragment.setX(x1);
fragment.setY(y1);
removeOnGlobalLayoutListener();
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fragment.getLayoutParams();
fragment.setLayoutParams(layoutParams);
fragment.setPivotX(0);
fragment.setPivotY(0);
fragment.setScaleX((x2 - x1) / fragment.getMeasuredWidth());
fragment.setScaleY((y2 - y1) / fragment.getMeasuredHeight());
fragment.setAlpha(0f);
PropertyValuesHolder pA = PropertyValuesHolder.ofFloat(View.ALPHA, 1f);
PropertyValuesHolder pX = PropertyValuesHolder.ofFloat(View.X, 0f);
PropertyValuesHolder pY = PropertyValuesHolder.ofFloat(View.Y, 0f);
PropertyValuesHolder pSx = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
PropertyValuesHolder pSy = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
final ObjectAnimator animator1 = ObjectAnimator.ofPropertyValuesHolder(fragment, pA, pX, pY, pSx, pSy);
animator1.setInterpolator(new AccelerateDecelerateInterpolator());
animator1.setDuration(200);
animator1.start();
}
private void removeOnGlobalLayoutListener() {
if (Build.VERSION.SDK_INT < 16) {
//noinspection deprecation
fragment.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
fragment.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
}
DO NOT FORGET TO REMOVE GLOBAL LAYOUT LISTENER;
If you have any problem implementing this, write and we will think something out :)
You can also check my github repo where I am actually using this to achieve opening fragment with viewpager from point of touch.
https://github.com/mrnmilutinovic/HumanityTestProject
Check ImagePagerFragment class to see it's usage.
This code is compilable, so you can also run and see that it works.
I'm trying to crop a sprite from a spritesheet, however the cropped image is not the part of the image I specified.
The spritesheet contains sprites with a size of 32 x 32 px and therefore the first sprite should be at pixels 0 to 32 (horizontally) and 0 to 32 (vertically), supposed the most upper left pixel is [0, 0].
Bitmap spritesheet = BitmapFactory.decodeResource(getResources(), R.drawable.spritesheet_32x32);
Bitmap sprite = Bitmap.createBitmap(spritesheet, 0, 0, 32, 32);
The ImageView is just a plain ImageView without a src, width or height set.
ImageView view = (ImageView)findViewById(R.id.player1);
view.setImageBitmap(sprite);
But the displayed image is just a much smaller part of the first sprite.
What do I oversee? Is view.setImageBitmap(sprite); the right method for this? Does the ImageView need a fix width and height before setting the bitmap?
EDIT: Maybe I should get clearer about my layout. There are two ImageViews arranged on a LinearLayout. Here's the XML:
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="false">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="-1"
android:id="#+id/player1"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/player2"
android:layout_weight="1"
android:layout_gravity="center_vertical" />
</LinearLayout>
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class ImageResizer {
public static Bitmap decodeSampledBitmapFromFile(String filename,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options
options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// BEGIN_INCLUDE (calculate_sample_size)
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
long totalPixels = width * height / inSampleSize;
// Anything more than 2x the requested pixels we'll sample down further
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2;
totalPixels /= 2;
}
}
return inSampleSize;
// END_INCLUDE (calculate_sample_size)
}
}
Call the method
Bitmap bmp = ImageResizer.decodeSampledBitmapFromFile(new File(filePath).getAbsolutePath(), 512, 342);
I haven't tried but the follows might work pass the method parameter like
imageView.getWidth() , imageView.getHeight()
Hello I have two images and i am merging that images using canvas
here is the below code
Intent intent = getIntent();
//bm is the image get from camera
bm = BitmapFactory.decodeFile(intent.getStringExtra("getdata"));
//this is simple white text image
bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.txt_made_hawk_nelson);
imgSeletedPhoto = (ImageView) findViewById(R.id.imgSelectedPhoto);
int width = bm.getWidth();
int height = bm.getHeight();
int maxWidth = (width > bm1.getWidth() ? width : bm1.getWidth());
int maxHeight = (height > bm1.getHeight() ? height : bm1.getHeight());
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) maxWidth) / width;
float scaleHeight = ((float) maxHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
bmOverlay = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
Canvas canvas = new Canvas(bmOverlay);
//canvas.drawBitmap(bm, 0, 0, null);
canvas.drawBitmap(bm1, 0, 50, null);
imgSeletedPhoto.setImageBitmap(bmOverlay);
} catch (Exception e) {
e.printStackTrace();
}
i want to scale image and image should look like below
but instead of by doing above code it is looking like
XML code of this file is
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:weightSum="10" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="7" > // this is covering 70% of screen
<ImageView
android:id="#+id/imgSelectedPhoto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true" />
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:background="#baca9d"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
can any body help me how to stretch image and make it full from bottom and top side also and put text in center
int targetWidth = 70% of screen width;
int targetHeight = 70% of screen height;
int saclex = (int)((float)targetWidth / (float)bmp1.getWidth());
int sacley = (int)((float)targetHeight / (float)bmp1.getHeight());
apply scale values to bmp1 and create scaled bitmap
saclex = (int)((float)targetWidth / (float)bmp.getWidth());
sacley = (int)((float)targetHeight / (float)bmp.getHeight());
apply scale values bmp and create scaled bitmap. Hope this will help. or else you can use this method to create it.
Bitmap bitmap = Bitmap.createScaledBitmap(mTargetImage, bWidth,
bHeight, false);
Currently I am using the following to set height and width:
//landsapeWebView is a WebView, and deviceHeight and deviceHeight are ints
landsapeWebView.setLayoutParams(new RelativeLayout.LayoutParams(deviceHeight, deviceWidth));
This seems to work fine on some devices but not others. The only conclusion I've come to is that this is setting the height and width like this:
<WebView android:layout_width="(deviceWidth)dp"
android:layout_height="(deviceHeight)dp" />
when what I want is this:
<WebView android:layout_width="(deviceWidth)px"
android:layout_height="(deviceHeight)px" />
How can I specify the unit of measure?
Extra disclosure for #collusionbdbh
display = getWindowManager().getDefaultDisplay();
if(getResources().getConfiguration().orientation == 1){
deviceWidth = display.getWidth();
deviceHeight = display.getHeight();
}else{
deviceWidth = display.getHeight();
deviceHeight = display.getWidth();
}
Try this:
DisplayMetrics metrics = this.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Are you sure it is not an orientation issue?
Regardless of the orientation of the device the width is the horizontal and the height is the vertical.
I would try this:
display = getWindowManager().getDefaultDisplay();
deviceWidth = display.getWidth();
deviceHeight = display.getHeight();
I am pretty sure width should be before height:
landsapeWebView.setLayoutParams(new RelativeLayout.LayoutParams(deviceHeight, deviceWidth));
should be:
landsapeWebView.setLayoutParams(new RelativeLayout.LayoutParams(deviceWidth, deviceHeight));
I have a layout that contains several sub-views all which set height like:
<View android:layout_width="fill_parent"
android:layout_height="18dip"
android:background="#ff000000" />
How can I retrieve the value stored in layout_height in code?
Ultimately, what I would like to do is being able to adjust the size defined in the layout so I can dynamically change the value defined by layout_height depending on the resolution of the screen resolution and the pixel density.
Is the only way to do so during the onLayout callback?
It would be much easier to redefined the XML on onCreate.
Comments, advices would be much appreciated.
Thanks
JY
Acces the View's Layout and then access the 'height' field: View.getLayoutParams().height
Should have searched a tad further prior to post a question.
This does the trick it seems...
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
factor_y = (dm.ydpi * dm.heightPixels) / (160 * 480);
factor_x = (dm.xdpi * dm.widthPixels) / (160 * 320);
LinearLayout layout = (LinearLayout) findViewById(R.id.mainlayout);
for (int i = 0; i < layout.getChildCount(); i++)
{
View v = layout.getChildAt(i);
int height = v.getLayoutParams().height;
if (height > 0)
{
v.getLayoutParams().height = (int) ((float) height * factor_y);
}
int width = v.getLayoutParams().width;
if (width > 0)
{
v.getLayoutParams().width = (int) ((float) width * factor_x);
}
}