簡體   English   中英

如何實現前棒棒糖的材料設計高程

[英]How to implement the Material-design Elevation for Pre-lollipop

谷歌已經表明,海拔效果顯示在棒棒糖一些不錯的方式在這里

android:elevation="2dp"

按鈕,

android:stateListAnimator="@anim/button_state_list_animator"

如何在沒有第三方庫的情況下模仿前Lollipop版本的高程效果?

您可以使用官方方法模仿前Lollipop上的高程。

我用相同的效果,

  android:background="@android:drawable/dialog_holo_light_frame"

我測試的輸出:

在此輸入圖像描述

參考 - https://stackoverflow.com/a/25683148/3879847

感謝用戶@Repo ..

更新:如果你想改變這個drawable的顏色,請嘗試@Irfan回答↓

https://stackoverflow.com/a/40815944/3879847

您無法使用官方方法模仿前Lollipop上的高程。

您可以使用一些drawable來在組件中創建陰影。 例如,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) {

}

你可以使用卡片視圖來破解它:

<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上的wiki文章)

要為Lollipop之前的設備帶來動態的動畫陰影,您必須:

  1. 將視圖的黑色形狀繪制為位圖
  2. 使用高程作為半徑模糊該形狀。 您可以使用RenderScript執行此操作。 它並不完全是Lollipop使用的方法,但可以提供良好的結果,並且很容易添加到現有視圖中。
  3. 在視圖下方繪制模糊的形狀。 可能最好的地方是drawChild方法。 您還必須覆蓋setElevationsetTranslationZ ,覆蓋布局中的子視圖繪制,關閉剪輯到填充並實現狀態動畫師。

在此輸入圖像描述

這是很多工作,但它提供了具有響應動畫的最佳外觀動態陰影。 如果沒有第三方庫,我不確定你為什么要這樣做。 如果您願意,您可以分析碳的來源並移植您想要在應用中使用的部分:

影子生成

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);
}

Elevation API向后移植到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();
}

使用在其周圍帶陰影的圖像上定義的可伸縮貼片創建9補丁圖像。

在此輸入圖像描述

使用填充添加此9補丁圖像作為按鈕的背景,以便陰影可見。

您可以在此處此處找到一些預定義的9-patch(.9.png)圖像,您可以從中選擇,自定義並復制到項目的可繪制圖像。

添加@Ranjith Kumar的答案

要為drawable添加背景顏色(示例按鈕背景顏色),我們需要以編程方式獲取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);

以防任何人搜索。

你可以通過宣布像這樣的drawable來輕松模擬它 -

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>

並使用它在你的主要xml像 -

 android:background="@drawable/shadow"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM