1

I am trying to make a view controller that is fixed in portrait mode, but that has a landscape version that can "fade in" over the top. What this means is that I need to be able to have a version of the screen that is the correct size for landscape, and is rotated 90 (or 270 depending) degrees. On iPhone this was easy, but I'm struggling with Android. I've got a custom view containing the view I want rotated, but I can't seem to size the child view correctly, or get the rotation to line up correctly. Is there an easier way? Alternatively, what am I doing wrong here?

@Override
protected void onDraw(Canvas canvas) {
    if (getChildCount() == 1) {
        canvas.save();
        canvas.rotate(90, canvas.getWidth() / 2, canvas.getHeight() / 2); 
        // I have no idea what my pivot point should be

        View child  = getChildAt(0);

        Bitmap bitmap = // bitmap of child
        Paint paint = new Paint();
        canvas.drawBitmap(bitmap, 0, 0, paint);

        canvas.restore();
    }
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    if (getChildCount() == 1) {
        View child  = getChildAt(0);
        child.layout(top, left, bottom, right);
    }
}

What is not helpful is suggesting actually changing the orientation of the view controller. I need it to stay in portrait mode in order to show the portrait version at the same time with a partial alpha transparency.

To be clear, I need to be able to interact with the view in its rotated coordinates, so I need to be able to press buttons, use a scrollview, etc.

Micah Hainline
  • 14,367
  • 9
  • 52
  • 85
  • Could you add a screenshot of the iphone showing the behaviour you're trying to replicate? – Ben Williams Apr 27 '11 at 14:59
  • I'll have to give you a textual screenshot. :) Consider a page of text shown in portrait mode. Now consider a page of text in landscape mode. Now picture both those together, where you can see the landscape mode text faintly overlayed over the top of the portrait mode text. That's what I'm shooting for, and there IS a reason, it just doesn't enter in to the problem itself. :) – Micah Hainline Apr 27 '11 at 15:13

3 Answers3

1

API Level 11 introduces setRotationX/Y for Views, which seems to be exactly what you're looking for.

Supposing Honeycomb is not you target API version, here's what I found out after a few hours toying with this (definitely more challenging than my current project!):

  1. Rendering is certainly doable (and relatively easy)
  2. It's a massive hack and you shouldn't be doing it in the first place

Basically, the main issue is not rendering but processing events. Since Android has no idea that the view is sideways (you just rendered it that way), the view will respond to the area bounded by original pre-rotation coordinates. Therefore, no clicking (well, unless you have a square button that just has the text sideways!).

Really, you should probably look into backporting the Honeycomb changes.

With that said and with a large disclaimer that there might be a multitude of cases where this won't work, here's a sample app:

package com.side;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;

public class TestActivity extends Activity {
    private class SidewaysGroup extends ViewGroup{
        public SidewaysGroup(Context context) {
            super(context);
        }

        @Override
        protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            Log.i("Sideways", "Parent size: " + getWidth() + "x" + getHeight() + ", child size: " + child.getWidth() + "x" + child.getHeight());

            // Create a new canvas for the child (there's probably a way to use the original canvas but I couldn't figure out the transformations)
            Canvas childCanvas = new Canvas();
            Bitmap childBitmap = Bitmap.createBitmap(child.getWidth(), child.getHeight(), Bitmap.Config.ARGB_8888);
            childCanvas.setBitmap(childBitmap);

            boolean ret = super.drawChild(childCanvas, child, drawingTime);

            Matrix matrix = new Matrix();

            // rotate at the bottom left corner
            matrix.postRotate(90f, 0, childBitmap.getHeight());

            // after the rotation we are one `height` further down than we should be
            matrix.postTranslate(0, -childBitmap.getHeight());

            canvas.drawBitmap(childBitmap, matrix, new Paint());

            return ret;
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right,
                int bottom) {
            if(changed && getChildCount()==1)
            {
                final View child = getChildAt(0);

                // This is breaking the flow (measuring would be done twice) - should be moved to onMeasure or measure() itself
                // notice that it inverts the dimensions
                child.measure(MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
                        MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(
                        getMeasuredWidth(), MeasureSpec.AT_MOST));

                child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
            }
        }

    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TextView verticalView = new TextView(this);
        verticalView.setText("This is the vertical text");
        verticalView.setGravity(Gravity.CENTER);
        verticalView.setTextSize(50f);
        verticalView.setTextColor(Color.parseColor("#88ffffff")); // add a bit of transparency to the text


        SidewaysGroup group = new SidewaysGroup(this);

        Button horizontalButton= new Button(this);
        horizontalButton.setText("This is the horizontal button");
        horizontalButton.setGravity(Gravity.CENTER);
        horizontalButton.setTextSize(50f);
        horizontalButton.setBackgroundDrawable(null);
        horizontalButton.setTextColor(Color.WHITE); 

        horizontalButton.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                Log.i("Sideways", "Button click");
            }
        });

        group.addView(horizontalButton);


        RelativeLayout mainLayout = new RelativeLayout(this);

        RelativeLayout.LayoutParams relparams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

        relparams.addRule(RelativeLayout.CENTER_IN_PARENT);

        mainLayout.addView(verticalView, relparams);
        mainLayout.addView(group, relparams);

        setContentView(mainLayout);

        mainLayout.requestLayout();
        

    }
}

Focusing and translating events are left as an exercise to the reader :)

Delyan
  • 8,881
  • 4
  • 37
  • 42
  • This is pretty good stuff, and further than I've been able to get thus far. I found some rotation code (http://groups.google.com/group/android-developers/msg/4c0388feef7bd3d2) that is useful in mapping the events, but there are too many things that just aren't working right, such as EditText views and highlights on buttons. – Micah Hainline May 04 '11 at 13:57
  • Thanks for your help. It hasn't solved my problem, but it's worth the bounty. – Micah Hainline May 09 '11 at 14:03
0

Do You want to scroll in both views (Vertical and Horizontal?) If not, maybe you can take a screenshot of the vertical text (see here ) then do an actual rotation of your layout (i know you didn't want that but I don't understand why, if the vertical view is just needed as an overlay) and then use the screenshot of the vertical screen as an simple transparent bitmap overlay...

Community
  • 1
  • 1
jpm
  • 3,300
  • 1
  • 19
  • 29
-1

From your comments it sounds like you want this:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
   <LinearLayout
        android:id="@+id/linearlayout1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/linearlayout2"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" />
    </LinearLayout>
</FrameLayout>

but I can't see how or why you would want this

Blundell
  • 75,855
  • 30
  • 208
  • 233