I have an imageview and i want to scale it with it's frame!
I used imageView.setScaleX() but this method only scales image within and image view X, Y, width and Height are the same.
What must i do to resize imageview with image?
You can add the property scaleType to the ImageView XML like this:
android:scaleType="fitXY"
More info:
https://robots.thoughtbot.com/android-imageview-scaletype-a-visual-guide
https://developer.android.com/reference/android/widget/ImageView.ScaleType.html
if you want to height change automatically when width changed, use this custom ImageView.
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import java.lang.reflect.Field;
public class AspectRatioImageView extends ImageView {
public AspectRatioImageView(Context context) {
super(context);
}
public AspectRatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AspectRatioImageView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = width * getDrawable().getIntrinsicHeight()
/ getDrawable().getIntrinsicWidth();
int maxHeight;
try {
Field f = ImageView.class.getDeclaredField("mMaxHeight");
f.setAccessible(true);
maxHeight = (Integer) f.get(this);
} catch (NoSuchFieldException e) {
maxHeight = Integer.MAX_VALUE;
}
setMeasuredDimension(width, height > maxHeight ? maxHeight : height);
} catch (Exception e) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Thanks to #saeid . I've replaced setScale with setWidth and solved my problem.
Related
I have created a custom subclass of AppCompatButton called SquareButton, which forces a button to be square. The code for that subclass was found here: https://stackoverflow.com/a/36991823/7648952.
This button works fine and displays when the layout it's contained in is inflated outside of a RecyclerView, however when used with a RecyclerView the button does not display. When I change my layout and code to use a normal Button, the Button displays, so there doesn't seem to be anything wrong with the way I'm using RecyclerView. I have no idea why this might be.
SquareButton.java:
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v7.widget.AppCompatButton;
import android.util.AttributeSet;
public class SquareButton extends AppCompatButton {
public SquareButton(Context context) {
super(context);
}
public SquareButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = width > height ? height : width;
setMeasuredDimension(size, size);
}
}
Screenshot of the SquareButton working when inflated outside of a RecyclerView:
Screenshot of the SquareButton not displaying inside of RecyclerView:
Screenshot of a regular Button working inside of RecyclerView:
It seems to me that this behavior is odd. Any help would be much appreciated.
Try this code block:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(width > height){
setMeasuredDimension(getMeasuredHeight(), getMeasuredHeight());
}else {
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
}
In this usage, you will set widthMeasureSpec or heightMeasureSpec instead of direct width or height value.
I want my ImageView to have 16:9 ratio and android:layout_width="match_parent".
I cannot find out how to set it in XML file. I know there is a PercentRelativeLayout that can help. But it supports API 23 and I don't want to use it.
I know I can set the ratio programmatically. But it's not good. Because when the screen is rotated I have to set the size of ImageView again.
Is there any way to do it with XML file?
To Create Percentage Ratio ImageView needs to Override the onMeasure method in ImageView.
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class PercentageImageView extends ImageView {
public PercentageImageView(Context context) {
super(context);
}
public PercentageImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public PercentageImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public PercentageImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PercentageImageView, 0, 0);
try {
heightRatio = a.getFloat(R.styleable.PercentageImageView_imageHeightRatio, 0f);
} finally {
a.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (heightRatio != 0) {
height = (int) (heightRatio * width);
}
setMeasuredDimension(width, height);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Inside values/attrs.xml
<resources>
<declare-styleable name="PercentageImageView">
<attr name="imageHeightRatio" format="float" />
</declare-styleable>
</resources>
Send ratio from the XML like below
<PercentageImageView
android:id="#+id/itemImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
app:imageHeightRatio="0.569" />// This is for 16:9 ratio.
Problem
I want to use RecyclerView to implement GridView.In fact I need to display all local photos in RecyclerView which has 3 columns.I know about GridLayoutManager
My code :
mManager = new GridLayoutManager(this,3);
And the item XML:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
however how can I set the item view (ImageView)'s width and height?
Solution
I define a custom view:
public class RatioImageView extends ImageView {
private int originalWidth;
private int originalHeight;
public RatioImageView(Context context) {
super(context);
}
public RatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOriginalSize(int originalWidth, int originalHeight) {
this.originalWidth = originalWidth;
this.originalHeight = originalHeight;
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (originalWidth > 0 && originalHeight > 0) {
float ratio = (float) originalWidth / (float) originalHeight;
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (width > 0) {
height = (int) ((float) width / ratio);
}
setMeasuredDimension(width, height);
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Is there another solution?
Instead of creating Custom ImageView when you are creating your view inside onCreateViewHolder() method by inflating your recycler item view, you can set width and height to your ImageView using layoutParamas.
If you want 3 columns then you can get screen width and divide it into 3 and pass that to your RecyclerView Adapter and use it in onCreateViewHolder() to apply.
I am trying to create a textview that will always be squared so I have implemented this custom class.
public class SquareTextView extends TextView {
public SquareTextView(Context context) {
super(context);
}
public SquareTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int max = Math.max(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(max, max);
}
}
Here is an example layout that illustrates the problem:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
<com.mypackage.SquareTextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="right|top"
android:background="#000"
android:gravity="center"
android:padding="4dp"
android:text="1"
android:textColor="#FFF"/>
</LinearLayout>
Here is an image of this
This works great in getting the view squared, however, it seems like the gravity gets messed up. With this, the text seems to always be in the top left corner. How can I have a TextView that will always be squared but still keep the gravity or at least be able to center the text?
EDIT: After some testing I have noticed that if you set the width or height to a specific dp size the gravity seems to be working again. So it probably has to do with the WRAP_CONTENT attribute. Will that be handled in another way in the onmeasure method that could cause my own method to not work as expected?
Hope you have already got the answer by now. If not, you can use this:
public class TextAlphaSquareTextView extends AppCompatTextView {
private int mTextAlpha = 0;
private boolean isSquare = false;
public TextAlphaSquareTextView(Context context) {
super(context);
init(null);
}
public TextAlphaSquareTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public TextAlphaSquareTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs == null) {
} else {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TextAlphaSquareTextView);
mTextAlpha = a.getInteger(R.styleable.TextAlphaSquareTextView_textAlpha, 100);
isSquare = a.getBoolean(R.styleable.TextAlphaSquareTextView_squareMode, false);
a.recycle();
if(mTextAlpha < 0 || mTextAlpha > 100)
throw new IllegalArgumentException("Alpha range should be b/w 0 to 100 (in percentage)");
else {
setAlphaOnTextColor();
}
}
setText(getText());
}
void setAlphaOnTextColor() {
int alpha = ((255 * mTextAlpha) / 100);
setTextColor(getTextColors().withAlpha(alpha));
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (isSquare) {
int width = this.getMeasuredWidth();
int height = this.getMeasuredHeight();
int size = Math.max(width, height);
int widthSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(widthSpec, heightSpec);
}
}
}
you need to call super.onMeasure() again with EXACT spec and the calculated size, since setMeasureDimension() seems to be ignoring the gravity.
I am trying to make a custom view that is square, using the width as the height. I am also using a pre-defined layout which I inflate as it's UI. As soon as I overrode onMeasure, the custom view no longer appears. Here is my code:
public class MyView extends RelativeLayout{
public MyView(Context context) {
super(context);
addView(setupLayout(context));
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
addView(setupLayout(context));
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
addView(setupLayout(context));
}
private View setupLayout(Context context) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View myView = inflater.inflate(R.layout.view_layout, null);
return myView;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(widthMeasureSpec));
}
}
I have 2 questions:
How do I override onMeasure so that it draws my view the way I am expecting it to?
Is there any way I can make this more efficient in terms of the view hierarchy (i.e. not be putting a RelativeLayout inside a RelativeLayout)
You can use this code from Jan Němec's answer to a similar question :
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SquareLayout extends LinearLayout {
public SquareLayout(Context context) {
super(context);
}
public SquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (width > (int)(mScale * height + 0.5)) {
width = (int)(mScale * height + 0.5);
} else {
height = (int)(width / mScale + 0.5);
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
}
}
Or try to use this library project.