How can I resize an Android UI element during onLayout? - android

I have a button class based on a LinearLayout that contains two TextViews, and I want the button always to be square (which I'm doing using onMeasure()), and the upper of the TextViews to resize automatically to fit the button. I want the lower TextView to have fixed-size text, so it looks roughly like
|---------|
| |
| 1 8 0 |
| |
| foo |
|---------|
with "180" being as large as possible in the available space.
I'm calling a function in onLayout() called adjustTextSize():
private void adjustTextSize() {
float height = mHeight - mTitleView.getMeasuredHeight() - 4 * mPadding - getPaddingTop() - getPaddingBottom(); //title text view
int numberheight = (int) (height * 0.75);
int extravertpadding = 0;//(int) (height * 0.125);
mTitleView.setPadding(mPadding, 0, mPadding, mPadding);
mNumberView.setTextSize(TypedValue.COMPLEX_UNIT_PX, numberheight);
mNumberView.setPadding(mPadding, mPadding + extravertpadding, mPadding, mPadding + extravertpadding);
if(mNumberView.getMeasuredWidth() > mWidth) {
float reduction = mWidth / mNumberView.getMeasuredWidth();
extravertpadding = (int) ((numberheight - height*reduction) / 2);
numberheight = (int) (height*reduction);
mNumberView.setTextSize(TypedValue.COMPLEX_UNIT_PX, numberheight);
mNumberView.setPadding(mPadding, mPadding + extravertpadding, mPadding, mPadding + extravertpadding);
}
mNumberView.setTextSize(TypedValue.COMPLEX_UNIT_PX, numberheight);
mNumberView.setPadding(mPadding, mPadding + extravertpadding, mPadding, mPadding + extravertpadding);
}
mWidth and mHeight are set in onLayout().
However, I have two initial problems: firstly, setting the text size to a certain pixel value makes the View somewhat bigger than the pixel value. I want to work out how large to set the text height to fit the available space; three quarters seems to work OK but I would rather have an exact value.
The second problem is that the value of getMeasuredWidth() seems not to be changing after I've changed the text size. Why not?

Answer to part 2:
It's necessary to call
mNumberView.measure(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
);
to tell the system to measure the view as though there were no constraints on its size. After this has been called you can find out how large the view would be if space were no object and scale it down accordingly.

Related

Android grid of dots to fill View

I'm trying to make a game in Android Studio using Kotlin, currently I have a grid drawn using two for loops (1 for cols and 1 for rows) that draws dots. I want the dots to fill the View, without the blank space around the grid. Any ideas?
val columns = 5
val rows = 5
var xPos: Float = width / (columns + 1)
var yPos: Float = height / (rows + 1)
for (col in 1..columns) {
for (row in 1..rows) {
canvas.drawPoint(col*xPos, row*yPos, paint)
}
}
What I get:
d
What I want:
You need a little space around the edges. So you can have a variable for padding, which should be half the diameter of your dots plus however many pixels of white you want around them. You would probably calculate the size using a constant DIP unit and the screen density, same as you probably did for your paint to get the dot diameter.
Then you can use the padding in your calculation. For example, if you have five columns, to get the space between dots, you want to divide the width by four, after subtracting the padding from both sides.
val padding = /* ... */
val columns = 5
val rows = 5
val hSpacing = (width - (2 * padding)) / (columns - 1)
val vSpacing = (height - (2 * padding)) / (rows - 1)
for (i in 0..columns)
for (j in 0..rows)
canvas.drawPoint(padding + i * hSpacing, padding + j * vSpacing, paint)

StaggeredGridLayoutManager left a gap before a full span element

I have a RecyclerView with a StaggeredGridLayoutManager.
The problem is that before a full span element there is a gap that I need to fill, increasing/decreasing the height of the last two elements.
A picture of the problem:
Just 2 columns are required.
The elements of the grid can have different heights.
I'm trying to calculate the height of the two columns using each element height, and at the end see which column is longer, and add height to the shortest, but I don't know where is a safe place to set the new height. When I know for sure the height of the view is the one is going to be draw.
I'm stuck for days with this problem, any suggestions are welcome!
onBindViewHolder
double positionHeight = getPositionRatio(position);
holder.img_event).setHeightRatio(positionHeight);
private double getPositionRatio(final int position) {
double ratio = sPositionHeightRatios.get(position, 0.0);
// if not yet done generate and stash the columns height
// in our real world scenario this will be determined by
// some match based on the known height and width of the image
// and maybe a helpful way to get the column height!
if (ratio == 0) {
ratio = getRandomHeightRatio();
sPositionHeightRatios.append(position, ratio);
Log.d("'", "getPositionRatio:" + position + " ratio:" + ratio);
}
return ratio;
}
private final Random mRandom = new Random();
private double getRandomHeightRatio() {
return (mRandom.nextDouble() / 2.0) + 1.0; // height will be 1.0 - 1.5 the width
}

Android Color Interpolation

I want to change textcolor of a textview which is in the header of the listview from a particular color (This could be any color) to WHITE onscroll of the listview.
I read about HSV and I need to decrease values of H and S towards 0 and increase the value of V towards 1 to get a color closer to WHITE
Has someone done a similar thing where in interpolation would happen with the value of scrollY?
Hope it will help:
private final float[] mHsvTemp = new float[3];
int hsvInterplate(float[] hsvWhen0, float[] hsvWhen1, float scale) {
if (scale <= 0) return Color.HSVToColor(hsvWhen0);
if (scale >= 1) return Color.HSVToColor(hsvWhen1);
float hDist = hsvWhen1[0] - hsvWhen0[0];
if (hDist > 180) hDist -= 360;
else if (hDist <= -180) hDist += 360;
mHsvTemp[0] = hsvWhen0[0] + hDist * scale;
mHsvTemp[1] = hsvWhen0[1] + (hsvWhen0[1] - hsvWhen0[1]) * scale;
mHsvTemp[2] = hsvWhen0[2] + (hsvWhen0[2] - hsvWhen0[2]) * scale;
return Color.HSVToColor(mHsvTemp);
}
This function takes two hsv arguments and interpolates between them, depend on scale value. scale is in range from 0 to 1.
zero or less - color fully as first argument
one or greater - color fully as second argument
0.5f - average middle between them.
So, you can pass something like (float)getScrollY() / computeVerticalScrollRange () as third argument

Drawing a square in the center of a canvas

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);

MuPDF Android Pdf fit to the screen

I succesfully installed MuPDF for one of my Android Apps. But the problems is, while rendering I cannot fit the PDF to the screen. Can anybody please suggest me how to achieve this. Thanks!
Edit the measureView method in ReaderView.java to become.
private void measureView(View v) {
// See what size the view wants to be
v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// Work out a scale that will fit it to this view
float scale;
if (getWidth()>getHeight())
{
scale = (float)getWidth()/(float)v.getMeasuredWidth();
MIN_SCALE = (float)v.getMeasuredWidth()/(float)getWidth();
MAX_SCALE = MIN_SCALE*5.0f;
}
else
scale = Math.min((float)getWidth()/(float)v.getMeasuredWidth(),
(float)getHeight()/(float)v.getMeasuredHeight());
// Use the fitting values scaled by our current scale factor
v.measure(View.MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()*scale*mScale),
View.MeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()*scale*mScale));
}
I was getting errors with regard to the MAX and MIN scale final variables, however I made a slight modification to #hassan83's solution. My solution is tested in MuPDF version 1.9a
private void measureView(View v) {
// See what size the view wants to be
v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// Work out a scale that will fit it to this view
float scale;
//landscape orientation
if (getWidth() > getHeight())
{
scale = (float)getWidth()/(float)v.getMeasuredWidth() * (1.0f ); //make sure that we fill the page by multiplying with a scale factor of one
}
else
scale = Math.min((float)getWidth()/(float)v.getMeasuredWidth(),
(float)getHeight()/(float)v.getMeasuredHeight());
// Use the fitting values scaled by our current scale factor
v.measure(View.MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()*scale*mScale),
View.MeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()*scale*mScale));
}
with this the page is resized boot too larger from the view. here is the "measureview" method in my code:
private void measureView(View v) {
// See what size the view wants to be
v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
if (!mReflow) {
// Work out a scale that will fit it to this view
float scale = Math.min((float) getWidth() / (float) v.getMeasuredWidth(),
(float) getHeight() / (float) v.getMeasuredHeight());
// Use the fitting values scaled by our current scale factor
v.measure(MeasureSpec.AT_MOST | (int) (v.getMeasuredWidth() * (scale + 0.8f) * mScale),
MeasureSpec.AT_MOST | (int) (v.getMeasuredHeight() * (scale+0.8f) * mScale));
} else {
v.measure(View.MeasureSpec.EXACTLY | (int) (v.getMeasuredWidth()),
View.MeasureSpec.EXACTLY | (int) (v.getMeasuredHeight()));
}
}
this is what I need, boot the page need to be fetched in exactly in the view, boot with the text readable.
In addition to answer https://stackoverflow.com/a/21168243/2982225,
you can re-render the view by adding the following code at the end:
requestLayout();
post(this);
again.
So the complete code is as follows (attribution for code before last comment: the accepted answer of this question ):
private void measureView(View v) {
// See what size the view wants to be
v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
// Work out a scale that will fit it to this view
float scale;
if (getWidth()>getHeight())
{
scale = (float)getWidth()/(float)v.getMeasuredWidth();
MIN_SCALE = (float)v.getMeasuredWidth()/(float)getWidth();
MAX_SCALE = MIN_SCALE*5.0f;
}
else
scale = Math.min((float)getWidth()/(float)v.getMeasuredWidth(),
(float)getHeight()/(float)v.getMeasuredHeight());
// Use the fitting values scaled by our current scale factor
v.measure(View.MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()*scale*mScale),
View.MeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()*scale*mScale));
// last comment
// to force the initial rendering to the new scale
requestLayout();
post(this);
}
Hope this helps.

Categories

Resources