Home » Android » java – Android TextView Long Shadow

java – Android TextView Long Shadow

Posted by: admin June 15, 2020 Leave a comment

Questions:

I’ve been trying this since morning, yet I can’t get it to work.

What I’m trying to do is create a somewhat like long shadow for the TextView, which is similar to the following:

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-720×400.png

My solution so far was to create a lot of TextViews and cascade them under each other, but there are a lot of performance issues if I go with the current way.

Another solution is the usage of a custom font that has that similar allure, yet I cannot find any that matches the font I am currently using.

So I was wondering, is it possible to use: (I have to mention, the textviews are created dynamically)

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

To create several of them in a line (as a cascading layer), making the shadow seem smooth? Or do you guys suggest any other solutions?

Thanks in advance.

How to&Answers:

Try to play with raster images:

  1. Detect bounds of text using Paint.getTextBounds() method
  2. Create transparent Bitmap with such metrics (W + H) x H (you may use Bitmap.Config.ALPHA_8 to optimize memory usage)
  3. Draw text on this Bitmap at 0x0 position
  4. Copy first row of Bitmap into new one with original width, but with height of 1px
  5. Iterate over the Y-axis of Bitmap (from top to bottom) and draw single-line Bitmap with the corresponding offset by X-axis (you will overdraw some transparent pixels)
  6. Now you have the top-part of your shadow
  7. Draw the bottom part using same technique, but choosing last row of this Bitmap

This algorithm may be optimized if you detect, that all pixels in last row have the same color (full shadow).

UPDATE 1

I achieved such result using this quick solution:

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

Here is final result which I achieved. Clone this demo from github.

enter image description here

Answer:

I’m afraid your suggested approach of using setShadowLayer() wont work as this approach effectively draws a second TextPaint with blurring.

Superimposing several TextPaints on top of each other will essentially mean you need to offset it by 1px for each step, which is very graphically intensive and will have a very poor performance.

This is an excellent question and a real challenge!

The only solution that comes to mind is to handle each glyph independently, inspecting all path elements and extending a shadow between the furthest bottom-left and top-right point. This seems very complicated, and I don’t know if there’s any mechanics in the SDK that facilitates an approach like that.

Suggested reading:

Answer:

Small comment if someone would try to run setText() method. it is not working now.
You should call invalidate(); in setText(); method

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

    mText = value;

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