android - studio - Cómo implementar la elevación del material de diseño para pre-piruleta
shape shadow android (7)
Crea una imagen de 9-patch con parches estirables definidos en una imagen con sombras a su alrededor.
Agregue esta imagen de 9 parches como fondo de su botón con un relleno para que la sombra sea visible.
Puede encontrar algunas imágenes predefinidas de 9 parches (.9.png) here o here desde donde puede seleccionar, personalizar y copiar al dibujable de su proyecto.
Google ha mostrado algunas buenas formas en que el efecto de elevación se muestra en Lollipop here .
android:elevation="2dp"
para botones,
android:stateListAnimator="@anim/button_state_list_animator"
¿Cómo puedo imitar el efecto de elevación en versiones anteriores a Lollipop sin una biblioteca de terceros?
No puedes imitar la elevación en pre-Lollipop con un método oficial.
Puede usar algunos elementos dibujables para hacer la sombra en su componente. Google lo usa de esta manera en CardView, por ejemplo.
ViewCompat.setElevation(View, int)
crea actualmente la sombra solo en API21 +. Si comprueba el código que está detrás, este método llama:
API 21+:
@Override
public void setElevation(View view, float elevation) {
ViewCompatLollipop.setElevation(view, elevation);
}
API <21
@Override
public void setElevation(View view, float elevation) {
}
Para brindar sombras dinámicas y animadas a los dispositivos anteriores a Lollipop, debes:
- Dibuja una forma negra de una vista en un mapa de bits
- Desenfoque esa forma usando la elevación como un radio. Puedes hacerlo usando RenderScript. No es exactamente el método que usa Lollipop, pero ofrece buenos resultados y es fácil de agregar a vistas existentes.
- Dibuja esa forma borrosa debajo de la vista. Probablemente el mejor lugar es el método
drawChild
. También tiene que anularsetElevation
ysetTranslationZ
, anular el dibujo de vista infantil en diseños, desactivar clip-to-padding e implementar animadores de estado.
Eso es mucho trabajo, pero ofrece las sombras dinámicas más atractivas con animaciones de respuesta. No estoy seguro de por qué le gustaría lograr eso sin bibliotecas de terceros. Si lo desea, puede analizar las fuentes de carbono y portar las partes que le gustaría tener en su aplicación:
Generación de sombras
private static void blurRenderScript(Bitmap bitmap, float radius) {
Allocation inAllocation = Allocation.createFromBitmap(renderScript, bitmap,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
Allocation outAllocation = Allocation.createTyped(renderScript, inAllocation.getType());
blurShader.setRadius(radius);
blurShader.setInput(inAllocation);
blurShader.forEach(outAllocation);
outAllocation.copyTo(bitmap);
}
public static Shadow generateShadow(View view, float elevation) {
if (!software && renderScript == null) {
try {
renderScript = RenderScript.create(view.getContext());
blurShader = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
} catch (RSRuntimeException ignore) {
software = true;
}
}
ShadowView shadowView = (ShadowView) view;
CornerView cornerView = (CornerView) view;
boolean isRect = shadowView.getShadowShape() == ShadowShape.RECT ||
shadowView.getShadowShape() == ShadowShape.ROUND_RECT && cornerView.getCornerRadius() < view.getContext().getResources().getDimension(R.dimen.carbon_1dip) * 2.5;
int e = (int) Math.ceil(elevation);
Bitmap bitmap;
if (isRect) {
bitmap = Bitmap.createBitmap(e * 4 + 1, e * 4 + 1, Bitmap.Config.ARGB_8888);
Canvas shadowCanvas = new Canvas(bitmap);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0xff000000);
shadowCanvas.drawRect(e, e, e * 3 + 1, e * 3 + 1, paint);
blur(bitmap, elevation);
return new NinePatchShadow(bitmap, elevation);
} else {
bitmap = Bitmap.createBitmap((int) (view.getWidth() / SHADOW_SCALE + e * 2), (int) (view.getHeight() / SHADOW_SCALE + e * 2), Bitmap.Config.ARGB_8888);
Canvas shadowCanvas = new Canvas(bitmap);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0xff000000);
if (shadowView.getShadowShape() == ShadowShape.ROUND_RECT) {
roundRect.set(e, e, (int) (view.getWidth() / SHADOW_SCALE - e), (int) (view.getHeight() / SHADOW_SCALE - e));
shadowCanvas.drawRoundRect(roundRect, e, e, paint);
} else {
int r = (int) (view.getWidth() / 2 / SHADOW_SCALE);
shadowCanvas.drawCircle(r + e, r + e, r, paint);
}
blur(bitmap, elevation);
return new Shadow(bitmap, elevation);
}
}
Dibujando una vista con una sombra
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (!child.isShown())
return super.drawChild(canvas, child, drawingTime);
if (!isInEditMode() && child instanceof ShadowView && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) {
ShadowView shadowView = (ShadowView) child;
Shadow shadow = shadowView.getShadow();
if (shadow != null) {
paint.setAlpha((int) (ShadowGenerator.ALPHA * ViewHelper.getAlpha(child)));
float childElevation = shadowView.getElevation() + shadowView.getTranslationZ();
float[] childLocation = new float[]{(child.getLeft() + child.getRight()) / 2, (child.getTop() + child.getBottom()) / 2};
Matrix matrix = carbon.internal.ViewHelper.getMatrix(child);
matrix.mapPoints(childLocation);
int[] location = new int[2];
getLocationOnScreen(location);
float x = childLocation[0] + location[0];
float y = childLocation[1] + location[1];
x -= getRootView().getWidth() / 2;
y += getRootView().getHeight() / 2; // looks nice
float length = (float) Math.sqrt(x * x + y * y);
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(
x / length * childElevation / 2,
y / length * childElevation / 2);
canvas.translate(
child.getLeft(),
child.getTop());
canvas.concat(matrix);
canvas.scale(ShadowGenerator.SHADOW_SCALE, ShadowGenerator.SHADOW_SCALE);
shadow.draw(canvas, child, paint);
canvas.restoreToCount(saveCount);
}
}
if (child instanceof RippleView) {
RippleView rippleView = (RippleView) child;
RippleDrawable rippleDrawable = rippleView.getRippleDrawable();
if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless) {
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(
child.getLeft(),
child.getTop());
rippleDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
return super.drawChild(canvas, child, drawingTime);
}
Elevation API backported a pre-Lollipop
private float elevation = 0;
private float translationZ = 0;
private Shadow shadow;
@Override
public float getElevation() {
return elevation;
}
public synchronized void setElevation(float elevation) {
if (elevation == this.elevation)
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
super.setElevation(elevation);
this.elevation = elevation;
if (getParent() != null)
((View) getParent()).postInvalidate();
}
@Override
public float getTranslationZ() {
return translationZ;
}
public synchronized void setTranslationZ(float translationZ) {
if (translationZ == this.translationZ)
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
super.setTranslationZ(translationZ);
this.translationZ = translationZ;
if (getParent() != null)
((View) getParent()).postInvalidate();
}
@Override
public ShadowShape getShadowShape() {
if (cornerRadius == getWidth() / 2 && getWidth() == getHeight())
return ShadowShape.CIRCLE;
if (cornerRadius > 0)
return ShadowShape.ROUND_RECT;
return ShadowShape.RECT;
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
setTranslationZ(enabled ? 0 : -elevation);
}
@Override
public Shadow getShadow() {
float elevation = getElevation() + getTranslationZ();
if (elevation >= 0.01f && getWidth() > 0 && getHeight() > 0) {
if (shadow == null || shadow.elevation != elevation)
shadow = ShadowGenerator.generateShadow(this, elevation);
return shadow;
}
return null;
}
@Override
public void invalidateShadow() {
shadow = null;
if (getParent() != null && getParent() instanceof View)
((View) getParent()).postInvalidate();
}
Puede hackearlo usando una vista de tarjeta:
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/btnGetStuff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp"
card_view:cardBackgroundColor="@color/accent"
>
<!-- you could also add image view here for icon etc. -->
<TextView
android:id="@+id/txtGetStuff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textSize_small"
android:textColor="@color/primary_light"
android:freezesText="true"
android:text="Get Stuff"
android:maxWidth="120dp"
android:singleLine="true"
android:ellipsize="end"
android:maxLines="1"
/></android.support.v7.widget.CardView>
O consulte el uso de esta biblioteca de terceros: https://github.com/rey5137/Material (consulte el artículo de la wiki sobre el botón https://github.com/rey5137/Material/wiki/Button )
Puedes simularlo fácilmente declarando un dibujable como este:
shadow.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
>
<gradient android:type="linear" android:angle="270" android:startColor="#b6b6b6" android:endColor="#ffffff"/>
</shape>
y úsalo int ur main xml como -
android:background="@drawable/shadow"
para agregar la respuesta de @Ranjith Kumar
Para agregar color de fondo al dibujable (color de fondo del botón de ejemplo), necesitamos obtener dibujable programáticamente.
primero obtener el dibujable
Drawable drawable = getResources().getDrawable(android.R.drawable.dialog_holo_light_frame);
establecer el color
drawable.setColorFilter(new PorterDuffColorFilter(getResources().getColor(R.color.color_primary), PorterDuff.Mode.MULTIPLY));
luego configúralo a la vista.
view.setBackgroundDrawable(drawable);
en caso de que alguien busque.
Puedes imitar la elevación en pre-Lollipop con un método oficial.
Logré el mismo efecto usando,
android:background="@android:drawable/dialog_holo_light_frame"
Mi resultado probado:
referencia - https://.com/a/25683148/3879847
Gracias al usuario @Repo ..
Actualización: si quieres cambiar el color de esta dibujable prueba la respuesta @Irfan a continuación ↓
https://.com/a/40815944/3879847