I'm trying to figure out how to draw a Square within my onDraw method in Android.
The square must be positioned in the exact center of the canvas
(Not the screen)
The padding/spacing on the left and right hand side of the square should be
equal
The padding/spacing on the top and bottom of the square should be equal
The size of the square should be relatively large, about 90% of the
canvas's width
Here's what I have so far.
//this.rect is an instance of Rect() which later gets called in the canvas.drawRect() method
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = this.getMeasuredWidth();
int height = this.getMeasuredHeight();
int padding = (width / 10);
this.size = width - padding;
this.rect.set(padding,padding,size,size);
}
The code above draws the square but I'm not sure how to get it to center in the canvas. I am also open to using another technique that does not involve using a Rect.
What properties do I need to set to this Rect() in order for the canvas.drawRect(rect,paint) to draw the rectangle directly in the center of the canvas?
Edit:
Terribly drawn example of what I want to achieve
Assuming width is the width of the canvas, I guess you're missing substracting the padding twice.-
this.size = width - padding * 2;
EDIT
Since we're talking about a rectangle here, you'll need to do some more changes to your code, and calculate different top and left padding .-
int width = this.getMeasuredWidth();
int height = this.getMeasuredHeight();
int paddingLeft = (width / 10);
size = width - paddingLeft * 2;
int paddingTop = (height - size) / 2;
this.rect.set(paddingLeft,paddingTop,size,size);
EDIT 2
Maybe a clearer approach would start calculating the size of your square.-
size = width * 0.9f;
int paddingLeft = (width - size) / 2;
int paddingTop = (height - size) / 2;
this.rect.set(paddingLeft,paddingTop,size,size);
Related
I'm using the thumb of the horizontal scroll view to indicate which page the user is on. What I would like to happen is the thumb width be the screenwidth/number of pages which is what happens on larger devices. The problem comes when I switch to a phone: the thumb width is no longer the size it's bigger which I cannot use.
Is there a way to change the width of the thumb in a horizontal scroll view (or the hight in a vertical scroll view)?
So I figured it out. You cannot change the actual scrollviews scrollbars because you need the class ScrollBarDrawable which isn't in the ADK. So what I did was overrode the scrollview and drew a custom scrollbar. Also make sure to disable the scrollviews default scrollbars (View.setVericalScrollbarEnabled(boolean) or android:scrollbars="none")
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
width = getWidth() - getPaddingLeft() - getPaddingRight();
//you need this to get the total width of the scrollview (with all of the children)
measure(w,h);
totalWidth = getMeasuredWidth();
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
float xPer = getScrollX() / totalWidth;
canvas.drawRect(
(xPer * width) + getScrollX(),
getHeight() - scrollBarHeight,
//scroll position //width of the bar //offset in the actual canvas
((xPer * width) + (width/(totalWidth/width)) + getScrollX(),
getHeight(), paint);
}
Hope this helps someone else!
Given a simple RelativeLayout like this:
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#0fffff">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="#drawable/img001" />
</RelativeLayout>
the left/top spacing between the layout border and the image border depends on the W/H ratio of the image being load in the imageView.
How can I know (programmatically) the real margin (width and height of the cyan area) after an image is shown in this layout?
This method will calculate the new rectangle which bounds the object after FIT_CENTER and all other related values.
It should work on all cases of object and container.
public static Rect calculateFitCenterObjectRect(float containerWidth, float containerHeight, float objectWidth, float objectHeight) {
// scale value to make fit center
double scale = Math.min( (double)containerWidth / (double)objectWidth, (double)containerHeight / (double)objectHeight);
int h = (int) (scale * objectHeight); // new height of the object
int w = (int) (scale * objectWidth); // new width of the object
int x = (int) ((containerWidth - w) * 0.5f); // new x location of the object relative to the container
int y = (int) ((containerHeight - h) * 0.5f); // new y location of the object relative to the container
return new Rect(x, y, x + w, y + h);
}
You can use FrameLayout to position the view wherever you want after using the previous method with the new x, y, width, height of the scaled object.
If you know the width of the ImageView, like this
int ivWidth = iv.getMeasuredWidth();
and the total width of the layout (your RelativeLayout), like this
int layoutWidth = yourLayout.getWidth();
then, you can easily get the horizontal margin, like this
int horizontalMargin = (layoutWidth - ivWidth)/2;
And the same goes for height.
You should call functions like getWidth and getHeight after the dimensions of your layout have been calculated, as described by Veer's and Khan's answer on How to get the width and height of an Image View in android?.
Calling getWidth or getHeight in onCreate will return 0.
I have recently delved into creating custom ViewGroups and have encountered a problem I can't figure out.
I have 2 ViewGroups - ViewManager and Article
ViewManager simply lays out an Article below the previous Article (ie like a vertical LinearLayout)
Article arranges several TextViews and an ImageView as you can see in the image. Creating a single Article and adding it to ViewManager works fine and everything shows up but when I add a second Article none of the content of the Article is visible.
So why are none of the TextViews or the ImageView showing up (note the blue bar is drawn with canvas.drawRect() in onDraw()). I have also printed out (via logcat) the the bound values of the child views(ie getLeft()/Top()/Right()/Bottom() in drawChild() and they all seem to look fine
FIRST TextView
FIRST left 5 right 225 top 26 bottom 147
FIRST TextView
FIRST left 5 right 320 top 147 bottom 198
FIRST TextView
FIRST left 5 right 180 top 208 bottom 222
FIRST ImageView
FIRST left 225 right 315 top 34 bottom 129
SECOND TextView
SECOND left 10 right 53 top 238 bottom 257
SECOND TextView
SECOND left 5 right 325 top 257 bottom 349
SECOND TextView
SECOND left 5 right 320 top 349 bottom 400
SECOND TextView
SECOND left 5 right 180 top 410 bottom 424
So does anyone have an idea of what I've done wrong?
the Article onMeasure method
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
specWidth = widthSpecSize;
int totalHeight=0;
int width = 0;
if(mImage!=null&&mImage.getParent()!=null){
measureChild(mImage,MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST));
width=widthSpecSize-mImage.getWidth();
imageWidth = mImage.getMeasuredWidth();
}
for(int i = 0;i<this.getChildCount();i++){
final View child = this.getChildAt(i);
//get the width of the available view minus the image width
if(imageWidth > 0)
width =widthSpecSize- imageWidth;
else
width = widthSpecSize;
//measure only the textviews
if(!(child instanceof ImageView)){
measureChild(child, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), heightMeasureSpec);
//calculate total height of the views, used to set the dimension
totalHeight+=child.getMeasuredHeight();
}
}
//if the title height is greater than the image then the snippet
//can stretch to full width
if(mTitle.getMeasuredHeight()>mImage.getMeasuredHeight())
measureChild(mSnippet, MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.AT_MOST), heightMeasureSpec);
//measure source to make it full width
measureChild(mSource,MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST), heightMeasureSpec);
setMeasuredDimension(widthSpecSize, totalHeight);
}
and the onLayout method
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int newLeft = left+outerMargin;
int newTop = top+marginTop;
int prevHeight = 0;
if(mEngine!=null){
int height = mEngine.getMeasuredHeight();
int childRight = newLeft+mEngine.getMeasuredWidth();
mEngine.layout(newLeft+marginLeft, newTop+prevHeight+2, childRight+marginLeft, newTop+height+prevHeight+2);
maxRight = Math.max(maxRight, childRight);
prevHeight += height+2;
topBarHeight = mEngine.getMeasuredHeight()+(marginLeft*2);
maxBottom = Math.max(maxBottom, mEngine.getBottom());
}
if(mTitle!=null){
int height = mTitle.getMeasuredHeight();
int childRight = newLeft+mTitle.getMeasuredWidth();
mTitle.layout(newLeft, newTop+prevHeight, childRight, newTop+height+prevHeight);
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mTitle.getBottom());
}
if(mSnippet!=null){
int height = mSnippet.getMeasuredHeight();
int childRight = newLeft+mSnippet.getMeasuredWidth();
mSnippet.layout(newLeft, newTop+prevHeight, right, newTop+height+prevHeight);
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mSnippet.getBottom());
}
if(mSource !=null){
int height = mSource.getMeasuredHeight();
int childRight = newLeft+mSource.getMeasuredWidth();
mSource.layout(newLeft, newTop+prevHeight+(marginTop*2), childRight, newTop+height+prevHeight+(marginTop*2));
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mSource.getBottom());
}
if(mImage!=null){
int height = mImage.getMeasuredHeight();
log("mxW "+maxRight);
int childRight = maxRight+mImage.getMeasuredWidth();
mImage.layout(right-mImage.getMeasuredWidth()+5, newTop+topBarHeight, right-5, height+topBarHeight);
totalWidth = childRight;
maxBottom = Math.max(maxBottom, mImage.getBottom());
}else
totalWidth = maxRight;
}
On a side note is it okay to use LayoutParams.WRAP_CONTENT as a parameter for makeMeasureSpec() like this?
MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST)
In onLayout, the children should be positioned with respect to their parent. You are adding top (and left) to the coordinates of the children. That's not a problem for the first Article because top and left are zero. For the second Article, left is still zero but top is the top of the second article in its ViewManager. Just get rid of newTop and newLeft and use, respectively, marginTop and outerMargin in their place.
Regarding your aside: the first argument to makeMeasureSpec is supposed to be a dimension. By using WRAP_CONTENT, you are setting the maximum dimension to -2, which is probably not what you want to do.
I am using Canvas.drawCircle to draw a circle in Android laout.
Method gets 3 parameters - first two are position - x and y.
Is it possible to skip hardcoded position of the circle and draw it centered ?
Following code can be used to get the width and height of the screen.
int width = this.getWidth();
int height = this.getHeight();
To draw circle in the middle of screen you can call :
Canvas.drawCircle(width/2, height/2)
You can paint a circle centered to the screen like this:
Display disp = ((WindowManager)this.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
canvas.drawCircle(disp.getWidth()/2, disp.getHeight()/2, radius, paint);
Assuming you are extending the View class:
int CentreX = (this.getWidth() / 2);
int CentreY = (this.getHeight() / 2);
I am drawing a Drawable to a custom view in android.
When the custom view is displayed the parent is a ScrollView. When the scrollview moves the Drawables shrink until they get off screen and then get bigger as you scroll back to the custom widget even though its explicitly set at 219dip square.
The reason seems to be the way I'm getting the bounds in the onDraw(Canvas canvas) method
int mRight;
int mLeft;
int mBottom;
int mTop;
mRight = canvas.getClipBounds().right;
mLeft = canvas.getClipBounds().left;
mBottom = canvas.getClipBounds().bottom;
mTop = canvas.getClipBounds().top;
int availableWidth = mRight - mLeft;
int availableHeight = mBottom - mTop;
It seems that the ClipBound changes to reflect how much of the view is on the screen. Therefore resize the Drawables until it gets really small and goes off screen.
I tried to replace the code above with this
int availableWidth = canvas.getWidth();
int availableHeight = canvas.getHeight();
Nothing is drawn to the screen now? shouldn't this work?
When drawing my custom Drawable I use:
float w = this.getBounds().width();
float h = this.getBounds().height();
to get the bounding area of the my Drawable in the draw(Canvas canvas)method. I have not tried this in a ScrollView, but I would think that the bounds of the Drawable should work the same either way.
Hope this helps.
This works for my custom View, this code goes in the onDraw(Canvas canvas) method.
mRight = this.getRight();
mLeft = this.getLeft();
mBottom = this.getBottom();
mTop = this.getTop();
int availableWidth = mRight - mLeft;
int availableHeight = mBottom - mTop;
previous answer was wrong, view has no method getBounds()