Как реализовать Elevation Material-Design для Pre-lollipop
Google показал несколько хороших способов, что эффект возвышения отображается на Lollipop здесь.
android:elevation="2dp"
для кнопок,
android:stateListAnimator="@anim/button_state_list_animator"
Как я могу имитировать эффект возвышения для версий до Lollipop без сторонней библиотеки?
Ответы
Ответ 1
Вы можете имитировать возвышение на пред-Lollipop официальным методом.
Я получаю тот же эффект, используя
android:background="@android:drawable/dialog_holo_light_frame"
Мой тестовый результат:
![enter image description here]()
ссылка - fooobar.com/questions/45818/...
Благодаря пользователю @Repo..
Обновление: Если вы хотите изменить цвет этого рисованного запроса @Irfan ниже ↓
fooobar.com/questions/45822/...
Ответ 2
Вы не можете имитировать возвышение на пред-Lollipop официальным методом.
Вы можете использовать некоторые чертежи для создания тени в своем компоненте. Google использует этот способ в CardView, например.
ViewCompat.setElevation(View, int)
в настоящее время создает тень только на API21+. Если вы проверите код позади, этот метод вызывает:
API 21 +:
@Override
public void setElevation(View view, float elevation) {
ViewCompatLollipop.setElevation(view, elevation);
}
API < 21
@Override
public void setElevation(View view, float elevation) {
}
Ответ 3
Вы можете взломать его, используя вид карты:
<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>
Или посмотрите на эту стороннюю библиотеку: https://github.com/rey5137/Material (см. статью в вики на кнопке https://github.com/rey5137/Material/wiki/Button)
Ответ 4
Создайте 9-patch с растягиваемыми патчами, определенными на изображении с тенью вокруг него.
![enter image description here]()
Добавьте это изображение с 9 патчами в качестве фона вашей кнопки с отступом, чтобы тень была видна.
Вы можете найти некоторые предварительно определенные 9-патча (.9.png) изображения здесь или здесь, из которого вы можете выбрать, настроить и скопировать в свой проект.
Ответ 5
Чтобы принести динамические, анимированные тени к устройствам до Lollipop, вам необходимо:
- Нарисуйте черную форму представления растрового изображения
- Размытие этой формы с использованием высоты в радиусе. Вы можете сделать это с помощью RenderScript. Это не тот метод, который использует Lollipop, но дает хорошие результаты и легко добавляется к существующим представлениям.
- Нарисуйте эту размытую форму под видом. Вероятно, лучшим местом является метод
drawChild
.
Вы также должны переопределить setElevation
и setTranslationZ
, переопределить чертеж дочернего представления в макетах, отключить клип-дополнение и реализовать аниматоры состояния.
![enter image description here]()
Это большая работа, но она дает лучшие, динамичные тени с анимацией ответа. Я не уверен, почему вы хотели бы добиться этого без сторонних библиотек. Если вы хотите, вы можете проанализировать источники Carbon и портировать части, которые вы хотели бы иметь в своем приложении:
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);
}
}
@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);
}
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();
}
Ответ 6
чтобы добавить ответ @Ranjith Kumar
Чтобы добавить фоновый цвет к выделенному (например, цвет фона кнопки), нам нужно сделать программно запрограммированным.
сначала получим drawable
Drawable drawable = getResources().getDrawable(android.R.drawable.dialog_holo_light_frame);
установите цвет
drawable.setColorFilter(new PorterDuffColorFilter(getResources().getColor(R.color.color_primary), PorterDuff.Mode.MULTIPLY));
затем установите его в представление.
view.setBackgroundDrawable(drawable);
в случае поиска.
Ответ 7
u может легко имитировать его, объявив его таким, как это:
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>
и использовать его int ur main xml like -
android:background="@drawable/shadow"