2014-10-12 11 views
8

Próbuję tego od rana, ale nie mogę go uruchomić.Android TextView Long Shadow

Co staram się zrobić, to stworzyć trochę jak długim cieniem na TextView, który jest podobny do następującego:

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

Moje rozwiązanie do tej pory było stworzenie dużo Wyświetlaj tekst i kaskaduj je pod sobą, ale jeśli przejdę w aktualny sposób, wystąpią problemy z wydajnością.

Innym rozwiązaniem jest użycie niestandardowej czcionki, która ma podobny urok, ale nie mogę znaleźć żadnego pasującego do czcionki, której aktualnie używam.

Więc zastanawiałem się, czy to możliwe, aby użyć: (muszę wspomnieć, że TextView są tworzone dynamicznie)

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

Aby utworzyć kilka z nich w jednej linii (jako warstwa kaskadowym), co sprawia, cień wydaje się gładki? A może sugerujesz jakieś inne rozwiązania?

Z góry dziękuję.

Odpowiedz

12

spróbować zagrać z obrazów rastrowych:

  1. Detect granice tekstu przy użyciu Paint.getTextBounds() metodę
  2. Tworzenie przejrzystej Bitmap z takich metryk (W + H) x H (można użyć Bitmap.Config.ALPHA_8 zoptymalizować zużycie pamięci)
  3. Draw tekst na tej Bitmap na 0x0 pozycji
  4. Skopiuj pierwszy rząd Bitmap na nowy o pierwotnej szerokości, lecz o wysokości 1px
  5. iteracyjnego Y-axis z Bitmap (od góry do dołu) i wyciągnąć pojedynczą linię Bitmap z odpowiadającymi skompensowane X-axis (można overdraw kilka przezroczystych pikseli)
  6. teraz masz top-część swojego cienia
  7. Narysuj dolną część za pomocą tej samej techniki, ale wybierając ostatni wiersz tego Bitmap

Ten al Gorithm może być zoptymalizowany, jeśli wykryjesz, że wszystkie piksele w ostatnim wierszu mają ten sam kolor (pełny cień).

aktualizacji 1

że taki wynik uzyskuje się za pomocą tego szybkiego rozwiązania:

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

UPDATE 2

Oto wynik końcowy której osiągnięty. Klon this demo z github.

enter image description here

+0

, więc to, co tu robimy, to rysowanie cienia w bitmapie bez konieczności "podglądania" każdego tekstu, a następnie pokazywania bitmapy, prawda? – John

+0

dokładnie! sprawdź moje aktualizacje, jest to bardzo gorące rozwiązanie. teraz wszystko, czego potrzebujesz - wystarczy skopiować ostatni surowiec z 'mBitmap' wewnątrz metody' onDraw() 'i narysować oryginalny tekst powyżej cienia. –

+0

Mam na myśli ostatni rząd (zamiast * surowego *) –

1

Obawiam się, że sugerowane podejście polegające na użyciu setShadowLayer() nie będzie działać, ponieważ to podejście skutecznie narysuje drugi obiekt TextPaint z rozmyciem.

Nakładanie kilku elementów TextPaints na siebie nawzajem będzie oznaczać, że musisz zrównoważyć je o 1 piksel za każdy krok, co jest bardzo intensywne graficznie i będzie miało bardzo niską wydajność.

To jest doskonałe pytanie i prawdziwe wyzwanie!

Jedynym rozwiązaniem, które przychodzi na myśl jest samodzielne obsługiwanie każdego glifu, kontrola wszystkich elementów ścieżki i rozszerzenie cienia między najdalszym lewym dolnym i górnym prawym punktem. Wydaje się to bardzo skomplikowane i nie wiem, czy w SDK jest jakaś mechanika, która ułatwia takie podejście.

Zalecane lektury:

+0

Dziękuję za tę odpowiedź, ale czy możesz wyjaśnić, w jaki sposób sprawiłoby to, że mogłem usunąć cień? Czy musiałbym zmienić ścieżkę glifu w nieco niestandardowy kierunek, a następnie narysować go na mapie bitowej? Mam trochę trudności z zrozumieniem tego. – John

+0

Poradziłbym sobie z tym za pomocą niestandardowego widoku, rysując każdy glif jako ścieżkę i sprawdzając Ścieżkę każdej postaci, aby narysować wydłużony cień poza nią przy użyciu innej farby. Wydaje się to jednak raczej trudnym zadaniem. –

+0

Każdy pomysł, jeśli Twój sposób będzie wymagał dużej wydajności do wykonania? Spodziewam się, że zrobi to na ponad 5+ TextViews.Byłoby również bardzo pomocne, gdybyś mógł podać mi przykład, ponieważ nie do końca rozumiałam, jak działają mechanizmy. Dzięki jeszcze raz! – John

0

Mała uwaga, jeśli ktoś będzie próbował uruchomić metodę setText(). teraz nie działa. Powinieneś wywołać invalidate(); w setText(); metoda

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

    mText = value; 

    if (changed) { 
     refresh(); 
    } 
    invalidate(); 
}