Where'd padding go, when setting background Drawable? - android

I have this issue on my EditText and Button views, where I have a nice padding for them to space away from the text, but when I change the background with setBackgroundDrawable or setBackgroundResource that padding is lost forever.

What I found was adding a 9 patch as a background resource reset the padding - although interestingly if I added a color, or non-9 patch image, it didn't. The solution was to save the padding values before the background gets added, then set them again afterwards.
private EditText value = (EditText) findViewById(R.id.value);
int pL = value.getPaddingLeft();
int pT = value.getPaddingTop();
int pR = value.getPaddingRight();
int pB = value.getPaddingBottom();
value.setBackgroundResource(R.drawable.bkg);
value.setPadding(pL, pT, pR, pB);

I was able to wrap the element inside another layout, in this case, a FrameLayout. That enabled me to change the background on the FrameLayout without destroying the padding, which is on the contained RelativeLayout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/commentCell"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/comment_cell_bg_single" >
<RelativeLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp" >
<ImageView android:id="#+id/sourcePic"
android:layout_height="75dp"
android:layout_width="75dp"
android:padding="5dp"
android:background="#drawable/photoframe"
/>
...
The other option is to set it programmatically after setting the background Drawable as suggested above. Just make sure to calculate the pixels to correct for the resolution of the device.

I had this problem in a TextView, so I subclassed TextView and made an Override method of the TextView.setBackgroundResource(int resid) method. Like this:
#Override
public void setBackgroundResource(int resid) {
int pl = getPaddingLeft();
int pt = getPaddingTop();
int pr = getPaddingRight();
int pb = getPaddingBottom();
super.setBackgroundResource(resid);
this.setPadding(pl, pt, pr, pb);
}
This way, it gets the padding of the item before it sets the resource, but doesn't actually mess with the original functionality of the method, other than keeping the padding.

Haven't tested this super thoroughly, but this method might be of use:
/**
* Sets the background for a view while preserving its current padding. If the background drawable
* has its own padding, that padding will be added to the current padding.
*
* #param view View to receive the new background.
* #param backgroundDrawable Drawable to set as new background.
*/
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {
Rect drawablePadding = new Rect();
backgroundDrawable.getPadding(drawablePadding);
int top = view.getPaddingTop() + drawablePadding.top;
int left = view.getPaddingLeft() + drawablePadding.left;
int right = view.getPaddingRight() + drawablePadding.right;
int bottom = view.getPaddingBottom() + drawablePadding.bottom;
view.setBackgroundDrawable(backgroundDrawable);
view.setPadding(left, top, right, bottom);
}
Use this instead of view.setBackgroundDrawable(Drawable).

Just to explain what's happening :
It's actually a feature. Layout drawables you might use as backgrounds can define a padding this way :
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingRight="8dp"
>
...
</layer-list>
This padding will be set along with the new drawable background. When there's no padding, the default value is 0.
More info from an email written by Romain Guy:
https://www.mail-archive.com/android-developers#googlegroups.com/msg09595.html

Backward compatable version of cottonBallPaws's answer
/**
* Sets the background for a view while preserving its current padding. If the background drawable
* has its own padding, that padding will be added to the current padding.
*
* #param view View to receive the new background.
* #param backgroundDrawable Drawable to set as new background.
*/
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#SuppressWarnings("deprecation")
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {
Rect drawablePadding = new Rect();
backgroundDrawable.getPadding(drawablePadding);
int top = view.getPaddingTop() + drawablePadding.top;
int left = view.getPaddingLeft() + drawablePadding.left;
int right = view.getPaddingRight() + drawablePadding.right;
int bottom = view.getPaddingBottom() + drawablePadding.bottom;
int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
view.setBackgroundDrawable(backgroundDrawable);
} else {
view.setBackground(backgroundDrawable);
}
view.setPadding(left, top, right, bottom);
}

You can give some padding by using 9-patch images and defining the content area in the drawable.
Check this
You can also set the padding in your layout from xml or programatically
xml padding tags
android:padding
android:paddingLeft
android:paddingRight
android:paddingTop
android:paddingBottom
You can try setting the padding manually from the code after you call the setBackgroundDrawable by calling setPadding on your EditText or Button Views

For common searcher,
just add setPadding after setBackgroudDrawable. When you change your drawable, you have to call setPadding again.
Like:
view.setBackgroundDrawable(backgroundDrawable);
view.setPadding(x, x, x, x);
The cleanest way is define your paddings inside a xml-drawable which points to the drawable-image-file
Greatings

My solution was to extend the view (in my case an EditText) and override setBackgroundDrawable() and setBackgroundResource() methods:
// Stores padding to avoid padding removed on background change issue
public void storePadding(){
mPaddingLeft = getPaddingLeft();
mPaddingBottom = getPaddingTop();
mPaddingRight = getPaddingRight();
mPaddingTop = getPaddingBottom();
}
// Restores padding to avoid padding removed on background change issue
private void restorePadding() {
this.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
}
#Override
public void setBackgroundResource(#DrawableRes int resId) {
storePadding();
super.setBackgroundResource(resId);
restorePadding();
}
#Override
public void setBackgroundDrawable(Drawable background) {
storePadding();
super.setBackgroundDrawable(background);
restorePadding();
}

Combining the solutions of all, I wrote one in Kotlin:
fun View.setViewBackgroundWithoutResettingPadding(#DrawableRes backgroundResId: Int) {
val paddingBottom = this.paddingBottom
val paddingStart = ViewCompat.getPaddingStart(this)
val paddingEnd = ViewCompat.getPaddingEnd(this)
val paddingTop = this.paddingTop
setBackgroundResource(backgroundResId)
ViewCompat.setPaddingRelative(this, paddingStart, paddingTop, paddingEnd, paddingBottom)
}
fun View.setViewBackgroundWithoutResettingPadding(background: Drawable?) {
val paddingBottom = this.paddingBottom
val paddingStart = ViewCompat.getPaddingStart(this)
val paddingEnd = ViewCompat.getPaddingEnd(this)
val paddingTop = this.paddingTop
ViewCompat.setBackground(this, background)
ViewCompat.setPaddingRelative(this, paddingStart, paddingTop, paddingEnd, paddingBottom)
}

just change lib to v7:22.1.0 in android studio like this
compile 'com.android.support:appcompat-v7:22.1.0'

Most of answers are correct but should handle the background setting correctly.
First get the padding of your view
//Here my view has the same padding in all directions so I need to get just 1 padding
int padding = myView.getPaddingTop();
Then set the background
//If your are supporting lower OS versions make sure to verify the version
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
//getDrawable was deprecated so use ContextCompat
myView.setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.bg_accent_underlined_white));
} else {
myView.setBackground(ContextCompat.getDrawable(context, R.drawable.bg_accent_underlined_white));
}
Then set the padding the view had before the background change
myView.setPadding(padding, padding, padding, padding);

I found another solution.
I was facing the similar problem with Buttons.
Eventually, i added:
android:scaleX= "0.85"
android:scaleY= "0.85"
it worked for me. The default padding is almost the same.

In my case I had a drawable and was so stupid I didn't see the paddings were all set to 0 in the xml.

maybe it will be relevant for someone
small hack using drawable in selector doesn't remove padding
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/anything" />
</selector>

Here an improved version of cottonBallPaws' setBackgroundAndKeepPadding. This maintains the padding even if you call the method multiple times:
/**
* Sets the background for a view while preserving its current padding. If the background drawable
* has its own padding, that padding will be added to the current padding.
*/
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {
Rect drawablePadding = new Rect();
backgroundDrawable.getPadding(drawablePadding);
// Add background padding to view padding and subtract any previous background padding
Rect prevBackgroundPadding = (Rect) view.getTag(R.id.prev_background_padding);
int left = view.getPaddingLeft() + drawablePadding.left -
(prevBackgroundPadding == null ? 0 : prevBackgroundPadding.left);
int top = view.getPaddingTop() + drawablePadding.top -
(prevBackgroundPadding == null ? 0 : prevBackgroundPadding.top);
int right = view.getPaddingRight() + drawablePadding.right -
(prevBackgroundPadding == null ? 0 : prevBackgroundPadding.right);
int bottom = view.getPaddingBottom() + drawablePadding.bottom -
(prevBackgroundPadding == null ? 0 : prevBackgroundPadding.bottom);
view.setTag(R.id.prev_background_padding, drawablePadding);
view.setBackgroundDrawable(backgroundDrawable);
view.setPadding(left, top, right, bottom);
}
You need to define a resource id via values/ids.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="prev_background_padding" type="id"/>
</resources>

I use this pretty easy workaround I define a accentColor in my style.xml like below
<item name="colorAccent">#0288D1</item>
and then I use the either of following styles in my Button tags
style="#style/Base.Widget.AppCompat.Button.Colored"
style="#style/Base.Widget.AppCompat.Button.Small"
for Example :
<Button
android:id="#+id/btnLink"
style="#style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/tvDescription"
android:textColor="#color/textColorPrimary"
android:text="Visit Website" />
<Button
android:id="#+id/btnSave"
style="#style/Base.Widget.AppCompat.Button.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/tvDescription"
android:layout_toRightOf="#id/btnLink"
android:textColor="#color/textColorPrimaryInverse"
android:text="Save" />

Related

How can I set the top padding of a ListView programmatically? [duplicate]

I am developing Android v2.2 app.
I have a Fragment. In the onCreateView(...) callback of my fragment class, I inflate an layout to the fragment like below:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.login, null);
return view;
}
The above inflated layout file is (login.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Username" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Username" />
</LinearLayout>
I would like to set a paddingTop to the above <LinearLayout> element , and I want to do it in the Java code instead of do it in xml.
How to set paddingTop to <LinearLayout> in my fragment Java class code ??
view.setPadding(0,padding,0,0);
This will set the top padding to padding-pixels.
If you want to set it in dp instead, you can do a conversion:
float scale = getResources().getDisplayMetrics().density;
int dpAsPixels = (int) (sizeInDp*scale + 0.5f);
To answer your second question:
view.setPadding(0,padding,0,0);
like SpK and Jave suggested, will set the padding in pixels. You can set it in dp by calculating the dp value as follows:
int paddingDp = 25;
float density = context.getResources().getDisplayMetrics().density
int paddingPixel = (int)(paddingDp * density);
view.setPadding(0,paddingPixel,0,0);
If you store the padding in resource files, you can simply call
int padding = getResources().getDimensionPixelOffset(R.dimen.padding);
It does the conversion for you.
Using Kotlin and the android-ktx library, you can simply do
view.updatePadding(top = 42)
See docs here
You can set padding to your view by pro grammatically throughout below code -
view.setPadding(0,1,20,3);
And, also there are different type of padding available -
Padding
PaddingBottom
PaddingLeft
PaddingRight
PaddingTop
These, links will refer Android Developers site. Hope this helps you lot.
Using TypedValue is a much cleaner way of converting to pixels compared to manually calculating:
float paddingDp = 10f;
// Convert to pixels
int paddingPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingDp, context.getResources().getDisplayMetrics());
view.setPadding(paddingPx, paddingPx, paddingPx, paddingPx);
Essentially, TypedValue.applyDimension converts the desired padding into pixels appropriately depending on the current device's display properties.
For more info see: TypedValue.applyDimension Docs.
Kotlin; extension function
fun Float.px(m: DisplayMetrics!): Int
get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, m).toInt()
...
val pad = 10.0f.px
use below method for setting padding dynamically
setPadding(int left, int top, int right, int bottom)
Example :
view.setPadding(2,2,2,2);
Here you can see in which section the padding is applied
bidding.subHeader.tvSubHeader.setPadding(0, 5, 0, 0);
Someone edited this answer, but I added an image that had been removed before, here it is again
Step 1: First, take the padding value as an integer.
int padding = getResources().getDimensionPixelOffset(R.dimen.padding);
or int padding = 16; [Use any method]
Step 2: Then assign the padding value to the layout.
layout.setPadding(padding, padding, padding, padding);
layout.setPadding(padding_left, padding_top, padding_right, padding_bottom);
All side different padding can be assigned. layout.setPadding(16, 10, 8, 12);
For removing padding (No Padding) set padding values as 0,
layout.setPadding(0, 0, 0, 0);
Write Following Code to set padding, it may help you.
TextView ApplyPaddingTextView = (TextView)findViewById(R.id.textView1);
final LayoutParams layoutparams = (RelativeLayout.LayoutParams) ApplyPaddingTextView.getLayoutParams();
layoutparams.setPadding(50,50,50,50);
ApplyPaddingTextView.setLayoutParams(layoutparams);
Use LinearLayout.LayoutParams or RelativeLayout.LayoutParams according to parent layout of the child view
Context contect=MainActivity.this;
TextView tview=new TextView(context);
tview.setPaddingRelative(10,0,0,0);
The best way is not to write your own funcion.
Let me explain the motivaion - please lookup the official Android source code.
In TypedValue.java we have:
public static int complexToDimensionPixelSize(int data,
DisplayMetrics metrics)
{
final float value = complexToFloat(data);
final float f = applyDimension(
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
value,
metrics);
final int res = (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
if (res != 0) return res;
if (value == 0) return 0;
if (value > 0) return 1;
return -1;
}
and:
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
As you can see, DisplayMetrics metrics can differ, which means it would yield different values across Android-OS powered devices.
I strongly recommend putting your dp padding in dimen xml file and use the official Android conversions to have consistent behaviour with regard to how Android framework works.
Using Jave's solution.
public static int getResourceDimension(Context context, String name, String defType, String defPackage) {
int sizeInDp = 0;
int resourceId = context.getResources().getIdentifier(name, defType, defPackage);
if (resourceId > 0) {
sizeInDp = context.getResources().getDimensionPixelSize(resourceId);
}
float scale = context.getResources().getDisplayMetrics().density;
int dpAsPixels = (int) (sizeInDp*scale + 0.5f);
return dpAsPixels;
}
then call when needed.
int statusBarHeight = getResourceDimension(getContext(), "status_bar_height",
"dimen", "android");
statusBarHeight = (int) (statusBarHeight + getResources().getDimension(R.dimen.fragment_vertical_padding));
view.setPadding(0, statusBarHeight, 0, 0);
While padding programmatically, convert to density related values by converting pixel to Dp.
binding.appBarMain.toolbar.setOnApplyWindowInsetsListener { _, insets ->
val statusBarSize: Int =
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars()).top
} else {
insets.systemWindowInsetTop
}
binding.appBarMain.appBarLayout.setPadding(0, statusBarSize, 0, 0)
return#setOnApplyWindowInsetsListener insets
}

How to know what size a TextView WOULD be with certain string

Maybe I'm not entering the right keywords, but I'm not finding an answer. I want to know what the dimensions of a TextView would be if I were set it with a certain string. However, I want to know before everything gets laid out in the activity.
My TextView has a fixed width and a variable height. I can get the height like this:
myTextView.setText(myString);
// ... UI gets laid out ...
myTextView.getHeight()
I want to change the width of the TextView if the height gets past a certain point. (But not before then.) And rather than waiting until after the UI gets laid out, I want to know beforehand what the height would be if it had myString and then change the width if I needed to.
I looked at the Layout class but I couldn't figure out what to do. I wonder if it might have something to do with overriding the TextView's onMeasure but I really don't know how to attempt that. Any help is appreciated.
Update
Thanks to both #user3249477 and #0xDEADC0DE for their answers. I'm marking #user3249477's answer as the solution for now (although since I need multiple resizes of the view I'm not sure about repeatedly turning the visibility on and off) but also +1 to #0xDEADC0DE for giving me the keywords I needed to further look into this problem.
I need to do more research and testing on this. Here are some links that I have found helpful so far:
OnLayoutChangeListener:
View.OnLayoutChangeListener
Capture Layout resize before API 11
After changing a property on a LayoutParams object, do I need to call setLayoutParams again?
measureText() and getTextBounds():
Android Paint: .measureText() vs .getTextBounds()
Paint.getTextBounds() returns to big height
Gettextbounds in android
Overriding onSizeChanged of the parent view also looks intriguing: https://stackoverflow.com/a/14399163/3681880
Set your TextView to invisible:
android:visibility="invisible"
and measure it. Once you're done set it to visible:
TextView myTextView = (TextView) findViewById(R.id.text);
final int maxHeight = 500;
myTextView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
Log.e("TAG", "H: " + v.getHeight() + " W: " + v.getWidth());
if (v.getWidth() > maxHeight) {
params.width += 100;
v.setLayoutParams(params);
}
v.setVisibility(View.VISIBLE);
}
});
You could do it without overriding. If you get the TextViews Paint with getPaint(), you can use measureText(string) the get the minimal with of the TextView when it is drawn with that Paint. I looks like this:
TextView textView = new TextView(this);
float textWidth = textView.getPaint().measureText("Some Text");
Update
To get the height, you can call getTextBounds() on the Paint object like this:
String text = "Some Text";
Rect textBounds = new Rect();
textView.getPaint().getTextBounds(text, 0, text.length(), textBounds);
float height = textBounds.height();
float width = textBounds.width();

Ellipsize not working properly for a multiline TextView with an arbitrary maximum height

I have a TextView with an unknown maximum height, which is dependent on the device's DPI/screen resolution. So, for instance, on and MDPI device this maximum height makes it possible to show only 2 lines at a time, a value that can be increased up to an undefined number.
My issue is related with the ellipsize functionality. Let's suppose that a certain device allows for 4 lines to be displayed. If I manually set the maximum number of lines, like this...
<TextView
android:id="#+id/some_id"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:ellipsize="end"
android:maxLines="4"
android:singleLine="false"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="This is some really really really really really long text"
android:textSize="15sp" />
...everything works OK. If the text doesn't fit properly, then the ellipsis are added at the end of the fourth line, like this:
This is some
really really
really really
really long...
But I'd rather not set the number of lines as a static variable, as I would prefer to include support for any combination of DPI/screen resolution. So if I remove maxLines the ellipsis is no longer correctly shown at line four, showing instead an incomplete portion of text:
This is some
really really
really really
really long
If I slightly increase the TextView size, I can see that the rest of the text is still being drawn "behind" the other Views. Setting the variable maxHeight doesn't seem to work either.
I really can't seem to find a solution for this issue. Any ideas? If it helps, I'm working only with Android v4.0.3 and up (API level 15).
Calculate how many lines fit into the TextView with TextView#getHeight() and TextView#getLineHeight(). Then call TextView#setMaxLines().
ViewTreeObserver observer = textView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int maxLines = (int) textView.getHeight()
/ textView.getLineHeight();
textView.setMaxLines(maxLines);
textView.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
});
The accepted answer works well up to API 27. However, since API 28, if the line height was not set (by one of the follow methods), by default Android adds extra spacing between lines, but not after the last line.
Set attribute android:lineHeight=... (documentation) in your layout XML
Calls textView.setLineHeight(...) in your source code.
To find out the new line height for API 28 and above, I used textView.getLineBounds().
Kotlin
val observer = textView?.viewTreeObserver
observer?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
textView?.let { view ->
val lineHeight: Int
lineHeight = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val bounds = Rect()
textView.getLineBounds(0, bounds)
bounds.bottom - bounds.top
} else {
textView.lineHeight
}
val maxLines = textView.height / lineHeight
textView.maxLines = maxLines
textView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
Android Java
ViewTreeObserver observer = textView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int lineHeight = textView.getLineHeight();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Rect bounds = new Rect();
textView.getLineBounds(0, bounds);
lineHeight = bounds.bottom - bounds.top;
}
int maxLines = (int) textView.getHeight() / lineHeight;
textView.setMaxLines(maxLines);
textView.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
});

android ImageView setPadding has no effect

I am trying to set padding of an ImageView. My code is below
private void createEpisodeView() {
float scale = this.getResources().getDisplayMetrics().density;
int padding = (int) (PADDING * scale + 0.5f);
rlItemsRoot = (LinearLayout) findViewById(R.id.rl_items_root);
for (int i = 0; i < GameLevels.TOTAL_EPISODES; i++) {
ImageView iv = new ImageView(this);
iv.setPadding(padding, padding, padding, padding);
iv.setBackgroundResource(R.drawable.icon_small);
rlItemsRoot.addView(iv);
}
}
But it has no effect. but when I set this in XML it looks fine.
A you noticed yourself you are using
iv.setBackgroundResource(R.drawable.icon_small);
This will set the Background for the ImageView. The Background Image will fill the whole view because it is behind all the content in the View.
Use
iv.setImageResource(R.drawable.icon_small);
instead.
Rather than setting
iv.setBackgroundResource(R.drawable.icon_small);
set
iv.setImageResource(R.drawable.icon_small);
and problem solved :)
If any body knows the reason please reply.

Is it possible to scale drawableleft & drawableright in textview?

I have TextView with drawableLeft & drawableRight in List item.
The problem is, whenever the height of TextView is larger, drawableLeft & drawableLeft didn't automatically scale based on the height of the TextView.
Is it possible to scale the height of drawableLeft & drawableRight in TextView ?
(I was using 9 patch image)
This might help you out.
There are two properties scaleX and scaleY
The code below will scale down the image and the text with 30%.
Therefore you have to increase the font size with that many "sp", so that when it get re-sized (scaled) it would fit the "sp" you prefer.
Example. If I set the font to 18, then 30% out of 18 is 5.4sp, so roughly, this is the value I am targeting at, because when it gets scaled, it would become 13sp
<TextView
android:textSize="18sp"
android:scaleX="0.7"
android:scaleY="0.7"
The last thing to do is set the CompundDrawable.
tview.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.xxx), null, null, null);
Wrap your resource in a drawable that defines your desired size similar to:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="#drawable/icon"
android:width="#dimen/icon_size"
android:height="#dimen/icon_size" />
</layer-list >
After that, use this drawable in your textview tag
Hope this help
Textview tv = (TextView) findViewById(R.id.tv_dummy)
int imageResource = R.mipmap.ic_image;
Drawable drawable = ContextCompat.getDrawable(context, imageResource);
int pixelDrawableSize = (int)Math.round(tv.getLineHeight() * 0.7); // Or the percentage you like (0.8, 0.9, etc.)
drawable.setBounds(0, 0, pixelDrawableSize, pixelDrawableSize); // setBounds(int left, int top, int right, int bottom), in this case, drawable is a square image
tv.setCompoundDrawables(
null, //left
null, //top
drawable, //right
null //bottom
);
The only acceptable answer here should be to use an ImageView with the scaleTypes as per usual. Hacky work arounds to scale an image on a TextView that isn't supported by Android seems.. unnecessary. Use the SDK as it was intended.
I solved an equivalent usecase by introducing a ScaleDrawable and overriding its .getIntrisicHeight() so that it is at least the TextView height. The TextView.addOnLayoutChangeListener part, required to rebind the Drawable on a TextView size change works with API11+
Drawable underlyingDrawable =
new BitmapDrawable(context.getResources(), result);
// Wrap to scale up to the TextView height
final ScaleDrawable scaledLeft =
new ScaleDrawable(underlyingDrawable, Gravity.CENTER, 1F, 1F) {
// Give this drawable a height being at
// least the TextView height. It will be
// used by
// TextView.setCompoundDrawablesWithIntrinsicBounds
public int getIntrinsicHeight() {
return Math.max(super.getIntrinsicHeight(),
competitorView.getHeight());
};
};
// Set explicitly level else the default value
// (0) will prevent .draw to effectively draw
// the underlying Drawable
scaledLeft.setLevel(10000);
// Set the drawable as a component of the
// TextView
competitorView.setCompoundDrawablesWithIntrinsicBounds(
scaledLeft, null, null, null);
// If the text is changed, we need to
// re-register the Drawable to recompute the
// bounds given the new TextView height
competitorView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
competitorView.setCompoundDrawablesWithIntrinsicBounds(scaledLeft, null, null, null);
}
});
I did not found way. But that you show looks like a list of items. Each item would be a LinearLayout horizontal with imagesView from left to right and text in center. The image dimension do a android:scaleType="fitXY" for ajust the size height to the textview height.
If you want no deform de image use scaleType="CENTER_INSIDE". http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
<LinearLayout
android:layout_width="fill_parent"
android:id="#+id/HeaderList"
android:layout_gravity="top"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/backImg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:background="#color/blancotransless"
android:src="#drawable/header"
android:scaleType="fitXY" >
</ImageView>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/NameText"
android:text="The time of prayer"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:paddingLeft="4dp"
android:paddingTop="4dp"
/>
<ImageView
android:id="#+id/backImg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:background="#color/blancotransless"
android:src="#drawable/header"
android:scaleType="fitXY" >
</ImageView>
</LinearLayout>
Another approach to create a custom size for the drawable inside the TextView is by using the BindingAdapter. Using this, you will be able to set you image size in your xml.
For example, TextViewBindingAdapter.java
public class TextViewBindingAdapter {
#BindingAdapter({"android:imageSize"})
public static void bindIconSize(TextView textView, int size) {
Drawable[] drawables = textView.getCompoundDrawables();
if(drawables[0] != null) {
drawables[0].setBounds(0, 0, size, size); // setBounds(int left, int top, int right, int bottom), in this case, drawable is a square image
textView.setCompoundDrawables(
drawables[0], //left
null, //top
null, //right
null //bottom
);
}
if(drawables[1] != null) {
drawables[1].setBounds(0, 0, size, size); // setBounds(int left, int top, int right, int bottom), in this case, drawable is a square image
textView.setCompoundDrawables(
null, //left
drawables[1], //top
null, //right
null //bottom
);
}
if(drawables[2] != null) {
drawables[2].setBounds(0, 0, size, size); // setBounds(int left, int top, int right, int bottom), in this case, drawable is a square image
textView.setCompoundDrawables(
null, //left
null, //top
drawables[2], //right
null //bottom
);
}
if(drawables[3] != null) {
drawables[3].setBounds(0, 0, size, size); // setBounds(int left, int top, int right, int bottom), in this case, drawable is a square image
textView.setCompoundDrawables(
null, //left
null, //top
null, //right
drawables[3] //bottom
);
}
}
}
Now, in your .xml you can set the android:imageSize.
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:drawableLeft="#drawable/alpha_s_circle"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:textSize="16sp"
android:imageSize="#{300}"/>
And don't forget to set in your gradle.build this code below:
android {
....
buildFeatures {
dataBinding = true
}
}
Just make 9-path background repeating this pattern.
And also it seems it will be better look in case the pattern will be applied to the list, not to individual item.
I think the cleanest solution would be to override TextView. Here's an example:
https://gist.github.com/hrules6872/578b52fe90c30c7445a2
I changed a bit:
private void resizeCompoundDrawables() {
Drawable[] drawables = getCompoundDrawables();
if (compoundDrawableWidth > 0 || compoundDrawableHeight > 0) {
for (Drawable drawable : drawables) {
if (drawable == null) continue;
Rect realBounds = drawable.getBounds();
float drawableWidth = realBounds.width();
if(this.compoundDrawableWidth>0)
drawableWidth = this.compoundDrawableWidth;
float drawableHeight = realBounds.height();
if(this.compoundDrawableHeight>0)
drawableHeight = this.compoundDrawableHeight;
realBounds.right = realBounds.left + Math.round(drawableWidth);
realBounds.bottom = realBounds.top + Math.round(drawableHeight);
drawable.setBounds(realBounds);
}
}
super.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);
}
This way you can set the size of the drawable but if you care about width/height ratio make it the way suits you.
Now if you want it to automatically match height of the textview you may also override the TextView's method onSizeChanged:
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
compoundDrawableHeight = h;
resizeCompoundDrawables();
}
You my also want to declare sizes for each side, for example leftCompoundDrawableWidth, rightCompoundDrawableWidth and so on.
Note that it does not work with a Shape as drawable. Sounds like TextView only accepts it if it has the size attributes, that way one can implement it by updating the size of the shapedrawable using setSize or setIntrinsicWidth or ... Not tested.
Maybe it's worth look at ImageSpan. It's a span that replaces the text it's attached to with a Drawable that can be aligned with the bottom or with the baseline of the surrounding. I think it will automatically scale it as well based on the height of the textview.
https://developer.android.com/reference/android/text/style/ImageSpan
I Tried all the solutions but none of them worked for me, then I tried this one
in TextView
<TextView
android:gravity="left"
android:id="#+id/product_view_count"
android:text="#string/view_product_count"
android:drawableLeft="#drawable/ic_eye"
android:drawablePadding="2dp"
android:drawableTint="#color/orange"
android:textSize="20sp"
android:textColor="#color/orange"
android:textStyle="bold"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
in your Activity or fragment or Adapter. I am using my adapter like this
viewcount=itemView.findViewById(R.id.product_view_count);
holder.viewcount.setPivotX(0);
holder.viewcount.setPivotY(0);
holder.viewcount.setScaleX(.7f);
holder.viewcount.setScaleY(.7f);
by using ScaleX, ScaleY in TextView text position will change. to keep the position in left/start using setPivotX,setPivotY
Thank's to Bö macht Blau for the nice solution here
The easiest is to create a new drawable and adjust the drawable objects size
<vector xmlns:android="http://schemas.android.com/apk/res/android"
**android:width="50dp"
android:height="50dp"**
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#android:color/white">
<path
android:fillColor="#android:color/black"
android:pathData="M19,3H4.99C3.89,3 3,3.9 3,5l0.01,14c0,1.1 0.89,2 1.99,2h10l6,-6V5C21,3.9 20.1,3 19,3zM7,8h10v2H7V8zM12,14H7v-2h5V14zM14,19.5V14h5.5L14,19.5z"/>
</vector>

Categories

Resources