Как повернуть TextView без ограничения его границ?

Я пытаюсь повернуть свой подкласс TextView с помощью canvas.rotate():

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

Я понимаю, что это из-за моего размера представления - он не изменяется во время вращения, пока он должен. Но если я изменю width\height в onMeasure, проблема останется - я использую LayoutParams.WRAP_CONTENT, поэтому TextView просто измените его размер в соответствии со значениями, указанными в setMeasuredDimensions.

Как я могу решить эту проблему?

Ответ 1

В первом взгляде, предложенном Г. Блейком Майком, это хороший способ начать работу, однако есть много проблем.

Для преодоления проблем существует другой путь. Это довольно просто, но вообще не следует использовать (поскольку я только что удалил ненужные для меня вещи из исходного источника Android). Чтобы взять контроль над рисованием бетона View, вы можете переопределить его метод 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;
}

Основная идея заключается в том, что я предоставляю не обрезанный холст для ребенка.

Ответ 2

Я думаю, что вся проблема здесь в том, что вы используете WRAP_CONTENT. Прямоугольник клипа для представления, когда вы это делаете, - это размер текстового содержимого. Самый простой способ исправить проблему - использовать прокладку. Что-то вроде этого, отлично работает для меня:

<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);
}

Единственная оставшаяся проблема заключается в том, что текст обернут в соответствии с размерами перед вращением. Вам также придется исправить это...

sinA и cosA вычисляются при установке mAngle.

Ответ 3

Хорошо, если вы не можете поворачивать textView в своем текущем иерархии представлений, вы должны анимировать его в другом.

Создайте растровое представление вашего textView. Используйте держатель для этого растрового изображения, такого как ImageView. Подключите изображениеView к другому макету, который находится сверху и где он не будет обрезан. Скройте свой текст. Запустите анимацию в ImageView. Когда анимация закончена, обновите представление textView, покажите textView и удалите ImageView.

Я не могу найти точный пример кода того, что вы хотите, но посмотрите этот. Взаимодействующее место:

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

Ответ 4

Вы можете проверить https://github.com/grantland/android-autofittextview Особенно его метод refitText().

Поворот вашего Textview должен последовать за вызовом метода onMeasure(), где начинается все волшебство.