Ответ 1
Я согласен с комментариями по вашему вопросу: программный эффект dropshadow - плохой выбор, и вы можете добиться такого же эффекта с помощью простого 9patch (или набора из них), как указано здесь.
Мне было слишком любопытно, и я закончил с взломом решения после работы.
Представленный код - это тест, и его следует рассматривать как простое доказательство концепции (так что, пожалуйста, не используйте downvote). Некоторые из показанных операций довольно дорогие и могут серьезно влиять на производительность (примеров много, посмотрите здесь, здесь, чтобы получить представление). Он должен быть последним решением только для компонента, показанного один раз в секунду.
public class BalloonView extends TextView {
protected NinePatchDrawable bg;
protected Paint paint;
protected Rect padding = new Rect();
protected Bitmap bmp;
public BalloonView(Context context) {
super(context);
init();
}
public BalloonView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BalloonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
@SuppressLint("NewApi")
protected void init() {
// decode the 9patch drawable
bg = (NinePatchDrawable) getResources().getDrawable(R.drawable.balloon);
// get paddings from the 9patch and apply them to the View
bg.getPadding(padding);
setPadding(padding.left, padding.top, padding.right, padding.bottom);
// prepare the Paint to use below
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.rgb(255,255,255));
paint.setStyle(Style.FILL);
// this check is needed in order to get this code
// working if target SDK>=11
if( Build.VERSION.SDK_INT >= 11 )
setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
// set the shadowLayer
paint.setShadowLayer(
padding.left * .2f, // radius
0f, // blurX
padding.left * .1f, // blurY
Color.argb(128, 0, 0, 0) // shadow color
);
}
@Override
protected void onDraw(Canvas canvas) {
int w = getMeasuredWidth();
int h = getMeasuredHeight();
// set 9patch bounds according to view measurement
// NOTE: if not set, the drawable will not be drawn
bg.setBounds(0, 0, w, h);
// this code looks expensive: let do once
if( bmp == null ) {
// it seems like shadowLayer doesn't take into account
// alpha channel in ARGB_8888 sources...
bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);
// draw the given 9patch on the brand new bitmap
Canvas tmp = new Canvas(bmp);
bg.draw(tmp);
// extract only the alpha channel
bmp = bmp.extractAlpha();
}
// this "alpha mask" has the same shape of the starting 9patch,
// but filled in white and **with the dropshadow**!!!!
canvas.drawBitmap(bmp, 0, 0, paint);
// let paint the 9patch over...
bg.draw(canvas);
super.onDraw(canvas);
}
}
Прежде всего, чтобы получить программную тень, вам нужно иметь дело с Paint.setShadowLayer(...)
, как указано здесь. В основном вы должны определить теневой слой для объекта Paint
, который используется для рисования на Canvas
вашего пользовательского представления. К сожалению, вы не можете использовать объект Paint
для рисования NinePatchDrawable, поэтому вам нужно преобразовать его в Bitmap (1-й взлом). Кроме того, кажется, что теневые слои не могут нормально работать с изображениями ARGB_8888, поэтому единственным способом, который я нашел для того, чтобы получить правильную тень, было рисование альфа-маски данного NinePatchDrawable (2-го взлома) чуть ниже самого себя.
Здесь пара sshots (протестирована на Android [email protected] и [email protected])
Изменить: только для того, чтобы быть тщательным, я прикрепил 9-патч, используемый в тесте (помещен в res/drawable/mdpi
)