ImageView doesn't wrap content in RelativeLayout - java

I have a TableLayout with 6 childs/entrys. These childs are a custom RelativeLayout. In each RelativeLayout is a big TextView in the middle and an ImageView and small TextView at the bottom.
The ImageView should be as tall as the TextView next to it. That's why I set the attribute ALIGN_TOP and ALIGN_BOTTOM to the TextView (you can see it in code below). This works very well and both - ImageView and TextView - have the same height now. But the problem is, that the left and right side of the ImageView don't "wrap content" anymore (as you can see on the screenshot).
Is there a way to fit the left and right side to the image and remove the "padding"?
Here is my code:
view_display_component.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="#+id/tvDisplayBig"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_weight="1"
android:gravity="center"
android:textColor="#color/white"
android:textSize="#dimen/font_size_extra_large" />
<ImageView
android:id="#+id/imageViewDisplayIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/tvDisplayBig"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:baselineAlignBottom="true"
android:scaleType="fitCenter"
android:src="#drawable/stopwatch_64"
android:visibility="visible" />
<TextView
android:id="#+id/tvDisplaySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom"
android:includeFontPadding="false"
android:textColor="#color/white"
android:textSize="#dimen/font_size_small" />
</merge>
class DisplayComponent which extends RelativLayout
public DisplayComponent(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_display_component, this, true);
tvDisplay = (TextView) getChildAt(0);
icon = (ImageView) getChildAt(1);
tvName = (TextView) getChildAt(2);
setupAlign();
}
private void setupAlign() {
if(index % 2 == 0) { // LEFT SIDE
// same as "RIGHT SIDE"
} else { // RIGHT SIDE
RelativeLayout.LayoutParams paramsIcon = (RelativeLayout.LayoutParams) icon.getLayoutParams();
paramsIcon.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
paramsIcon.addRule(RelativeLayout.ALIGN_TOP, tvName.getId());
paramsIcon.addRule(RelativeLayout.ALIGN_BOTTOM, tvName.getId());
icon.setLayoutParams(paramsIcon);
RelativeLayout.LayoutParams paramsTvName = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
paramsTvName.addRule(RelativeLayout.RIGHT_OF, icon.getId());
tvName.setLayoutParams(paramsTvName);
tvName.setBackgroundColor(Color.BLUE); // only for testing
icon.setBackgroundColor(Color.YELLOW);
}

I found an (ugly) solution. Because my icon is square, I created a custom ImageView and overrode the onSizeChanged() method like this:
public class IconImageView extends ImageView {
public IconImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(h != oldh && h > 0)
getLayoutParams().width = h; // same width as height
}
}
But this works only if the image is square. That's why I am still searching for a better solution. Maybe some layout solution with a better setting of alignment.
Best regards!

Related

Problem with inflating xml-Layout on custom layout class

I have a custom ConstraintLayout class (Card.java) which overrides the onDraw() method to draw a hexagon in his background. On the foreground i try to have three TextViews to display three numbers.
For this I inflate a card.xml in the constructor of Card. The TextViews are displayed, but not at the right position. They should match the width and height of the Card and then position itself to the top-left and top-right corner and one to the bottom of the Card. But they do something like shrink itself and go to the top-left corner.
I have tried to change the root element of card.xml to "merge" instead of "...ConstraintLayout" but this doesn't change anything.
I also tried to use Guidelines to position the TextViews relative to its width. I try to prevent the use of fixed margins, so the Text is always at the right place, also when the size of the Card changes.
Card.java:
public class Card extends ConstraintLayout {
private int mSize;
private Path mHexagon;
private Paint mPaintHexagon;
private TextView mT1, mT2, mT3;
public Card(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.card, this);
// Numbers
mT1 = findViewById(R.id.num1);
mT2 = findViewById(R.id.num2);
mT3 = findViewById(R.id.num3);
// Hexagon
mSize = Field.getHexSize(); // Size is used to calculate the
setPath();
mPaintHexagon = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintHexagon.setColor(0x50888888);
mPaintHexagon.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mHexagon, mPaintHexagon);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = 2 * mSize;
int height = (int) (Math.sqrt(3) * mSize);
Log.d(TAG, "Measure w:" + width + " h:" + height);
setMeasuredDimension(width, height);
}
}
card.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num2"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="2"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num1"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="1"
app:layout_constraintStart_toEndOf="#+id/num2"
app:layout_constraintEnd_toStartOf="#+id/num3"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num3"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="3"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"/>
</android.support.constraint.ConstraintLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
android:background="#color/colorAccentDark"
android:padding="5dp">
<de.portugall.markus.takeiteasy.Card
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="#+id/card"/>
</android.support.constraint.ConstraintLayout>
Screenshot Card in Layout-Debug mode
'onMeasure()` has some rules that you are not strictly following. I have seen these rules broken with impunity, but I think that you are being caught, but we will push on.
In onMeasure() you are setting the height and width of the custom layout but ConstraintLayout still understands the layout as wrap_content. You will need to set the layout params to the new height and width. Add the following code to the end of onMeasure():
// Although we have measured the layout, we need to tell ConstraintLayout in the
// LayoutParams that the size is not longer "wrap_content".
ViewGroup.LayoutParams lp = getLayoutParams();
lp.width = width;
lp.height = height;
setLayoutParams(lp);
The second issue that you have is that you are adding a ConstraintLayout (card.xml) to a ConstraintLayout (your custom layout), but you are not setting the constraints. In the constructor for Card.java, add the following to set the constraints:
ConstraintLayout layout = (ConstraintLayout) inflate(context, R.layout.card, this);
// We have added R.layout.card to a ConstraintLayout (this custom layout), so we need
// to make sure that it is constrained properly.
ConstraintSet cs = new ConstraintSet();
cs.clone(layout);
cs.connect(R.id.layout, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
cs.connect(R.id.layout, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
cs.connect(R.id.layout, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END);
cs.connect(R.id.layout, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM);
cs.applyTo(layout);
You will need to change the height and width of the layout in card.xml to 0dp. match_parent is never appropriate in ConstraintLayout.
This is a pictorial description of what is happening:
On a related note, you should consider using the merge facility to avoid nested ConstraintLayouts as other have mentioned.

Android TextView Align text to Left and Right

I'm trying to have a TextView that has two texts, one aligned at the left side of the TextView and one at the right side of the TextView.
I referred this [Android TextView Align text to Right and Left (#daemontus) for setting the text.
But how do add a text if I enter the text at right side of the TextView first and then left side of the TextView
1) Input: Enter Button 1 to display Text1
2) Output: Enter Button 2 to display Text2 along with Text1
public void setLeftRightText(TextView view, String left, String right,Enum keysel) {
if(keysel == RSK_KEY) {
SpannableString merged=new SpannableString(left + "\n" + right);
merged.setSpan(
new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
0, left.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
merged.setSpan(
new LineOverlapSpan(),
left.length(), left.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
merged.setSpan(
new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE),
left.length() + 1, left.length() + 1 + right.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
view.setText(merged);
}
else if (keysel == LSK_KEY){
final String resultText = right + " " + left;
final SpannableString styledResultText = new SpannableString(resultText);
styledResultText.setSpan((new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE )), left.length() + 2, left.length() + 2 +right.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
styledResultText.setSpan((new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL )), 0, left.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
view.setText(styledResultText);
}
}
LineOverlapSpan.java
public class LineOverlapSpan implements LineHeightSpan {
public void chooseHeight(final CharSequence text, final int start, final int end, final int spanstartv, final int v, final Paint.FontMetricsInt fm) {
fm.bottom += fm.top;
fm.descent += fm.top;
}
}
You can achieve the desired result in a single text view. Create a view that extends text view and override the onDraw
I've put together a small example. Hope it will give you an idea at-least
public class LeftRightTextView extends AppCompatTextView {
private TextPaint mTextPaint;
private String mLeftText, mRightText;
public LeftRightTextView(Context context) {
this(context, null);
}
public LeftRightTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public void setLeftText(String leftText) {
mLeftText = leftText;
setText(mLeftText);
}
public void setRightText(String rightText) {
mRightText = rightText;
invalidate();
}
public LeftRightTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LeftRightTextView, defStyleAttr, 0);
mLeftText = typedArray.getString(R.styleable.LeftRightTextView_lr_left_text);
mRightText = typedArray.getString(R.styleable.LeftRightTextView_lr_right_text);
typedArray.recycle();
setText(mLeftText);
mTextPaint = new TextPaint();
mTextPaint.setColor(getCurrentTextColor());
}
#Override
protected void onDraw(Canvas canvas) {
//let it draw the left text as usual
super.onDraw(canvas);
//Now draw the right text
int rightEnd = getWidth() - getPaddingRight();
float textWidth = mTextPaint.measureText(mRightText);
canvas.drawText(mRightText, rightEnd - textWidth, mTextPaint.getFontMetrics().descent - mTextPaint.getFontMetrics().ascent, mTextPaint);
}
}
And the attributes
<declare-styleable name="LeftRightTextView">
<attr name="lr_left_text" format="string"/>
<attr name="lr_right_text" format="string"/>
</declare-styleable>
In layout
<me.srs.myapplication.LeftRightTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:lr_left_text="Hello"
app:lr_right_text="World"/>
And this is the result
You need to take two separate textviews to achieve your requirements.
Please use following code and check.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#android:color/black"
android:padding="10dp"
android:weightSum="1">
<TextView
android:text="Left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:textSize="36sp"
android:fontFamily="sans-serif-light"
android:textColor="#android:color/white"
android:background="#android:color/transparent"
android:gravity="left"
android:padding="5dp"/>
<TextView
android:text="Right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:textSize="36sp"
android:fontFamily="sans-serif-light"
android:textColor="#android:color/white"
android:background="#android:color/transparent"
android:gravity="right"
android:padding="5dp"/>
</LinearLayout>
You need to create a custom view and override onDraw method of same , to achieve desired functionality.

Load Image With Glide and Change Aspect Ratio

I have an imageview and I would like to load an image with Glide into it. The thing here is, I want all the images have the same height (width match_parent) according to the 16:9 aspect ratio.
<ImageView
android:id="#+id/thumbImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="#string/thumbnail_text"
android:scaleType="fitCenter"
android:src="#drawable/thumbnail_default"
android:cropToPadding="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/thumbTitle"/>
Glide code
Glide.with(mThumb.getContext())
.load(getThumbUrl())
.into(mThumb);
When I load an image with aspect ratio 16:9, everything is great. However, when I load another image, the height adjusts to the height of the image.
I tried adding Constraint layout dimension ratio
app:layout_constraintDimensionRatio="16:9"
I tried playing around with adjustViewBounds and scaleType but no success.
So I think I should play with Glide to adjust the bitmap before loading it to the imageview but I couldn't find a tutorial about that.
How can I show image with width match_parent and height calculated as aspect ratio 16:9?
Thank you,
Note: I tried
<ImageView
android:id="#+id/thumbImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:contentDescription="#string/thumbnail_text"
android:scaleType="centerCrop"
android:src="#drawable/thumbnail_default"
android:cropToPadding="true"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/thumbTitle"/>
It works, but then if the orientation is changed, the height goes 0.
One way around is to use a Image view with 16:9 ratio .
public class CustomImageView extends AppCompatImageView {
public CustomImageView(Context context) {
super(context);
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height=(width * 9) / 16;
setMeasuredDimension(width, height);
}
}
This will make a ImageView with hard code ratio of 16/9 . You can use Custom attributes for it to make it more flexible.
Update:- Now with the ConstraintsLayout its easy to break views in ratio . U can try following.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/expandableModes"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Another way is to use a SimpleTarget<> as describe in this post.
Then you can resize the image based on the current image's size.
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>{
#Override
public void onResourceReady(Bitmap resource,GlideAnimation<? extends Bitmap>(){
// resize the bitmap
bitmap = resize(width,height);
// set the image to the imageView
imageView.setImageBitmap(bitmap);
}
})

How to make square imageButton?

Is there a file xml in the android studio:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<imageButton
android:id="#+id/imageButton1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent"
android:scaleType="fitXY" />
<imageButton
android:id="#+id/imageButton2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent"
android:scaleType="fitXY" />
</LinearLayout>
I need to have the full width of 2 square buttons, how can this be done?
You can make your custom view here by Extending ImageButton View.In onMeasure(), you can set the height of the ImageButton equal to its width. you can use this class in your XML as well.
Here is the snippet:-
public class SquareImageButton extends ImageButton {
public SquareImageButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width=getMeasuredWidth();
setMeasuredDimension(width,width);
}
}
Well firstly you'll need a square image to put into the imageButton. Then you'll set AdjustViewBounds to true. and then set the scaletype to FitXY. I use linear layouts and use the weights and views to adjust the size and position. Good for scaling and the imageButton will square. This is under my ImageButton in the xml:
android:scaleType="fitXY"
android:layout_weight="1"
android:adjustViewBounds="true"
1. Programmatically get the width of the display.
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
2. Finally, use this width value to make Square Button by setting its new LayoutParams(width, width).
ImageButton imageButton1 = (ImageButton) findViewById(R.id.imageButton1);
ImageButton imageButton2 = (ImageButton) findViewById(R.id.imageButton2);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, width);
imageButton1.setLayoutParams(params);
imageButton2.setLayoutParams(params);
3. Use ScrollView as a container of your LinearLayout to make your layout scroll-able to show both button on UI and update XML as below:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_red_light"
android:scaleType="fitXY" />
<ImageButton
android:id="#+id/imageButton2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_light"
android:scaleType="fitXY" />
</LinearLayout>
</ScrollView>
OUTPUT:
FYI, ImageButton1 is RED colored and ImageButton2 is BLUE colored. Both are in Square shape.
Hope this will help~

Android. Center text in TextView vertically

I have a ViewGroup class which adds a custom View and draws a pie chart on a canvas. In the center of the circle i have a TextView. But whatever i do, i cannot get the text in the TextView centered vertically.
The Root view in this case is a LinearLayout.
Snippet:
public class PieChart extends ViewGroup {
public PieChart(Context context) {
super(context);
mPieView = new PieView(getContext());
addView(mPieView);
}
...
private class PieView extends View {
public PieView(Context context) {
super(context);
text = new TextView(getContext());
text.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.FILL_PARENT));
text.setHeight(400);
text.setText(getCenterTextValue());
text.setTextColor(Color.BLACK);
text.setGravity(Gravity.CENTER);
text.setBackgroundColor(Color.GRAY);
addView(text);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
text.layout((int)mBoundsInner.left, (int)mBoundsInner.top, (int)mBoundsInner.right, (int)mBoundsInner.bottom);
}
}
...
}
This is how it looks like in the emulator. I have set a grey background for demonstration purposes so that you can see the bounds of the TextView
Picture of how it looks like in android emulator
Here is the activity.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ls="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:orientation="vertical" >
<com.lifescraper.view.PieChart
android:id="#+id/Pie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:width="0dip">
</com.lifescraper.view.PieChart>
</LinearLayout>

Categories

Resources