I have a button.
The button is constructed from the StateListDrawable (made of 3 9-patch images).
I need to add an extra drawable that will reside on the button's right side, and i need it to be aligned with the button's right side.
I tried to use the following:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/blue_back_button_drawable" />
<item>
<bitmap android:src="#drawable/filter_by_button_v_mark" android:gravity="right"/>
</item>
</layer-list>
it didn't work and the V image is indeed aligned right but it has a diff from the button's right side. here's a snapshot:
I want the image to be aligned with the button's left, the only way right now i think i can do it is:
inherit button, in onLayout AFTER the width has been set get the right edge.
get the background drawable (layerDrawable)
calc the button's width minus the v image width and set it as left margin in the drawable.
I should not mention this sounds horrid :-) i hoped there's a better way. Oh the reason it's not part of the image is that i need to know it's width so i can calc the text padding so it wont be hidden by the button's text and because it's not so nice looking as a 9-patch.
Seems like there was no proper solution to this. layer is for static and not 9-patch images apparently, or so it seems.
So i extended Button, changed it's on Draw, i have to use Canvas.restore() to break out of the margins that pushed my edges down and put my image in the upper right corner...
here's the code:
#Override
public void setPadding(int left, int top, int right, int bottom) {
right += mImageWidth;
super.setPadding(left, top, right, bottom);
}
private void setVImageDrawable(Drawable d) {
mVImageDrawable = d;
mImageWidth = mVImageDrawable.getIntrinsicWidth();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if( mVImageDrawable != null && mIsMarked){
//for some reason the clip binds us to position with margins, which is not so good for us.
//we need to save the clip, restore the canvas to pre clipping mode, do what we need then
//put the canvas back to it's previous clipping state.
Rect bounds = canvas.getClipBounds();
canvas.restore();
int right = getRight();
int left = right - mImageWidth;
int top = getTop();
int bottom = top + mVImageDrawable.getIntrinsicHeight();
mVImageDrawable.setBounds(left, top, right, bottom);
mVImageDrawable.draw(canvas);
canvas.save();
canvas.clipRect(bounds);
}
}
If i don't break out the original canvas iget 'pushed down' by any margins i add, it may also be fixed i think using xml property that specifies if children should be clipped on the layout containing this button, but i find that lame solution as it makes my button dependent on our conditions.
The image itself i get from XML using new attributes and style and theme i declare.
Hope it'll help some ppl out there.
Related
As an Android drawable, this represents a black rectangle with rounded corners.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp" />
<solid android:color="#000000" />
</shape>
However, the corner radius is specified as an absolute 20dp, so if this same drawable is shown at different sizes, it appears differently. The smaller shape is not just a "scaled down" version of the larger one. Instead, it's "rounder" and the larger one is "more square" because the border radius is a static 20dp regardless of the size of the drawable.
I want to specify the radius relative to the size of the full drawable, so when it is drawn at different sizes each one appears as a scaled up/down version of the others.
I'm more familiar with CSS, where this can be done in one line:
border-radius: 20%;
I'm surprised to find Android lacking this CSS simplicity. Android does not recognize % as a unit.
<corners android:radius="20%" />
Is there some simple way to achieve my desired result in Android?
You are right, there is no built in way. But you can achieve it programatically. A time ago I wrote this small class and it works for me
public class CornerDrawable extends GradientDrawable {
private float percent;
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
setCornerRadius(getBounds().height() / percent);
}
#Override
public void setBounds(#NonNull Rect bounds) {
super.setBounds(bounds);
setCornerRadius(getBounds().height() / percent);
}
/**
* Sets the corner radius of each corner to the <code>percent</code> of the height of this drawable. For example, passing
* <i>50</i> will result in a fully rounded side of the target view.
*
* #param percent The percentage of the height of the view to be rounded
*/
public void setCornerRadiusPercent(float percent) {
this.percent = 100 / percent;
}
}
Then you only need to create an instance and configure it the way you want and set it on your target view. For example like this:
//the view you want to apply the corner radius to
View targetView = ...
CornerDrawable cornerDrawable = new CornerDrawable();
//pass your desired percent value
cornerDrawable.setCornerRadiusPercent(20);
// set the solid color from your xml above
cornerDrawable.setColor(getResources().getColor(R.color.black, requireContext().getTheme()));
targetView.setBackground(cornerDrawable);
The only drawback of this is, that you have to do all configuration programmatically, instead of using your drawable xml file.
I have a round corners layout, now I want to add a child view (an Imageview) which matches the parent layout's height and width.
My problem is that the child view hides the round corners of the parent.
How can I constrain it inside the borders of the parent layout without using the margin property, so that the parent's round corners stay visible?
PS: I created round corners of parent layout by overriding the onDraw() method.
My code:
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, width, height, mpaint);
super.onDraw(canvas);
}
In my opinion, you can put your child views into a CardView(in support v7), which is actually a FrameLayout, but it handle the corners by just set one line of code:
app:cardCornerRadius="3dp"
It can clip the corner with the radius you set no matter what the child views are.
I suggest you add padding to the "rounded corner" view. This could be padding on all sides, bottom and top or left and right. Depending on what suits you the best.
I can't think of a more simple method than this. Your onDraw method looks fine, first the background than the child views.
When you add a child to your ViewGroup, that child is being drawn on top of your ViewGroup, thus your rounded corner doesn't take effect.
In order to achieve your goal you have to perform clipping on a certain path in your layout. That sounds a bit complicated, but in fact it is not.
Essentially, you can understand clipping as "cutting off" some part from your layout.
RectF rect = new RectF(0, 0, width, height);
path = new Path();
path.addRoundRect(rect, cornerRadius, cornerRadius, Direction.CW);
canvas.clipPath(path); // clipping here
// now anything that is outside this path will be "clipped", i.e. not drawn
You can refer to this for a complete source code.
My relative layout is named "highlight".
public static void selectText(float left, float right, float top, float bottom) {
highlight.getLayoutParams().width =(int) (right-left);
highlight.getLayoutParams().height=(int) (bottom - top);
highlight.setX(left);
highlight.setY(top);
}
This works great for highlighting text as far as setting the top left corner of the highlight box. But, the box expands all the way to the bottom right corner of the screen, no matter how small I make the .width and .height values.
You set your width and height as wrap_content. Your layout will have the size of it's content.
Instead of:
highlight.getLayoutParams().width =(int) (right-left);
highlight.getLayoutParams().height=(int) (bottom - top);
Try:
highlight.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Check this link:
http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html
I don't know why, but the simple 'get layout params' wasn't working. I needed to make a new layout params entirely.
int width = (int) (right-left);
int height = (int) (bottom - top);
RelativeLayout.LayoutParams rlMainlayoutParams = new RelativeLayout.LayoutParams((int) (right-left), (int) (bottom - top));
highlight.setLayoutParams(rlMainlayoutParams);
highlight.setX(left);
highlight.setY(top);
The above answer doesn't answer my question, perhaps because I was not clear that this relative layout doesn't hold any text, it only highlights certain text already present on the screen (in a PDF document, where I cannot necessarily just extract the text to a PDF document).
After you've modified a view's layout params, call requestLayout() on the view for the changes to take effect.
(Calling setLayoutParams() also implicity calls requestLayout().)
I want to create a layout that contains an image on top of which I want to place many images and TextViews. I know how to place images on top of one another using RelativeLayout, but how to align them in a desired way? Eg I want an image to be exactly in a place where my “background” image has a specific black circle. Playing with values like android:layout_marginTop etc does not seem to do the effect in every screen.
So which is the proper way to handle these issues?
EDIT:
I cannot upload the images, but I uploaded a very simple sketch of what I want here:
http://imageshack.us/photo/my-images/715/buttonlayout.png/
all the buttons have also Icons and text (which must be a textview so that I can change it programmatically if need be)
You have to create a custom layout that places the image specifically where you want them relative to the size of the parent view. If you choose, you can override the LayoutParams and apply custom attributes to them for your custom view to read.
Anyway, to specifically place an item, say 30% down from the top and 20% from the left, you would overwrite onLayout().
#Override
public void onLayout(boolean c, int left, int top, int right, int bottom) {
super.onLayout(c, left, top, right, bottom);
int width = right - left;
int height = bottom - top;
View v = getTheChildView();
int viewL = left + (int)(width * .2f); // The left pixel is 20% down the total width of the parent view
int viewR = viewL + v.getWidth(); // The right pixel is the left pixel plus the measured width of the child view itself
int viewT = top + (int)(height * .3f); // The top pixel is 30% down the total height of the parent view
int viewB = top + v.getHeight(); // The bottom pixel is the top pixel plus the measured height of the child view itself
v.layout(viewL, viewT, viewR, viewB);
}
I'd like to be able to use an image as my background in a relative layout, without it stretching. I don't want to just enter the images details into an XML file, as I want to use different images, with different sizes.
I've tried the following, but it just stretches the images (I know the left, top, right and bottom variables have the wrong values, but I've just entered something into them, as a test to see if it makes any difference to the output, but it does not. If it did, I'd calculate the correct values programmaticly : -
RelativeLayout explosionlayout = (RelativeLayout) findViewById (R.id.explosionlayout);
imageAnim = (ImageView) findViewById(R.id.explosion);
Drawable d;
d = this.getResources().getDrawable(R.drawable.bomb);
int left = 10; int top = 10; int right = 20; int bottom = 20;
d.setBounds(left, top, right, bottom);
explosionlayout.setBackgroundDrawable(d);
This is obviously wrong. What would be the correct solution?
Thanks.
Set the ImageView's scaleType attribute to "center".