Is there a way the this problem can be fixed? I tried invalidate() but, it still displays the same problem. What happens is that, after opening the page/ the Activity, the images behaves like the one in Figure A. It only renders to my desired layout (Figure B) after scrolling it back and forth.
What I'm trying to do is set the width and heigth of the image during runtime. So this is also in relation to my previous questions : Images in my HorizontalListView changes it size randomly and ImageView dynamic width and height which received a very little help.
Any tips regarding this matter, please?
EDIT: btw, my classes are:
MyCustomAdapter (extends baseadapter, this calls the displayimage() from ImageLoader ),
MyActivity and
ImageLoader (this is where my image url are loaded, decoded, displayed asynchronously)
Im also confused as to where i will set the height and width of the imageView. For now, i set it at ImageLoader. It was okay. but i dont know if i did the right thing.
If you want to set the width and height manually at runtime, grab a reference to the ImageView's LayoutParams AFTER the View has been measured by the layout system. If you do this too early in the rendering phase, your view's width and height as well as its parent view and so on will be 0.
I have some code in an open source library that might help you. The process is two parts:
Set up an OnPreDrawListener attached to the ViewTreeObserver for your control. My example does this inside of a custom control, but you can do this in your activity as well.
Inside the onPreDraw method, your image and it's parent will now have their width and height values assigned to them. You can make your calculations and then set your width and/or height manually to the LayoutParams object of your view (don't forget to set it back).
Check out this example where I'm applying an aspect ratio to a custom ImageView just before it's rendered to the screen. I don't know if this exactly fits your use case, but this will demonstrate how to add an OnPreDrawListener to a ViewTreeObserver, removing it when you're done, and applying dynamic sizing to a View at runtime
https://github.com/aguynamedrich/beacon-utils/blob/master/Library/src/us/beacondigital/utils/RemoteImageView.java#L78
Here's a modified version that removes my particular resizing logic. It also grabs the ViewTreeObserver from the imageView, which is a more likely scenario if you're not implementing a custom control and you only want to do this in the Activity
private void initResizeLogic() {
final ViewTreeObserver obs = imageView.getViewTreeObserver();
obs.addOnPreDrawListener(new OnPreDrawListener() {
public boolean onPreDraw() {
dynamicResize();
obs.removeOnPreDrawListener(this);
return true;
}
});
}
protected void dynamicResize() {
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
// resize logic goes here...
// imageView.getWidth() and imageView.getHeight() now return
// their initial layout values
lp.height = someCalculatedHeight;
lp.width = someCalculatedWidth;
imageView.setLayoutParams(lp);
}
}
Related
I have a number of shared elements that I'm transitioning between two activities via ActivityOptions.makeSceneTransitionAnimation. When I enter the second activity, I would like to determine what the final target size that the ImageView will be so I can set the appropriate width of the ImageView. I believe that doing so will allow the object to the right of it (also a shared element) to transition smoothly.
The ImageView is a fitStart scaleType with a fixed height and a wrap_content width. The image will be dynamically loaded and is being shrunk down from the first activity to the second, so I don't know the final width. Before the transition starts, I want to set the ImageView's LayoutParams width to match the final width that it will be after the transition ends.
Currently, the wrap_content property of this ImageView is causing the element to the right of it to not know where its final resting place will be, so it goes off the screen to the right before coming back in and landing in the right place.
Here's a gif of the animation. FIRST ELEMENT is the ImageView, animating correctly, and Second Element is the button to the right of the ImageView:
Notice that when the second activity goes back to the first activity, the Second Element transitions smoothly back to the left of the screen because the end position is known.
When I give the ImageView a specific width like 100dp instead of wrap_content (and then change it back to wrap_content after the transition finishes), Second Element moves more smoothly to its final resting place, but since the ImageView will be dynamically loaded and scaled, I won't know the true width of the image in XML beforehand so there would always be a jerky movement of the Second Element when I change it to wrap_content after the transition.
So I would like to determine the target width of the ImageView and set it programmatically before the transitions begin in the second activity so that Second Element knows how far to the left it needs to go. I've tried playing with ViewTreeObserver and TransitionValues but am missing how to get the final width the ImageView is transitioning to.
Is there something I can call in onCreate of the second activity to determine what width the ImageView will end up at after the transition?
EDIT
I tried the solution in this answer but it's reporting back the ImageView's beginning width, not the final width it's transitioning to. For reference, these are the layouts of the two activities I'm switching between:
Note that I'm moving AND shrinking the ImageView.
I tweaked the answer's code since I'm not using the support library and had to move to imageView.getViewTreeObserver() in the onPreDraw() call to avoid an IllegalStateException:
postponeEnterTransition();
final ImageView imageView = (ImageView) findViewById(R.id.image_view);
final ViewGroup.LayoutParams imageViewLayoutParams = imageView.getLayoutParams();
final ViewTreeObserver viewTreeObserver = imageView.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override public boolean onPreDraw() {
imageView.getViewTreeObserver().removeOnPreDrawListener(this);
// You can acquire the width here
logoLayoutParams.width = imageView.getWidth();
Log.i(TAG, "width is " + imageViewLayoutParams.width);
imageView.setLayoutParams(imageViewLayoutParams);
// `imageView` has been laid out, but not yet been drawn
startPostponedEnterTransition();
return true;
}
});
The log is reporting a width of 998, which is the larger size of the imageView in Activity 1 (its starting size before the transition). So the Second Element is still flying off the screen.
Now that I have this code in place, I can use the aspect ratio information of the larger image to figure out the final width that the smaller image will be since I know the final height, but is that guaranteed with the fitStart scaleType?
It would be really nice to know if Android can tell me the final size the shared element targeting.
Here's the case: if you change in the second activity the resource from #drawable/first_element to #drawable/first_element_thumb, you'll no long experience that behavior. The downside is, that you'll see a glitch each time the transition is starting, that's because the drawable is being changed from high resolution to a low resolution right before transition starts.
Here's with 300ms animation time:
Here's with 5000ms animation time:
If you want the transitioning to be without that glitch, than the solution is to start the transition from high resolution drawable and substitute that drawable to a low resolution whilst transitioning the view. You can see Nick Butcher describing the feature in his "Animatable" talk. You'll end up writing your custom transition class like this one.
There are few thread about save layout as image here and here.
What if I have a list view, and it is impossible to make them display on one screen.
Try with this:
First enable the drawing cache in your ListView
vListView.setDrawingCacheEnabled(true);
Then adjust the size of the ListView to make every item visible.
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) vListView.getLayoutParams();
params.height = (int) (mAdapter.getItemCount() * getResources().getDimension(R.dimen.max_item_height));
vListView.setLayoutParams(params);
Finally you can use either a callback or do a postDelayed with a Handler and get the bitmap.
vListView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
drawingCache = vListView.getDrawingCache();
}
});
Let me know if it did help you.
BTW Remember that in Android Studio, in debug mode, you can check the bitmap variables contents.
You have to add it in hierarchy or it won't compute its layout. But no one says you have to actually show it on screen - create layout in which you can place ListView outside of screen bounds and use drawing cache to construct widget image.
I have to set an image in my activity and in my scenario the image covers fairly large portion of the activity. However I want to keep its width/height to be no more than 33% of its parent viewGroup's width.
I am dynamically changing the ImageView's dimension at runtime using this code.
#Override
protected void onResume() {
super.onResume();
//try to resize the imageview according to current width of container layout
int nWidthPix = findViewById(R.id.layout_info).getWidth();
ImageView imView = (ImageView)findViewById(R.id.imageView_main);
imView.setAdjustViewBounds(true);
int imViewWidth = nWidthPix/3;
imView.setMaxWidth(imViewWidth);
imView.setMaxHeight(imViewWidth);
//
}
The problem is that nWidthPix which is width of RelativeLayout which contains my ImageView is returned 0 in the first call to onResume() that is when the app is launched for first time. As a result image is not shown , in the subsequent calls to onResume() it returns 720 which is correct.
I am guessing that since the view is not really shown yet before the first call on onResume it does not know the width ? but then we can find views on the other hand even before the activity is shown for the first time. .. Not sure what's causing it.
Help
You can use the ViewTreeObserver of the root layout and set an OnGlobalLayoutListener to know when the layout finishes loading. Take a look at this answer for a reference.
View always report 0 as their width and height before they've been measured by the framework. onResume() is too soon to query this information, unfortunately.
If the parent view is the Activity itself, then you can get the window's dimensions (for example with this method) and use those values. Otherwise you need to postpone execution of this code until the layout pass is complete (via an OnGlobalLayoutListener).
As the title states, I am trying to set the width of a layout element to be equal to the height (which is set to match the parent). I've tried setting the width parameter programatically from the height parameter, but this shows up as -1 rather than the actual height (since it matches the parent). Anyone know how I can create square layout elements whose size is dynamic? Thanks.
So what I've been doing for things like this -- and I don't know that it's ideal, but it works quite well -- is use ViewTreeObserver.OnGlobalLayoutListener. Something like this:
View myView = findViewById(R.id.myview);
ViewTreeObserver observer = myView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new SquareLayoutAdjuster());
class SquareLayoutAdjuster
implements ViewTreeObserver.OnGlobalLayoutListener {
#Override
public void onGlobalLayout() {
int dimension = myView.getHeight();
LayoutParams params = myView.getLayoutParams();
params.width = dimension;
myView.setLayoutParams(params);
observer.removeGlobalOnLayoutListener(this);
}
}
That's just the general idea, I don't know if it compiles as is, but basically in the onGlobalLayout() method, your views are guaranteed to be measured. You remove the listener at the end to prevent it from being called multiple times (unless you need that to happen for whatever reason).
the solution of KCOPPOCK works for me, thank you. But i got "java.lang.IllegalStateException: This ViewTreeObserver is not alive, call getViewTreeObserver() again" at runtime. So i use
findViewById(R.id.serie_main_layout).getViewTreeObserver().removeOnGlobalLayoutListener(this);
instead
observer.removeGlobalOnLayoutListener(this);
and all works perfectly.
I have more than one question, but I'll start with the more important and problematic one:
I have a FrameLayout with a ImageView inside it. I need to get the size of the "usable area" of the screen my activity is ocupping, so I set the onSizeChanged on my View (I extended the ImageView class). Everything worked fine here. Now I have a 1000x1000 image that I want to show on the screen, but without scaling. I want it to be clipped, really. If I set the ImageView dimensions using viewObject.setLayoutParams(new Gallery.LayoutParams(1000, 1000)); I get the image being showed correctly, but then the onSizeChanged event returns me always the "1000 x 1000" custom size rather than the real screen size value.
Any ideas of how can I show an image with its real size (no scale!) and still get the view to report the screen available space? I can change the layout as needed as well, of course.
. Amplexos.
Are you asking to get the dimensions of the ImageView? If so then you can get that using getLocalVisibleRect. Here's roughly how it's done:
ImageView yourImageView;
public void onCreate(...){
setContentView(...)
yourImageView = (ImageView) findViewById(...);
(...)
}
getImageViewSize(){
Rect imageViewSize = new Rect();
yourImageView.getLocalVisibleRect(imageViewSize);
// imageViewSize now has all the values you need
Log.d("Your log tag", "ImageView width = " + (imageViewSize.right -
imageViewSize.left));
}
There is however a catch. You have to make sure that you don't try to get the size of the view until after view is finished being laid out on the screen. In other words, if you try to get its size in onCreate, its size will be 0. You have to get it afterwards, for example at the same time as you resize your image, assuming that's done with a button. (If you're using a SurfaceHolder you can also call it during the surfaceCreated callback, but I doubt you're using one of those...)