I am creating a triangle (Custom View) and then adding it to the parent view (RelativeLayout). i am facing problem here was view taking full screen of parent, how can i set the size of custom view upto that boundaries. triangle extends from view and it has touch listeners. dynamically i'm changing the size of the triangle.
i was added onMeasure() to that view but it takes some fixed amount of width and height..
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
i want set the width and height of custom view depending on the size of view when user change how much it has. look at the image.
It does not matter where the points are. You can draw the triangle of any orientation. But the custom view will always be a rectangular. If you do not draw anything outside the triangle the view will appear triangular.
Related
I am creating a custom view which extends LinearLayout. I'm adding some shapes to the linear layout, currently with a fixed value of space between them. I'm doing so by defining a LayoutParams and setting the margins to create the space between the shapes.
What I want to do is span them at an equal space across the entire screen so they would fill it, but only if the width of the view is set to either match_parent or fill_parent. If it's set to wrap_content then the original fixed value of space should be set.
I've tried doing:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
spaceBetweenShapesPixels = (parentWidth - shapeWidth * numberOfShapes) / numberOfShapess;
}
However, the method seems to be called twice - Once for the width and height of the parent and after that for the View's itself and when it's for the view itself, the space gets a 0 value.
So how can I just make this logic:
if(width is wrap_content)
{
space = 10;
}
else
{
space = (parentWidth - shapeWidth * numberOfShapes) / numberOfShapess;
}
You should use WidthMode and HeightMode
In the onMeasure() method. Below is sample a from one of my projects
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int measuredWidth = 0, measuredHeight = 0;
if (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST) {
measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
}
if (heightMode == MeasureSpec.EXACTLY) {
measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
} else if (heightMode == MeasureSpec.AT_MOST) {
double height = MeasureSpec.getSize(heightMeasureSpec) * 0.8;
measuredHeight = (int) height;// + paddingTop + paddingBottom;
}
}
MeasureSpec.EXACTLY - A view should be exactly this many pixels regardless of how big it actually wants to be.
MeasureSpec.AT_MOST - A view can be this size or smaller if it measures out to be smaller.
MeasureSpec.UNSPECIFIED - A view can be whatever size it needs to be in order to show the content it needs to show.
Refer this for more details https://stackoverflow.com/a/16022982/2809326
I have a custom View which has bigger height than its inner drawn content. I tried to set desired content height manually in code like this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
centerX = chosenDimension / 2 - offsetCenterX;
centerY = chosenDimension / 2 - offsetCenterY;
setMeasuredDimension(chosenDimension, chosenDimension - 200); //IN HERE I`M TRYING TO CROP THE VIEWS HEIGHT
}
But it appears that my View scales down. I do not need this.
My solution to this was to reset ALL the dimensions in dimen.xml, but it kinda wrong. Any idea how to crop it in a right and easy way?
Here it is a picture what I want to cut off
Well, I would not have to cut off that space. Just set the correct values in onMesaure(). And to scale all dimens in dimens.xml
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.
The way I solved this problem is by creating a custom view for the child views, and then overriding onMeasure() for the custom view. The new onMeasure() sets the width and height to be as large as possible.
The problem is when you show the soft keyboard and rotate the phone. With the orientation change and the keyboard showing, onMeasure() sets the "largest" available height to be something ridiculously small, so when I hide the keyboard, the child views have the wrong size.
Is there a way to tell the views to recompute the layout when the keyboard goes away? Or am I doing onMeasure() wrong? Here's the code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
setLayoutParams(new LinearLayout.LayoutParams(
measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec))
);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
Display display = ( (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int screenWidth = display.getWidth();
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the view
result = screenWidth;
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by measureSpec
result = Math.min(result, specSize);
}
}
Log.d(TAG, "Width: "+String.valueOf(result));
return result;
}
measureHeight() is done the same way.
Your super.onMeasure(widthMeasureSpec, heightMeasureSpec) uses the passed in values I would think you would want these to be the actual measured width and height:
super.onMeasure(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
I made a custom view. If I add the view to the layout XML file and I set the height to fill_parent "specSize" return 0. Why?
Code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = 90;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(MeasureSpec.getMode(heightMeasureSpec));
if(specMode != MeasureSpec.UNSPECIFIED){
measuredHeight = specSize;
}
setMeasuredDimension(60, measuredHeight);
}
Does anyone know how I can get the height of fill_parent?
You don't need to call MeasureSpec.getMode inside of call to 'getSize' whole idea of measure spec is to combine way of measuring ( knowing as Spec) and associated Size. See documentation for MeasureSpec.makeMeasureSpec method.So correct code will look something like:
int widthSpec = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
The correct code will be:
int specMode = MeasureSpec.getMode(heightMeasureSpec); ----This was already correct
int specSize = MeasureSpec.getSize(heightMeasureSpec); ----This is corrected.