working studio not example android material-design appcompat android-elevation

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:

  1. Dibuja una forma negra de una vista en un mapa de bits
  2. 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.
  3. Dibuja esa forma borrosa debajo de la vista. Probablemente el mejor lugar es el método drawChild . También tiene que anular setElevation y setTranslationZ , 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