Using ideas from breceivemail answer I made rotate layout that actually works. It's designed to hold single view, it will dispatch touch events correctly but no padding or margins are supported in this realization. Also it's only supports angles like 90, 180, 270 etc. Layout size will just match the size of it's child after rotation.
public class RotateLayout extends ViewGroup {
public static class LayoutParams extends ViewGroup.LayoutParams {
public int angle;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RotateLayout_Layout);
angle = a.getInt(R.styleable.RotateLayout_Layout_layout_angle, 0);
}
public LayoutParams(ViewGroup.LayoutParams layoutParams) {
super(layoutParams);
}
}
public RotateLayout(Context context) {
super(context);
}
public RotateLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public View getView() {
return view;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
view = getChildAt(0);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
if(angle != layoutParams.angle) {
angle = layoutParams.angle;
angleChanged = true;
}
if(Math.abs(angle % 180) == 90) {
measureChild(view, heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(
resolveSize(view.getMeasuredHeight(), widthMeasureSpec),
resolveSize(view.getMeasuredWidth(), heightMeasureSpec));
}
else {
measureChild(view, widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(
resolveSize(view.getMeasuredWidth(), widthMeasureSpec),
resolveSize(view.getMeasuredHeight(), heightMeasureSpec));
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if(changed || angleChanged) {
layoutRectF.set(0, 0, r - l, b - t);
layoutTransitionMatrix.setRotate(angle, layoutRectF.centerX(), layoutRectF.centerY());
layoutTransitionMatrix.mapRect(layoutRectFRotated, layoutRectF);
layoutRectFRotated.round(viewRectRotated);
angleChanged = false;
}
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right, viewRectRotated.bottom);
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.rotate(-angle, getWidth() / 2f, getHeight() / 2f);
super.dispatchDraw(canvas);
canvas.restore();
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
invalidate();
return super.invalidateChildInParent(location, dirty);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
touchPoint[0] = event.getX();
touchPoint[1] = event.getY();
layoutTransitionMatrix.mapPoints(childTouchPoint, touchPoint);
event.setLocation(childTouchPoint[0], childTouchPoint[1]);
return super.dispatchTouchEvent(event);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new RotateLayout.LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams layoutParams) {
return layoutParams instanceof RotateLayout.LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) {
return new RotateLayout.LayoutParams(layoutParams);
}
private View view;
private int angle;
private final Matrix layoutTransitionMatrix = new Matrix();
private final Rect viewRectRotated = new Rect();
private final RectF layoutRectF = new RectF();
private final RectF layoutRectFRotated = new RectF();
private final float[] touchPoint = new float[2];
private final float[] childTouchPoint = new float[2];
private boolean angleChanged = true;
}
attrs.xml (add this file to res/values folder)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RotateLayout_Layout">
<attr name="layout_angle" format="integer" />
</declare-styleable>
</resources>
Usage example:
<com.you.package.name.RotateLayout
xmlns:app="http://schemas.android.com/apk/res/com.you.package.name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_angle="-90"
android:text="Rotated Button"/>
</com.you.package.name.RotateLayout>