ImageView Resizing On Orientation changes - android

I have a question regarding the process of resizing an ImageView Have on orientation changes.
I have to place a bitmap on the top of an image view. (Basically a marker placed by the user when touching it). On orientation change, I store the relatives position of the marker and intent to restaure it when the image is re-displayed.
for now I do it this way :
on restauration I call this :
handler.post(setCollectedAnswersRunnable);
refering to this :
Runnable setCollectedAnswersRunnable = new Runnable() {
#Override
public void run() {
if (iv.getMeasuredHeight() != 0) {
int x = Math.round(coordinateCollectedAnswer.x * iv.getMeasuredWidth() / (float) question.getImage().getWidth());
int y = Math.round(coordinateCollectedAnswer.y * iv.getMeasuredHeight() / (float) question.getImage().getHeight());
selectedX = x;
selectedY = y;
addPointerImageView(x, y);
} else {
handler.postDelayed(this, 330);
}
}
};
The important thing to note is this works very well when I go from a larger ImageView to a smaller one ie when passing from landscape to portrait.
But in the other way, it doesn't. The ImageView seems to be restaured first with the same size and then scaling up witch mess my marker up. So I certainely could check if it's the same size as in portrait in addition to the !=0 condition in my runnable. but first I'm not very proud of that way to do it, I'd rather be notified when the ImageView get it's final sizing. And I would prefer not passing the viewsize in my state bundle.
Is there a simpler way to achieve this ?
By the way I load the ImageView this way :
Picasso.with(iv.getContext())
.load(AppConstants.SERVER_URL + "/api/" + question.getImage().getUrl())
.config(Bitmap.Config.RGB_565)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
iv.setImageBitmap(bitmap);
iv.setAdjustViewBounds(true);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});

I´ve got a SurfaceView that keeps to be square. i´ve notice thatn when android runtime is doing all the stuff for layout will measure the component again and again and you never can tell when is the last time and the number of executions does not seem to be related to the amount of components on the screen, it is not random but maybe has something to do with the layout constraints of all the stuff around.
here´s my class overriding the measure ion order to keep square:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
int size = w;
if (w < h) {
setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
} else {
size = h;
setMeasuredDimension(heightMeasureSpec, heightMeasureSpec);
}
if (h > 0 && squareSurfaceViewListener != null) {
squareSurfaceViewListener.onChangeSize(size);
}
}
So I´d inherit the imageview, detect the change and reload the image.
Using picasso it´ll cache the stuff but, maybe, you could forget about that and call for a new load each time the component is measured or add some kind of observer in the middle.

Related

Android - Best place to add views inside custom viewgroup/relativelayout when width is known

I am currently optimising a complex view containing many nested views. therefor i have created a custom layout extending the relativelayout class.
Based on data set from outside the class i further build the view with childs.
The child building is done within the custom layout. I got it working and the performance gain is enourmous. But before i can create and add the childs i need to know the width of the view.
There are several ways of getting the width of the view:
1 add global layout listener
public void init(){
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//width is known -> create child views
}
});
}
This solution does not always fire an event. For example when the view is inside a fragment and restored from a backstack state. Also there seem to be a 100 to 500ms delay before this event is triggered.
in onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(w, h);
post(new Runnable() {
#Override
public void run() {
//width is known -> create child views
MyCustomViewGroup.this.postInvalidate();
}
});
}
The difficulty here is that adding views inside onmeasure will result into a call to onmeasure again. And endless loop is the result. Logic needed to prevent this. Could't figure out how.
3 add views in onLayout
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//width is known -> create child views
}
Various unexpected layout problems. Views not respecting layoutparams and showing weird behaviour. Not really sure how to solve it.
hacky timer implementation
private Handler ha = new Handler();
private Runnable r;
public void init() {
if (w > 0) {
//width is known ->create childs
return;
}
r = new Runnable() {
private long time = 0;
#Override
public void run() {
Log.d(TAG, "run");
if (w > 0) {
//width is known ->create childs
} else {
init(); //width is not known -> check later
}
}
};
ha.postDelayed(r, 5);
}
Ironically the last solution works best for me.
I know its a hell of a hack.
is there is anyone out there knowing alternatives? or can give me tips.

Android surfaceChanged incorrect values for width/height

I've a bitmap that is drawn on a surface and needs to be resized every time the device is rotated. Because of soft buttons, I use this code to get current dimensions (it seems to work ok):
public void getCanvasXY(){
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2){
// HoneyComb 3.2 and up
DisplayMetrics metrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
Global.CanvasX = metrics.widthPixels;
Global.CanvasY = metrics.heightPixels;
} else{
// before HoneyComb
Global.CanvasX = getWidth();
Global.CanvasY = getHeight();
}
}
I thought the best place to recalculate the dimensions and create my bitmap was in surfaceChanged().
#Override
public void surfaceChanged(SurfaceHolder holder1, int format, int width, int height) {
getCanvasXY();
LoadBMP();
}
I see that this is called more than once, and I get diferent values for CanvasX and CanvasY while device is rotating. The last time it's called works ok, but I see how onDraw() is drawing my bitmap and sprites bad resized and finally ok. It's an ugly effect.
How to solve this?
thanks
Here I assume you mean SurfaceView, where you are using a background thread to draw to a surface and your are using SurfaceHolder.CallBack(). This is not a complete answer, however, this problem also has plagued me for a while. In my late night troubleshooting I managed to put together a workaround which may be a little more elegant than yours. The result being that nothing is drawn until the surfacechanged has settled to it's last state, so the user is unaware of what is happening.
In your surfacechanged you'll notice that your height or width isn't changing to the correct value, in otherwords the result surface if drawn to is MalFormed. What you need is a reference value to what your height and width should be, you can get that by placing the following code before you SetContentView() in your main activity:
WindowManager w = getWindowManager();
Display d = w.getDefaultDisplay();
int Meausredwidth = d.getWidth();
int Measuredheight = d.getHeight();
Now from your measured height and width, work out how much you are assigning to the surfaceView, and pass those values to your SurfaceView as global variables. And make sure you are calling the above code in onconfigurationchanged() in your main activity so those values get recalculated each time. Now on your SurfaceView which should somehow implement SurfaceHolder.Callback, Change your surfacechanged to look like this:
#Override
public void surfaceChanged(SurfaceHolder mholder, int format, int width,
int height) {
if (width != mWidth) {
MalFormed = true;
thread.setRunning(false);
count++;
} else if (width == mWidth) {
if (MalFormed) {
MalFormed = false;
Log.d("ROTATE", "ReDraw");
thread = new DrawingThread(this, holder, mHandler);
thread.setRunning(true);
thread.start();
}
}
}
Where mWidth is the desired width. Here you may need to change the if statement depending on your specific problem. And MalFormed is a global boolean in your surfaceview.
Now it's important to remember, if a change occurs SurfaceChanged() gets called before SurfaceCreated, which means we can easily modify any code which attempts to interact with the thread to suit our above changes.
So anywhere where else you are attempting to join or start that background drawing thread you must check first if the surface is MalFormed. E.g.
if (thread.getState() == State.TERMINATED){
if(!MalFormed){
holder.removeCallback(this);
holder = getHolder();
holder.addCallback(this);
thread = new DrawingThread(this, holder,mHandler);
thread.setRunning(true);
thread.start();
}
}else{
if(!MalFormed){
thread.setRunning(true);
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Hope I'm in the ballpark somewhere. All the best.

Getting the width/height of a layout in Android

I'm wondering how to measure the dimensions of a view. In my case it is aan Absolute Layout. I've read the answers concerning those questions but I still don't get it.
This is my code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AbsoluteLayout layoutbase = (AbsoluteLayout) findViewById(R.id.layoutbase);
drawOval();
}
public void drawOval(){ //, int screenWidth, int screenHeight){
AbsoluteLayout layoutbase = (AbsoluteLayout) findViewById(R.id.layoutbase);
int screenWidth = layoutbase.getWidth();
int screenHeight = layoutbase.getHeight();
Log.i("MyActivity", "screenWidth: " + screenWidth + ", screenHeight: " +screenHeight);
Coordinates c = new Coordinates(BUTTONSIZE,screenWidth,screenHeight);
...some code ...
((ViewGroup) layoutbase ).addView(mybutton, new AbsoluteLayout.LayoutParams(BUTTONSIZE, BUTTONSIZE, c.mX, c.mY));
mybutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showText(mybutton);
}
});
}
public void showText(View button){
int x = findViewById(LAYOUT).getWidth();
int y = findViewById(LAYOUT).getHeight();
Toast message = Toast.makeText(this, "x: " + x , Toast.LENGTH_SHORT);
message.show();
}
The getWidth() command works great in showText() but it does not in drawOval(). I know it looks a bit different there but I also used the int x = findViewById(LAYOUT).getWidth(); version in drawOval(), and x/y are always 0. I don't really understand why there seems to be no width/height at that earlier point. Even if I actually draw a Button on the Absolute Layout, getWidth() returns 0. Oviously I want to measure the sizes in drawOval().
I think will help you.
LinearLayout headerLayout = (LinearLayout)findviewbyid(R.id.headerLayout);
ViewTreeObserver observer = headerLayout .getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
int headerLayoutHeight= headerLayout.getHeight();
int headerLayoutWidth = headerLayout.getWidth();
headerLayout .getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
});
}
getWidth() is giving you 0 because onCreate is called before layout actually happens. Due to views being able to have dynamic positions and sizes based on attributes or other elements (fill_parent for example) there's not a fixed size for any given view or layout. At runtime there is a point in time (actually it can happen repeatedly depending on many factors) where everything is actually measured and laid out. If you really need the height and width, you'll have to get them later as you've discovered.
This specially deal with Dimensions so
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
This may help you in managing dimensions.
Note: This returns the display dimensions in pixels - as expected. But the getWidth() and getHeight() methods are deprecated. Instead you can use:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
as also Martin Koubek suggested.
If your goal is to simply draw an oval on the screen, then consider creating your own custom View rather than messing around with AbsoluteLayout. Your custom View must override onDraw(android.graphics.Canvas), which will be called when the view should render its content.
Here is some extremely simple sample code that might help get you started:
public class MainActivity extends Activity {
private final Paint mPaint = new Paint();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
// create a nested custom view class that can draw an oval. if the
// "SampleView" is not specific to the Activity, put the class in
// a new file called "SampleView.java" and make the class public
// and non-static so that other Activities can use it.
private static class SampleView extends View {
public SampleView(Context context) {
super(context);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.CYAN);
// smoothen edges
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4.5f);
// set alpha value (opacity)
mPaint.setAlpha(0x80);
// draw oval on canvas
canvas.drawOval(new RectF(50, 50, 20, 40), mPaint);
}
}
}
This give you screen resolution:
WindowManager wm = (WindowManager)context.getSystemService(context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point outSize = new Point();
display.getSize(outSize);
kabuko's answer is correct, but could be a little more clear, so let me clarify.
getWidth() and getHeight() are (correctly) giving you 0 because they have not been drawn in the layout when you call them. try calling the two methods on the button after addView() (after the view has been drawn and is present in the layout) and see if that gives you the expected result.
See this post for more information.

Determining the size of an Android view at runtime

I am trying to apply an animation to a view in my Android app after my activity is created. To do this, I need to determine the current size of the view, and then set up an animation to scale from the current size to the new size. This part must be done at runtime, since the view scales to different sizes depending on input from the user. My layout is defined in XML.
This seems like an easy task, and there are lots of SO questions regarding this though none which solved my problem, obviously. So perhaps I am missing something obvious. I get a handle to my view by:
ImageView myView = (ImageView) getWindow().findViewById(R.id.MyViewID);
This works fine, but when calling getWidth(), getHeight(), getMeasuredWidth(), getLayoutParams().width, etc., they all return 0. I have also tried manually calling measure() on the view followed by a call to getMeasuredWidth(), but that has no effect.
I have tried calling these methods and inspecting the object in the debugger in my activity's onCreate() and in onPostCreate(). How can I figure out the exact dimensions of this view at runtime?
Use the ViewTreeObserver on the View to wait for the first layout. Only after the first layout will getWidth()/getHeight()/getMeasuredWidth()/getMeasuredHeight() work.
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
viewWidth = view.getWidth();
viewHeight = view.getHeight();
}
});
}
There are actually multiple solutions, depending on the scenario:
The safe method, will work just before drawing the view, after the layout phase has finished:
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
runnable.run();
return true;
}
};
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
Sample usage:
ViewUtil.runJustBeforeBeingDrawn(yourView, new Runnable() {
#Override
public void run() {
//Here you can safely get the view size (use "getWidth" and "getHeight"), and do whatever you wish with it
}
});
On some cases, it's enough to measure the size of the view manually:
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth();
int height=view.getMeasuredHeight();
If you know the size of the container:
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxWidth, View.MeasureSpec.AT_MOST)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
view.measure(widthMeasureSpec, heightMeasureSpec)
val width=view.measuredWidth
val height=view.measuredHeight
if you have a custom view that you've extended, you can get its size on the "onMeasure" method, but I think it works well only on some cases :
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int newHeight= MeasureSpec.getSize(heightMeasureSpec);
final int newWidth= MeasureSpec.getSize(widthMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
If you write in Kotlin, you can use the next function, which behind the scenes works exactly like runJustBeforeBeingDrawn that I've written:
view.doOnPreDraw { actionToBeTriggered() }
Note that you need to add this to gradle (found via here) :
android {
kotlinOptions {
jvmTarget = "1.8"
}
}
implementation 'androidx.core:core-ktx:#.#'
Are you calling getWidth() before the view is actually laid out on the screen?
A common mistake made by new Android developers is to use the width
and height of a view inside its constructor. When a view’s
constructor is called, Android doesn’t know yet how big the view will
be, so the sizes are set to zero. The real sizes are calculated during
the layout stage, which occurs after construction but before anything
is drawn. You can use the onSizeChanged() method to be notified of
the values when they are known, or you can use the getWidth() and
getHeight() methods later, such as in the onDraw() method.
Based on #mbaird's advice, I found a workable solution by subclassing the ImageView class and overriding onLayout(). I then created an observer interface which my activity implemented and passed a reference to itself to the class, which allowed it to tell the activity when it was actually finished sizing.
I'm not 100% convinced that this is the best solution (hence my not marking this answer as correct just yet), but it does work and according to the documentation is the first time when one can find the actual size of a view.
Here is the code for getting the layout via overriding a view if API < 11 (API 11 includes the View.OnLayoutChangedListener feature):
public class CustomListView extends ListView
{
private OnLayoutChangedListener layoutChangedListener;
public CustomListView(Context context)
{
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
if (layoutChangedListener != null)
{
layoutChangedListener.onLayout(changed, l, t, r, b);
}
super.onLayout(changed, l, t, r, b);
}
public void setLayoutChangedListener(
OnLayoutChangedListener layoutChangedListener)
{
this.layoutChangedListener = layoutChangedListener;
}
}
public interface OnLayoutChangedListener
{
void onLayout(boolean changed, int l, int t, int r, int b);
}
You can check this question. You can use the View's post() method.
Use below code, it is give the size of view.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.e("WIDTH",""+view.getWidth());
Log.e("HEIGHT",""+view.getHeight());
}
This works for me in my onClickListener:
yourView.postDelayed(new Runnable() {
#Override
public void run() {
yourView.invalidate();
System.out.println("Height yourView: " + yourView.getHeight());
System.out.println("Width yourView: " + yourView.getWidth());
}
}, 1);
I was also lost around getMeasuredWidth() and getMeasuredHeight() getHeight() and getWidth() for a long time.......... later i found that getting the view's width and height in onSizeChanged() is the best way to do this........ you can dynamically get your CURRENT width and CURRENT height of your view by overriding the onSizeChanged() method.
might wanna take a look at this which has an elaborate code snippet.
New Blog Post: how to get width and height dimensions of a customView (extends View) in Android http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html
In Kotlin file, change accordingly
Handler().postDelayed({
Your Code
}, 1)
You can get both Position and Dimension of the view on screen
val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;
if (viewTreeObserver.isAlive) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
//Remove Listener
videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);
//View Dimentions
viewWidth = videoView.width;
viewHeight = videoView.height;
//View Location
val point = IntArray(2)
videoView.post {
videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
viewPositionX = point[0]
viewPositionY = point[1]
}
}
});
}
If you need to know the dimensions of a View right after it is drawn you can simply call post() on that given View and send there a Runnable that executes whatever you need.
It is a better solution than ViewTreeObserver and globalLayout since it gets called repeatedly not just once.
This Runnsble will execute only once and you will know the views size.
works perfekt for me:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
CTEditor ctEdit = Element as CTEditor;
if (ctEdit == null) return;
if (e.PropertyName == "Text")
{
double xHeight = Element.Height;
double aHaight = Control.Height;
double height;
Control.Measure(LayoutParams.MatchParent,LayoutParams.WrapContent);
height = Control.MeasuredHeight;
height = xHeight / aHaight * height;
if (Element.HeightRequest != height)
Element.HeightRequest = height;
}
}

Rotate View Hierarchy 90 degrees

I am working on a subclass of FrameLayout that is supposed to rotate all of its children by 90 degrees. I am doing this to overcome the landscape-only camera limitation present in android 2.1 and below, by having the activity be in landscape, but placing my camera overlay into this framelayout overlay to cause it to appear as if it was portrait (this is how Layar does it) To accomplish this, I'm adapting Jeff Sharkey's code to rotate views. My problem is that I can rotate the Framelayout, but I cannot resize it to match the new dimensions. So on my g1, instead of a 320x480 portrait view over a 480x320 camera view in landscape, I get a 320x320 box in the middle showing my portrait view with the sides chopped off.
Here is my code so far:
public class RotateLayout extends FrameLayout {
private Matrix mForward = new Matrix();
private Matrix mReverse = new Matrix();
private float[] mTemp = new float[2];
public RotateLayout(Context context) {
super(context);
}
public RotateLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/* (non-Javadoc)
* #see android.widget.FrameLayout#onMeasure(int, int)
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//This didn't work:
//super.onMeasure(heightMeasureSpec, widthMeasureSpec);
}
/* (non-Javadoc)
* #see android.widget.FrameLayout#onSizeChanged(int, int, int, int)
*/
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.rotate(270, getWidth()/2, getHeight()/2);
//This code will stretch the canvas to accommodate the new screen size. This is not what I want.
//float scaleX=(float)getHeight()/getWidth();
//float scaleY=(float)getWidth()/getHeight();
//canvas.scale(scaleX, scaleY, getWidth()/2, getHeight()/2);
mForward = canvas.getMatrix();
mForward.invert(mReverse);
canvas.save();
canvas.setMatrix(mForward); //This is the matrix we need to use for proper positioning of touch events
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
final float[] temp = mTemp;
temp[0] = event.getX();
temp[1] = event.getY();
mReverse.mapPoints(temp);
event.setLocation(temp[0], temp[1]);
return super.dispatchTouchEvent(event);
}
}
I have tried overriding OnMeasure to switch the X and Y dimensions of the View, but have not been able to get that to work.
Any help you can provide is greatly appreciated.
I had the same problem and managed to solve it.
Instead of rotating each view or the layout by hand, I used a LayoutAnimationController.
First, place a file in /res/anim/ called rotation.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="-90"
android:pivotX="50%"
android:pivotY="50%"
android:duration="0" android:fillAfter="true">
</rotate>
Then, in your Activity's onCreate, do
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.myscreen);
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotation);
LayoutAnimationController animController = new LayoutAnimationController(rotateAnim, 0);
FrameLayout layout = (FrameLayout)findViewById(R.id.MyScreen_ContentLayout);
layout.setLayoutAnimation(animController);
}
If you want to rotate elements that lie above your camera preview view (SurfaceHolder), simply place a FrameLayout above the SurfaceHolder, place all your elements in that FrameLayout and call the Layout "MyScreen_ContentLayout". Done.
Hope that helped someone out, took me quite a while to get everything together.
Using API level 11 and later you can use the method setRotation(degreesFloat); to change the rotation of a view programmatically, or you can use the XML attribute android:rotation="" to change it in your XML. There are also methods/attributes for changing only the X or Y values of a view's rotation: Android Docs - View (setRotation).
So nowadays as long as you're using API level 11 or above, you should be able to apply the rotation to a wrapper layout node. However, you probably will also have to change the dimensions of the top-level layout to match the dimensions you desire after the rotation. I.e. if you have a portrait view w/ dimensions 800x1280, you'll have to change them to 1280x800 in order for it to line up after rotating to landscape.
Using this library you can rotate whole view hierarchy https://github.com/rongi/rotate-layout
Like this
This is what has worked for me in general.
private void init() {
setRotation(90f);
}
public YourViewOrViewGroup(final Context context) {
super(context);
init();
}
... (all the View/ViewGroup constructors) ...
#Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
super.onLayout(changed, l, t, r, b);
final int width = getWidth();
final int height = getHeight();
final int offset = Math.abs(width - height) / 2;
setTranslationX(-offset);
setTranslationY(offset);
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
}
What you want to do is swap the width with height and then put the X & Y offsets so that the view becomes full screen after the rotation.
The above is a 'landscape'-rotated version. To achieve a landscape inverted just apply 270-deg rotation. You can either modify code within the snippet or apply the rotation outside in a more generic way, i.e
final YourViewOrViewGroup layout = inflater.inflate(...);
if (currentOrientation.isInverted()) {
layout.setRotation(layout.getRotation + 180f);
}
this way you are able to embed the rotated View/ViewGroup within the xml definition and inflate 'port' and 'land' versions while the screen orientation changes, but this seems out of this topic.
Edit: actually it is much better to defer the offset setting until at least one layout pass is over.
This is due the fact that in my case after first onMeasure() the view would be drawn (before the offsets were set). Eventually it could be experienced as glitching because the view/layout would not get drawn within the final bounds at first.
(updated the snippet)
I think you forgot one line in your onMeasure.
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
Taken from the code for a vertical seekbar here:
How can I get a working vertical SeekBar in Android?
You might need to fiddle with the getMeasuredHeight() to make it the correct size of your screen.
Try turning off children clipping of your view root: call setClipChildren(false) on parent of your RotateLayout and in onMeasure method of your RotateLayout put these lines:
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
I'm having basically the same problem as you and I still haven't tested my solution - I'll do it tomorrow and tell if it is working correctly.

Categories

Resources