如何旋转TextView而不裁剪其边界?

10

我正在尝试使用canvas.rotate()旋转我的TextView子类:

canvas.save();

final int w = getWidth();
final int h = getHeight();

float px = w/2f;
float py = h/2f;
canvas.rotate(mAngle, px, py);

super.draw(canvas);

canvas.restore();

我的 TextView 被旋转了,但是我的视图边界被裁剪了:

illustration

我理解这是因为我的视图大小没有在旋转期间改变。如果我在 onMeasure 中更改宽度/高度,问题仍将存在 - 我正在使用 LayoutParams.WRAP_CONTENT,所以 TextView 只会根据 setMeasuredDimensions 中提供的值来更改其大小。

我该如何解决这个问题?


2
尝试将android:clipChildren=false;和android:clipToPadding=false;设置到TextView的父级。 - Leonidos
@Leonidos,旋转时会导致奇怪的伪影。此外,我必须剪切一些视图,所以不能只设置clipChildren=false - Dmitry Zaytsev
4个回答

4

我认为这里的整个问题是你使用了WRAP_CONTENT。当你那样做时,视图的剪辑矩形是文本内容的大小。解决这个问题的最简单方法是使用padding。像这样的方式对我来说可以很好地工作:

<com.example.twistedtext.TwistedTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="30dp"
    android:gravity="center"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    twistedtext:rotation="30.0f"
    android:text="@string/hello_world" />

当然,如果这样做,每个内容的填充都需要选择稍微不同的填充值。如果不能这样做,请覆盖onMeasure方法,使其完全按照TextView的onMeasure方法执行,然后根据旋转需要增加相应的填充。
稍后添加: 实际上,这是一件有趣的事情。我有以下onMeasure方法,效果还不错:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int w = getMeasuredWidth();
    int h = getMeasuredHeight();
    w = (int) Math.round(w * cosA + h * sinA);
    h = (int) Math.round(h * cosA + w * sinA);
    setMeasuredDimension(w, h);
}

唯一剩下的问题是文本根据预旋转尺寸进行换行。你也需要解决这个问题...

当设置mAngle时,会计算sinA和cosA。


2

好的,如果您无法在当前视图层次结构中旋转textView,则应在另一个视图层次结构中对其进行动画处理。

创建textView的位图表示。像ImageView这样使用一个持有者来保存该位图。将imageView连接到另一个位于顶部且不会被裁剪的布局上。隐藏您的textView。在ImageView上运行动画。当动画完成后,更新textView表示,显示textView并删除ImageView。

我找不到您想要的确切代码示例,但请看这个。交互的地方是:

mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(YOUR_VIEW_WITH_ANIMATION, mWindowParams);

2

乍一看,G. Blake Meike提供的解决方案是一个不错的开始,但是还有很多问题需要解决。

为了克服这些问题,还有另一种方法。它非常简单,但通常不应该使用(因为我刚刚从原始的Android源代码中删除了我不需要的东西)。要控制具体视图的绘制,您可以覆盖其父类的drawChild方法(是的,您需要自己的ViewGroup子类或更具体的东西,如FrameLayout)。以下是我的解决方案示例:

@Override
protected boolean drawChild(Canvas canvas, View child, long time) {
    //TextBaloon - is view that I'm trying to rotate
    if(!(child instanceof TextBaloon)) {
        return super.drawChild(canvas, child, time);
    }

    final int width = child.getWidth();
    final int height = child.getHeight();

    final int left = child.getLeft();
    final int top = child.getTop();
    final int right = left + width;
    final int bottom = top + height;

    int restoreTo = canvas.save();

    canvas.translate(left, top);

    invalidate(left - width, top - height, right + width, bottom + height);
    child.draw(canvas);

    canvas.restoreToCount(restoreTo);

    return true;
}

主要思想是我授予子级非裁剪画布。

1
啊!我被抢劫了!那不是一个解决方案,那只是一个hack!嗯,好吧,解决它的过程很有趣。 - G. Blake Meike
@G.BlakeMeike 不算是黑客攻击:) 这就是drawChild方法的完整点 - 控制绘制。然而,你的回答最终将导致更加“正确”的解决方案,所以我会给你一份赏金) - Dmitry Zaytsev
非常感谢 Dmitry。结果发现需要重写的方法 TextView.makeNewLayout 来修复文本布局是受保护的,但被标记为 @hide。基于新限制重新换行文本会很困难。挺遗憾的。 - G. Blake Meike
等一下,我觉得我刚刚嘴里吐了。所以,如果你不是围绕中心点旋转 ImageView,它就会被裁剪。肯定有一个很好的理由,但它似乎很愚蠢。我不想覆盖 onDraw,添加填充或其他任何愚蠢的东西。来吧,安卓系统只需旋转位图并根据需要在父视图上移动/调整大小即可! - Batdude
我想旋转一个ImageView而不裁剪它,这是唯一对我有效的解决方案。太神奇了。Android应该更容易些。 - Ferran Maylinch

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接