You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

截图1

我正在开发一款可自定义的TextView,执行textView.setRotation(45)后,TextView旋转但未更新宽高(已在onMeasure()中打印getWidth()和getHeight()日志),显示效果不符合预期。

我找到过相关问题,但现有答案只能在旋转角度为90度时修复尺寸(仅交换宽高)。请问有没有办法让TextView旋转后宽高能动态适配其内容?

截图2


编辑补充

基于@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仍被限制在原始边界内,如何让它在新边界内绘制内容?

截图3


解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:45:24