Android View.setRotation后视图尺寸未更新,自定义TextView适配问题
自定义TextView旋转后宽高适配问题
这是一个TextView:
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/high_padding" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toLeftOf="parent" android:id="@+id/draggable_view_component_main"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> </FrameLayout> ... </merge>

我正在开发一款可自定义的TextView,执行textView.setRotation(45)后,TextView旋转但未更新宽高(已在onMeasure()中打印getWidth()和getHeight()日志),显示效果不符合预期。
我找到过相关问题,但现有答案只能在旋转角度为90度时修复尺寸(仅交换宽高)。请问有没有办法让TextView旋转后宽高能动态适配其内容?

编辑补充
基于@avalerio的回答,我实现了宽高计算逻辑:
public class CustomEditText extends androidx.appcompat.widget.AppCompatEditText { ... @Override public void setRotation(float rotation) { super.setRotation(rotation); requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int oldwidth = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(); int oldheight = getMeasuredHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom(); int width = oldwidth; int height = oldheight; double angle = Math.toRadians(getRotation()); ... // 一系列计算逻辑 ... width += getCompoundPaddingLeft() + getCompoundPaddingRight(); height += getCompoundPaddingTop() + getCompoundPaddingBottom(); setMeasuredDimension(width, height); } }
计算出的宽高日志显示正确,但TextView仍被限制在原始边界内,如何让它在新边界内绘制内容?

解决方案
1. 修正旋转后宽高的计算逻辑
对于任意角度的旋转,文本区域的外接矩形宽高需要用三角函数精准计算,替换你代码中的空白计算逻辑:
// 替换原有的...//一系列计算逻辑部分 float rotatedContentWidth = (float) (Math.abs(oldwidth * Math.cos(angle)) + Math.abs(oldheight * Math.sin(angle))); float rotatedContentHeight = (float) (Math.abs(oldwidth * Math.sin(angle)) + Math.abs(oldheight * Math.cos(angle))); width = (int) Math.ceil(rotatedContentWidth); height = (int) Math.ceil(rotatedContentHeight);
2. 重写onDraw调整画布绘制原点
TextView默认会基于原始边界裁剪内容,即便测量出正确宽高,也需要调整画布让内容在新边界内完整绘制:
@Override protected void onDraw(Canvas canvas) { canvas.save(); // 计算偏移量,让旋转后的内容在新View范围内居中 float centerX = getMeasuredWidth() / 2f; float centerY = getMeasuredHeight() / 2f; // 先平移到View中心,旋转后再确保内容对齐 canvas.translate(centerX - getWidth()/2f, centerY - getHeight()/2f); canvas.rotate(getRotation(), getWidth()/2f, getHeight()/2f); super.onDraw(canvas); canvas.restore(); }
3. 关闭裁剪限制
如果仍出现内容被裁剪的情况,需要关闭View及父容器的裁剪开关:
// 在自定义View的构造方法中添加 setClipToPadding(false); setClipChildren(false); // 同时在父布局(比如你的FrameLayout)中也设置 parentFrameLayout.setClipChildren(false);
4. 确保父布局支持wrap_content
检查你的FrameLayout及上层父布局的layout_width和layout_height是否设置为wrap_content,固定宽高会限制TextView的尺寸扩展。
完整自定义EditText示例
public class RotationAdjustedEditText extends androidx.appcompat.widget.AppCompatEditText { private float mRotation = 0f; public RotationAdjustedEditText(Context context) { super(context); init(); } public RotationAdjustedEditText(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RotationAdjustedEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setClipToPadding(false); setClipChildren(false); } @Override public void setRotation(float rotation) { mRotation = rotation; super.setRotation(rotation); requestLayout(); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int contentWidth = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(); int contentHeight = getMeasuredHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom(); if (contentWidth <= 0 || contentHeight <= 0) return; double angle = Math.toRadians(mRotation); float rotatedWidth = (float) (Math.abs(contentWidth * Math.cos(angle)) + Math.abs(contentHeight * Math.sin(angle))); float rotatedHeight = (float) (Math.abs(contentWidth * Math.sin(angle)) + Math.abs(contentHeight * Math.cos(angle))); int finalWidth = (int) Math.ceil(rotatedWidth) + getCompoundPaddingLeft() + getCompoundPaddingRight(); int finalHeight = (int) Math.ceil(rotatedHeight) + getCompoundPaddingTop() + getCompoundPaddingBottom(); setMeasuredDimension(finalWidth, finalHeight); } @Override protected void onDraw(Canvas canvas) { canvas.save(); float centerX = getMeasuredWidth() / 2f; float centerY = getMeasuredHeight() / 2f; // 调整画布位置,让旋转后的内容居中 canvas.translate(centerX - getWidth()/2f, centerY - getHeight()/2f); canvas.rotate(mRotation, getWidth()/2f, getHeight()/2f); super.onDraw(canvas); canvas.restore(); } }
内容的提问来源于stack exchange,提问作者Amin Guermazi
相关产品推荐
相关产品推荐

