Пробую это с утра, но не могу заставить работать.

Я пытаюсь создать что-то вроде длинной тени для TextView, которая похожа на следующее:

http://www.iceflowstudios.com/v3/wp-content/uploads/2013/07/long_shadow_banner.jpg http://web3canvas.com/wp-content/uploads/2013/07/lsd-ps-action-720x400.png

Мое решение до сих пор заключалось в том, чтобы создать множество TextView и каскадировать их друг под другом, но есть много проблем с производительностью, если я буду придерживаться текущего пути.

Другое решение - использование специального шрифта, который имеет такое же очарование, но я не могу найти ни одного шрифта, который соответствует шрифту, который я использую в настоящее время.

Поэтому мне было интересно, можно ли использовать: (я должен упомянуть, что текстовые представления создаются динамически)

TV.setShadowLayer(1f, 5f, 5f, Color.GREY);

Чтобы создать несколько из них в линию (как каскадный слой), чтобы тень казалась гладкой? Или вы, ребята, предлагаете какие-нибудь другие решения?

Заранее спасибо.

9
Johnaudi 12 Окт 2014 в 18:20

3 ответа

Лучший ответ

Попробуйте поиграть с растровыми изображениями:

  1. Определите границы текста с помощью Paint.getTextBounds() метод
  2. Создайте прозрачный Bitmap с такими показателями (W + H) x H (вы можете использовать Bitmap.Config.ALPHA_8 для оптимизации использования памяти)
  3. Нарисуйте текст на этом Bitmap в позиции 0x0
  4. Скопируйте первую строку Bitmap в новую с исходной шириной, но с высотой 1px
  5. Обходите Y-axis из Bitmap (сверху вниз) и нарисуйте однострочный Bitmap с соответствующим смещением на X-axis (некоторые прозрачные пиксели будут перекрыты)
  6. Теперь у вас есть верхняя часть вашей тени
  7. Нарисуйте нижнюю часть, используя ту же технику, но выбрав последний ряд этого Bitmap

Этот алгоритм можно оптимизировать, если вы обнаружите, что все пиксели в последней строке имеют одинаковый цвет (полная тень).

ОБНОВЛЕНИЕ 1

Такого результата я добился с помощью этого быстрого решения:

enter image description here

MainActivity.java

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);

        LongShadowTextView longShadow = new LongShadowTextView(this);
        longShadow.setText("Hello World");
        setContentView(longShadow);
    }
}

LongShadowTextView.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.View;

public class LongShadowTextView extends View {
    private Bitmap mBitmap;
    private String mText;

    public LongShadowTextView(Context context) {
        super(context);
    }

    public void setText(String text) {
        Paint paint = new Paint();
        // TODO provide setters for these values
        paint.setColor(Color.BLACK);
        paint.setTextSize(142);

        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);

        Bitmap bitmap = Bitmap.createBitmap(rect.width() + rect.height(), rect.height(), Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(bitmap);

        canvas.drawText(text, 0, rect.height(), paint);

        Rect src = new Rect();
        RectF dst = new RectF();

        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        src.left = 0;
        src.right = w;

        for (int i = 0; i < h; ++i) {
            src.top = i;
            src.bottom = i + 1;

            dst.left = 1;
            dst.top = i + 1;
            dst.right = 1 + w;
            dst.bottom = i + 2;

            canvas.drawBitmap(bitmap, src, dst, null);
        }

        mText = text;
        mBitmap = bitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, 0, 0, null);
    }
}

ОБНОВЛЕНИЕ 2

Вот окончательный результат, которого я добился. Скопируйте эту демонстрацию из github.

enter image description here

12
Oleksii K. 17 Окт 2014 в 01:39
Итак, что мы здесь делаем, так это рисуем тень в растровом изображении, не позволяя ему «просматривать» каждое текстовое представление за раз, а затем показываем только растровое изображение, верно?
 – 
Johnaudi
16 Окт 2014 в 19:22
В точку! проверьте мои обновления, это очень горячее решение. теперь все, что вам нужно - просто скопировать последний ряд из mBitmap внутрь метода onDraw() и нарисовать исходный текст над тенью.
 – 
Oleksii K.
16 Окт 2014 в 19:28
Я имею в виду последнюю строку (вместо raw)
 – 
Oleksii K.
16 Окт 2014 в 20:40
Я еще не проверял это, но я собираюсь наградить вас за то, что вы приложили столько труда и усилий, спасибо!
 – 
Johnaudi
16 Окт 2014 в 21:23
2
Привет, я полностью решил твою задачу! проверьте эту демонстрацию: github.com/shamanland/longshadowtextview
 – 
Oleksii K.
17 Окт 2014 в 01:36

Небольшой комментарий, если кто-то попытается запустить метод setText (). сейчас он не работает. Вы должны вызвать invalidate (); в setText (); метод

 public void setText(String value) {
    boolean changed = 
       mText == null && value != null || mText != null && !mText.equals(value);

    mText = value;

    if (changed) {
        refresh();
    }
    invalidate();
}
0
rurik 3 Янв 2015 в 22:37

Боюсь, что предложенный вами подход к использованию setShadowLayer() не сработает, поскольку этот подход эффективно рисует второй TextPaint с размытием.

Наложение нескольких TextPaints друг на друга будет означать, что вам нужно смещать их на 1 пиксель для каждого шага, что очень графически интенсивно и будет иметь очень низкую производительность.

Это отличный вопрос и настоящий вызов!

Единственное решение, которое приходит на ум, - обрабатывать каждый глиф независимо, проверяя все элементы контура и увеличивая тень между самой дальней нижней левой и верхней правой точкой. Это кажется очень сложным, и я не знаю, есть ли в SDK какие-либо механизмы, облегчающие такой подход.

Предлагаемое чтение:

0
Community 23 Май 2017 в 15:17
Спасибо за этот ответ, но не могли бы вы уточнить, как это позволит мне отрисовывать тень? Должен ли я изменить путь глифа на несколько произвольное направление, а затем нарисовать его на растровом изображении? Я как бы с трудом понимаю это.
 – 
Johnaudi
12 Окт 2014 в 19:12
Я бы решил это, используя пользовательский вид, рисуя каждый глиф как путь и проверяя путь каждого символа, чтобы нарисовать расширенную тень за ним, используя другую краску. Однако это кажется довольно сложной задачей.
 – 
Paul Lammertsma
12 Окт 2014 в 19:21
Любая идея, если ваш способ потребует много производительности для выполнения? Я ожидаю, что это будет сделано на более чем 5 TextViews. Кроме того, было бы очень полезно, если бы вы могли предоставить мне пример, поскольку я не совсем понял, как будет работать механика. Еще раз спасибо!
 – 
Johnaudi
12 Окт 2014 в 19:24
Покопавшись в этом немного глубже, кажется, что в Android нет Java-реализации рисования глифов. Код, используемый в TextView, вызывает используемый макет, который, в свою очередь, вызывает TextLine, который использует Canvas.drawTextRun() для рисования текста с помощью собственного кода.
 – 
Paul Lammertsma
12 Окт 2014 в 20:20
Я боюсь, что в SDK нет реализации Java для получения пути из глифа, поэтому использование этого подхода потребует огромного объема работы. Вам придется интерпретировать TTF самостоятельно, создавая путь для каждого глифа и рисуя его на холсте, используя drawPath(). Для расширения тени вам нужно будет перемещаться по пути и определять границы каждого закрытого пути, чтобы выяснить, как расширить его внутри представления.
 – 
Paul Lammertsma
12 Окт 2014 в 20:22