View's getWidth() and getHeight() returning 0 - android

I have looked at the similar questions and tried their solutions, but it didn't work for me. I'm trying to read width of an imageView, but it is returning 0. Here is the code:
public class MainActivity extends Activity {
private ImageView image1;
float centerX,centerY;
private ImageView image2;
private boolean isFirstImage = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rotate);
image1 = (ImageView) findViewById(R.id.ImageView01);
image2 = (ImageView) findViewById(R.id.ImageView02);
image2.setVisibility(View.GONE);
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
}
private void applyRotation(float start, float end) {
centerX=image1.getWidth()/2.0f;
centerY=image1.getHeight()/2.0f;
final Flip3dAnimation rotation =
new Flip3dAnimation(start, end,centerX , centerY);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(new DisplayNextView(isFirstImage, image1,
image2));
if (isFirstImage)
{
image1.startAnimation(rotation);
} else {
image2.startAnimation(rotation);
}
}
}
Can anyone help me with this? Thanks

You need to use the onSizechanged() method.
This is called during layout when the size of this view has changed

You should use: image1.getLayoutParams().width;

You are either accessing the ImageViews width and height before it has been measured, or after you set its visibility to GONE.
image2.setVisibility(View.GONE);
Then getWidth() or getHeight() will return 0.,
You can also have a look at onWindowFocusChanged(), onSizeChanged() and onMeasure():
#Override
public void onWindowFocusChanged(boolean focus) {
super.onWindowFocusChanged(focus);
// get the imageviews width and height here
}

public class GETImageView extends ImageView {
public GETImageView (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The ImageView have not yet draw the image, so no width or height return.
You may use a subclass to get the imageview width.
Here is some link provide you another easy methods:
How to get the width and height of an android.widget.ImageView?
How to get the width and height of an Image View in android?
Android ImageView return getWidth, getHeight zero
Hope this help you.

you can use OnGlobalLayoutListener for the ImageView image1 so you can get exact width and height occupied by the view in the layout...as
image1 = (ImageView) findViewById(R.id.ImageView01);
ViewTreeObserver viewTreeObserver = image1.getViewTreeObserver();
viewTreeObserver
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
image1.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
else
image1.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});

By the way, views don't get sized until the measurement steps have been taken. See this link:
getWidth() and getHeight() of View returns 0

Maybe you should call View.measure(int,int) first.
Since the width and height is not defined until this view is draw. You can call measure by hand to enforce this view to calculate it's size;

Please using a Handler and get height and width on this with delay time >= 10
This may be useful for use:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
getHeight();
}
private void getHeight() {
// TODO Auto-generated method stub
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Height:" + mAnimationView.getMeasuredHeight(),
Toast.LENGTH_SHORT).show();
}
}, 10L);
}

Related

onDraw method is not drawing

i am working on application in which my ondraw method is not working properly. I checked similar questions but i didnt get the what is the problem. This code drawing a square but not the correct size. It's so small. Also ı write a code for cells but they are not even working. I didnt get the where is the problem.
My class Code:
public SudokuBoard(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
setWillNotCacheDrawing(false);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.SudokuBoard,0,0);
try{
boardColor = a.getInteger(R.styleable.SudokuBoard_boardColor,0);
}
finally {
a.recycle();
}
}
#Override
protected void onMeasure(int width, int height){
super.onMeasure(getWidth(),getHeight());
int dimension = Math.min(this.getMeasuredWidth(),this.getMeasuredHeight());
cellSize = dimension/9;
setMeasuredDimension(dimension,dimension);
}
#Override
protected void onDraw(Canvas canvas){
boardColorPaint.setStyle(Paint.Style.STROKE);
boardColorPaint.setStrokeWidth(16);
boardColorPaint.setColor(boardColor);
boardColorPaint.setAntiAlias(true);
canvas.drawRect(0,0,getWidth(),getHeight(),boardColorPaint);
drawBoard(canvas);
}
private void drawThinLine(){
boardColorPaint.setStyle(Paint.Style.STROKE);
boardColorPaint.setStrokeWidth(10);
boardColorPaint.setColor(boardColor);
}
private void drawThickLine(){
boardColorPaint.setStyle(Paint.Style.STROKE);
boardColorPaint.setStrokeWidth(4);
boardColorPaint.setColor(boardColor);
}
private void drawBoard(Canvas canvas){
for(int c = 0;c<10;c++){
if(c%3==0){
drawThickLine();
}
else{
drawThinLine();
}
canvas.drawLine(cellSize*c,0,cellSize*c,getWidth(),boardColorPaint);
}
for(int r = 0;r<10;r++){
if(r%3==0){
drawThickLine();
}
else{
drawThinLine();
}
canvas.drawLine(0,cellSize*r,getWidth(),cellSize*r,boardColorPaint);
}
}
You cannot call getMeasuredWidth() or getMeasuredHeight() from within onMeasure(), those values aren't determined until setMeasuredDimension() is called.
Also you shouldn't call super.onMeasure(), your job in onMeasure() is to calculate the dimensions and call setMeasuredDimension().
Assuming your SodukoBoard is set as layout_width="match_parent" and layout_height="match_parent", this code should set your SodukoBoard width to a square using the lesser of the two dimensions:
void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
int dimension = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension (dimension, dimension);
}

Animate Relative layout width change

Am trying to do a simple animation in order scale a relative layout's width. I managed to do it using this code but the animation is done very quickly even with 5000ms duration. I cant see the intermidiate results. Any suggestion?
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration){
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(baseLayer.getLayoutParams().width * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
I have found the solution. The problem was that i was calling the animation method from OnStart(). Now am calling it from onWindowFocusChanged().
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
testBar.animate(2000,0.8);
}
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration,float fillPercentage){
final int targetWidth = (int)(baseLayer.getMeasuredWidth() * fillPercentage);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(targetWidth * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
And throughout this procedure I have found from other post an alternative way to do the same job. But again it the method need to be called form the onWindowFocusChanged().
ValueAnimator.ofObject(new WidthEvaluator(fillLayer), fillLayer.getLayoutParams().width, targetWidth).setDuration(2000).start();
class WidthEvaluator extends IntEvaluator {
private View v;
public WidthEvaluator(View v) {
this.v = v;
}
#Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int num = (Integer)super.evaluate(fraction, (Integer)startValue, (Integer)endValue);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = num;
v.setLayoutParams(params);
return num;
}
}
[Update Solution]
Following to that I have added my custom views into a ListView and then the problems started again. ListView should be getting ready before views get their actual size. So the animation wasn't able to be done because getMeasuredWidth() was returning always 0 (this was and the problem from the beginning).
So what i did is i used this code snippet in OnCreate().
ViewTreeObserver vto = listBars.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!HackClass.GlobalDrawnFlag) {
adapter.notifyDataSetChanged();
HackClass.GlobalDrawnFlag = true;
}
}
});
And this inside my list adapter class
if (item.getMeasuredWidth==0)
HackClass.GlobalDrawnFlag = false;
else
HackClass.GlobalDrawnFlag = true;
This approach solved all my problems apparently. However is that a good solution ? Any other suggestion is welcome.

Android - Is there a callback function when a View is all set?

I have a custom View, in which I need to perform treatment that require that the size of the view is set (so onCreate doesn't works), but before displaying anything. So I wondered if there is a callback function tat I could override for that ?
Right now I'm using onLayout, but it seems to be called twice, so I'm assuming it's probably not the right use for this. I also tried doing it in onMeasure but it seems that getWidth() and getHeight still returns 0 in it.
Thkanks,
Robin.
Try to use OnGlobalLayoutListener inside onResume()
#Override
public void onResume(){
super.onResume();
ViewTreeObserver viewTreeObserver = yourView.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
viewWidth = yourView.getWidth();
viewHeight = yourView.getHeight();
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println(viewHeight + " on resume x" + yourView.getX());
}
});
}
}
You can use the callback OnSizeChanged.
use onSizeChanged,
#Override
protected void onSizeChanged(int width, int height, int oldwidth, int oldheight) {
super.onSizeChanged(width, height, oldwidth, oldheight);
// your code here.
}
Try those methods
1.
ViewTreeObserver vto2 = imageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new
OnGlobalLayoutListener() {
#Override
public void onGlobalLayout () {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = imageView.getHeight();
int width = imageView.getWidth();
textView.append("\n\n" + imageView.getHeight() + "," + imageView.getWidth());
}
}
);
2.
imageView.post(new Runnable() {
#Override
public void run () {
int h = imageView.getHeight();
int w = imageView.getWidth();
Log.i(TAG, "Height=" + h);
}
};

View does not fit the canvas on canvas scale

When I'm trying to scale my canvas to a draw SCALED view, my view is actually scaled, but view is getting clipped. (probably because of its layout parameters?)
public void onDraw(Canvas canvas) {
canvas.scale(2f, 2f);
view.draw(canvas);
}
simple image:
image after new onDraw called, for example when I click this button:
The button should be full sized when canvas is scaled. Do you have any ideas how to solve it?
p.s. call of
view.invalidate();
view.requestLayout();
doesn't help.
I'm using MyDragShadowBuilder because I want my view to be double sized when I drag the view.
private final class MyDragShadowBuilder extends DragShadowBuilder {
public MyDragShadowBuilder(View view) {
super(view);
}
#Override
public void onDrawShadow(Canvas canvas) {
final View view = getView();
if (view != null) {
canvas.scale(2f, 2f);
view.draw(canvas);
} else {
Log.e("DragShadowBuilder", "Asked to draw drag shadow but no view");
}
}
I add my view into my Absolute Layout implementation with WRAP_CONTENT layout properties
I ran into the same trouble. After some time i found a way to make it work :)
This scales the original view by a factor of 4.
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
private static final int SCALING_FACTOR = 4;
public MyDragShadowBuilder(View view) {
super(view);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
View v = getView();
final int width = v.getWidth() * SCALING_FACTOR;
final int height = v.getHeight() * SCALING_FACTOR;
shadowSize.set(width, height);
shadowTouchPoint.set(width / 2, height / 2);
}
#Override
public void onDrawShadow(Canvas canvas) {
canvas.scale(SCALING_FACTOR, SCALING_FACTOR);
getView().draw(canvas);
}
}

Animate view sliding out of another view, pushing views below out of the way

I have a list of buttons. When I press a button, a View should slide in a downwards motion out of the button, like this:
Start:
Halfway:
End:
How would I go about this? The View that should slide out is bigger than the button, so first hiding the View behind the button and then sliding it downwards causes the View to be visible above the button. That should not happen.
Any ideas or examples on how to approach this?
I believe the simplest approach is to extend Animation class and override applyTransformation() to change the view's height as follows:
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
public class MyCustomAnimation extends Animation {
public final static int COLLAPSE = 1;
public final static int EXPAND = 0;
private View mView;
private int mEndHeight;
private int mType;
private LinearLayout.LayoutParams mLayoutParams;
public MyCustomAnimation(View view, int duration, int type) {
setDuration(duration);
mView = view;
mEndHeight = mView.getHeight();
mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
mType = type;
if(mType == EXPAND) {
mLayoutParams.height = 0;
} else {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
}
view.setVisibility(View.VISIBLE);
}
public int getHeight(){
return mView.getHeight();
}
public void setHeight(int height){
mEndHeight = height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
if(mType == EXPAND) {
mLayoutParams.height = (int)(mEndHeight * interpolatedTime);
} else {
mLayoutParams.height = (int) (mEndHeight * (1 - interpolatedTime));
}
mView.requestLayout();
} else {
if(mType == EXPAND) {
mLayoutParams.height = LayoutParams.WRAP_CONTENT;
mView.requestLayout();
}else{
mView.setVisibility(View.GONE);
}
}
}
}
To use it, set your onclick() as follows:
int height;
#Override
public void onClick(View v) {
if(view2.getVisibility() == View.VISIBLE){
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.COLLAPSE);
height = a.getHeight();
view2.startAnimation(a);
}else{
MyCustomAnimation a = new MyCustomAnimation(view2, 1000, MyCustomAnimation.EXPAND);
a.setHeight(height);
view2.startAnimation(a);
}
}
Regards.
Use something like:
Animation a = new ScaleAnimation(1, 1, 0, 1, Animation.RELATIVE_TO_SELF, (float) 0.5, Animation.RELATIVE_TO_SELF, (float) 0);
a.setFillAfter(true);
view.setAnimation(a);
a.setDuration(1000);
view.startAnimation(a);
Here is simple example of hand-made animation, that provide what you want. It works in test app, but I'm not sure that there is no bugs:
public class MainActivity extends Activity implements OnClickListener {
private Timer timer;
private TimerTask animationTask;
private View view1;
private View view2;
boolean animating;
boolean increasing = true;
int initHeight = -1;
private LayoutParams params;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = new Timer();
view1 = findViewById(R.id.view1);// clickable view
view1.setOnClickListener(this);
view2 = findViewById(R.id.view2);// animated view
params = view2.getLayoutParams();
}
#Override
protected void onDestroy() {
super.onDestroy();
timer.cancel();
}
#Override
public void onClick(View v) {
Toast.makeText(this, "start animating...", Toast.LENGTH_SHORT).show();
animationTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (animationFinished()) {
animating = false;
cancel();//canceling animating task
return;
}
params.height += increasing ? 1 : -1;
view2.setLayoutParams(params);
}
});
}
private boolean animationFinished() {
int viewHeight = view2.getHeight();
if (increasing && viewHeight >= initHeight) {
return true;
}
if (!increasing && viewHeight <= 0) {
return true;
}
return false;
}
};
//if we already animating - we just change direction of animation
increasing = !increasing;
if (!animating) {
animating = true;
int height = view2.getHeight();
params.height = height;
view2.setLayoutParams(params);//change param "height" from "wrap_conent" to real height
if (initHeight < 0) {//height of view - we setup it only once
initHeight = height;
}
timer.schedule(animationTask, 0, 10);//changing repeat time here will fasten or slow down animation
}
}
}
Maybe you can set the height to 0 and gradually increase the height. But then you will have the problem that you have to be sure your text is aligned at the bottom of the view. And also to know what the maximal height of the view should be.
use a sliding list adapter so much easier than messing around with animations
https://github.com/tjerkw/Android-SlideExpandableListView
Simply pass android:animateLayoutChanges to LinearLayout that holds all the views, you will achieve your desired result.
I would do it like that. First the layout for the whole collapsible panel component: (pseudo xml)
RelativeLayout (id=panel, clip)
LinearLayout (id=content, alignParentBottom=true)
LinearLayout (id=handle, above=content)
This should ensure that the content is always below the handle.
Then when you need to collapse:
Animate the top margin of content from 0 to -content.height
Animate the height of the panel from current to current-content.height

Categories

Resources