package com.github.rongi.rotate_layout.layout; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.github.rongi.rotate_layout.example.R; 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) { this(context, null); } public RotateLayout(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(false); } public View getView() { return getChildAt(0); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final View view = getView(); if (view != null) { 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)); } } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (angleChanged || changed) { final RectF layoutRect = tempRectF1; final RectF layoutRectRotated = tempRectF2; layoutRect.set(0, 0, r - l, b - t); rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY()); rotateMatrix.mapRect(layoutRectRotated, layoutRect); layoutRectRotated.round(viewRectRotated); angleChanged = false; } final View view = getView(); if (view != null) { 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) { viewTouchPoint[0] = event.getX(); viewTouchPoint[1] = event.getY(); rotateMatrix.mapPoints(childTouchPoint, viewTouchPoint); event.setLocation(childTouchPoint[0], childTouchPoint[1]); boolean result = super.dispatchTouchEvent(event); event.setLocation(viewTouchPoint[0], viewTouchPoint[1]); return result; } @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 int angle; private final Matrix rotateMatrix = new Matrix(); private final Rect viewRectRotated = new Rect(); private final RectF tempRectF1 = new RectF(); private final RectF tempRectF2 = new RectF(); private final float[] viewTouchPoint = new float[2]; private final float[] childTouchPoint = new float[2]; private boolean angleChanged = true; }
实现方案:
旋转,重新layout实现重新布局,传递消息时将x y坐标转换,达到目的。
完整工程:
https://github.com/luxiaoming/rotate-layout