API Level 11 introduces setRotationX/Y
for View
s, 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!):
- Rendering is certainly doable (and relatively easy)
- 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 :)