2015-11-07 161 views
0

我使用android材質設計庫來製作這樣的標籤條。如何y偏移Android 5棒棒糖陰影方向

enter image description here

我在像這樣的屏幕的底部加入的圖。

enter image description here

現在,我想從頂部窗口小部件添加陰影效果的仰視圖。

我找到了答案爲this question的解決方案。

我發現創建頂部陰影的唯一方法是修改一些Android兼容性v7 CardView項目的源代碼。此項目將CardView類帶入較舊的Android版本,因此還包含高程陰影。由此產生的陰影非常接近「真實」高程陰影。

我遵循的指示,並且我用下面的結果結束了使用的圖示

enter image description here

我添加這些顏色到res/values/values.xml

<color name="cardview_shadow_end_color">#03000000</color> 
<color name="cardview_shadow_start_color">#47000000</color> 
<dimen name="cardview_compat_inset_shadow">1dp</dimen> 

我使用的類RoundRectDrawableWithShadow從修改的Android兼容性v7 CardView項目設置影子

這裏是我的體改CardView項目代碼RoundRectDrawableWithShadow

/* 
* Copyright (C) 2014 The Android Open Source Project 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 

import android.graphics.RectF; 
import android.content.res.Resources; 
import android.graphics.Canvas; 
import android.graphics.ColorFilter; 
import android.graphics.LinearGradient; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.PixelFormat; 
import android.graphics.RadialGradient; 
import android.graphics.Rect; 
import android.graphics.RectF; 
import android.graphics.Shader; 
import android.graphics.drawable.Drawable; 


/** 
* A rounded rectangle drawable which also includes a shadow around. 
*/ 
public class RoundRectDrawableWithShadow extends Drawable { 
    // used to calculate content padding 
    final static double COS_45 = Math.cos(Math.toRadians(45)); 

    final static float SHADOW_MULTIPLIER = 1.5f; 

    final int mInsetShadow; // extra shadow to avoid gaps between card and shadow 

    /* 
    * This helper is set by CardView implementations. 
    * <p> 
    * Prior to API 17, canvas.drawRoundRect is expensive; which is why we need this interface 
    * to draw efficient rounded rectangles before 17. 
    * */ 
    static RoundRectHelper sRoundRectHelper; 

    Paint mPaint; 

    Paint mCornerShadowPaint; 

    Paint mEdgeShadowPaint; 

    final RectF mCardBounds; 

    float mCornerRadius; 

    Path mCornerShadowPath; 

    // updated value with inset 
    float mMaxShadowSize; 

    // actual value set by developer 
    float mRawMaxShadowSize; 

    // multiplied value to account for shadow offset 
    float mShadowSize; 

    // actual value set by developer 
    float mRawShadowSize; 

    private boolean mDirty = true; 

    private final int mShadowStartColor; 

    private final int mShadowEndColor; 

    private boolean mAddPaddingForCorners = true; 

    /** 
    * If shadow size is set to a value above max shadow, we print a warning 
    */ 
    private boolean mPrintedShadowClipWarning = false; 

    public RoundRectDrawableWithShadow(
      Resources resources, int backgroundColor, float radius, 
      float shadowSize, float maxShadowSize 
    ) { 
     mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color); 
     mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color); 
     mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow); 
     mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 
     mPaint.setColor(backgroundColor); 
     mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); 
     mCornerShadowPaint.setStyle(Paint.Style.FILL); 
     mCornerRadius = (int) (radius + .5f); 
     mCardBounds = new RectF(); 
     mEdgeShadowPaint = new Paint(mCornerShadowPaint); 
     mEdgeShadowPaint.setAntiAlias(false); 
     setShadowSize(shadowSize, maxShadowSize); 

     RoundRectDrawableWithShadow.sRoundRectHelper 
       = new RoundRectDrawableWithShadow.RoundRectHelper() { 
      @Override 
      public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, 
             Paint paint) { 
       canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint); 
      } 
     }; 
    } 

    /** 
    * Casts the value to an even integer. 
    */ 
    private int toEven(float value) { 
     int i = (int) (value + .5f); 
     if (i % 2 == 1) { 
      return i - 1; 
     } 
     return i; 
    } 

    public void setAddPaddingForCorners(boolean addPaddingForCorners) { 
     mAddPaddingForCorners = addPaddingForCorners; 
     invalidateSelf(); 
    } 

    @Override 
    public void setAlpha(int alpha) { 
     mPaint.setAlpha(alpha); 
     mCornerShadowPaint.setAlpha(alpha); 
     mEdgeShadowPaint.setAlpha(alpha); 
    } 

    @Override 
    protected void onBoundsChange(Rect bounds) { 
     super.onBoundsChange(bounds); 
     mDirty = true; 
    } 

    void setShadowSize(float shadowSize, float maxShadowSize) { 
     if (shadowSize < 0 || maxShadowSize < 0) { 
      throw new IllegalArgumentException("invalid shadow size"); 
     } 
     shadowSize = toEven(shadowSize); 
     maxShadowSize = toEven(maxShadowSize); 
     if (shadowSize > maxShadowSize) { 
      shadowSize = maxShadowSize; 
      if (!mPrintedShadowClipWarning) { 
       mPrintedShadowClipWarning = true; 
      } 
     } 
     if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) { 
      return; 
     } 
     mRawShadowSize = shadowSize; 
     mRawMaxShadowSize = maxShadowSize; 
     mShadowSize = (int)(shadowSize * SHADOW_MULTIPLIER + mInsetShadow + .5f); 
     mMaxShadowSize = maxShadowSize + mInsetShadow; 
     mDirty = true; 
     invalidateSelf(); 
    } 

    @Override 
    public boolean getPadding(Rect padding) { 
     int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius, 
       mAddPaddingForCorners)); 
//  int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius, 
//    mAddPaddingForCorners)); 
//  padding.set(hOffset, vOffset, hOffset, vOffset); 
     padding.set(0, vOffset, 0, 0); 
     return true; 
    } 

    static float calculateVerticalPadding(float maxShadowSize, float cornerRadius, 
              boolean addPaddingForCorners) { 
     if (addPaddingForCorners) { 
      return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius); 
     } else { 
      return maxShadowSize * SHADOW_MULTIPLIER; 
     } 
    } 

    static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius, 
              boolean addPaddingForCorners) { 
     if (addPaddingForCorners) { 
      return (float) (maxShadowSize + (1 - COS_45) * cornerRadius); 
     } else { 
      return maxShadowSize; 
     } 
    } 

    @Override 
    public void setColorFilter(ColorFilter cf) { 
     mPaint.setColorFilter(cf); 
     mCornerShadowPaint.setColorFilter(cf); 
     mEdgeShadowPaint.setColorFilter(cf); 
    } 

    @Override 
    public int getOpacity() { 
     return PixelFormat.TRANSLUCENT; 
    } 

    void setCornerRadius(float radius) { 
     radius = (int) (radius + .5f); 
     if (mCornerRadius == radius) { 
      return; 
     } 
     mCornerRadius = radius; 
     mDirty = true; 
     invalidateSelf(); 
    } 

    @Override 
    public void draw(Canvas canvas) { 
     if (mDirty) { 
      buildComponents(getBounds()); 
      mDirty = false; 
     } 
     canvas.translate(0, -mRawShadowSize/2); 
     drawShadow(canvas); 
     canvas.translate(0, +mRawShadowSize/2); 
     sRoundRectHelper.drawRoundRect(canvas, mCardBounds, mCornerRadius, mPaint); 
    } 

    private void drawShadow(Canvas canvas) { 
     final float edgeShadowTop = -mCornerRadius - mShadowSize; 
     final float insetVertical = mCornerRadius + mInsetShadow + mRawShadowSize/2; 
     final float insetHorizontal = -mInsetShadow; 
     // LT top 
     int saved = canvas.save(); 
     canvas.translate(mCardBounds.left + insetHorizontal, mCardBounds.top + insetVertical); 
     canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); 
     canvas.drawRect(0, edgeShadowTop, 
       mCardBounds.width() - 2 * insetHorizontal, -mCornerRadius + mShadowSize, 
       mEdgeShadowPaint); 
     canvas.restoreToCount(saved); 

     // RT right 
     saved = canvas.save(); 
     canvas.translate(mCardBounds.right - insetHorizontal, mCardBounds.top + insetVertical); 
     canvas.rotate(90f); 
     canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); 
     canvas.restoreToCount(saved); 
    } 

    private void buildShadowCorners() { 
     RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius); 
     RectF outerBounds = new RectF(innerBounds); 
     outerBounds.inset(-mShadowSize, -mShadowSize); 

     if (mCornerShadowPath == null) { 
      mCornerShadowPath = new Path(); 
     } else { 
      mCornerShadowPath.reset(); 
     } 
     mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD); 
     mCornerShadowPath.moveTo(-mCornerRadius, 0); 
     mCornerShadowPath.rLineTo(-mShadowSize, 0); 
     // outer arc 
     mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false); 
     // inner arc 
     mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false); 
     mCornerShadowPath.close(); 
     float startRatio = mCornerRadius/(mCornerRadius + mShadowSize); 
     mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize, 
       new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor}, 
       new float[]{0f, startRatio, 1f} 
       , Shader.TileMode.CLAMP)); 

     // we offset the content shadowSize/2 pixels up to make it more realistic. 
     // this is why edge shadow shader has some extra space 
     // When drawing bottom edge shadow, we use that extra space. 
     mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0, 
       -mCornerRadius - mShadowSize, 
       new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor}, 
       new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP)); 
     mEdgeShadowPaint.setAntiAlias(false); 
    } 

    private void buildComponents(Rect bounds) { 
     // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift. 
     // We could have different top-bottom offsets to avoid extra gap above but in that case 
     // center aligning Views inside the CardView would be problematic. 
     final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER; 
     mCardBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset, 
       bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset); 
     buildShadowCorners(); 
    } 

    float getCornerRadius() { 
     return mCornerRadius; 
    } 

    void getMaxShadowAndCornerPadding(Rect into) { 
     getPadding(into); 
    } 

    void setShadowSize(float size) { 
     setShadowSize(size, mRawMaxShadowSize); 
    } 

    void setMaxShadowSize(float size) { 
     setShadowSize(mRawShadowSize, size); 
    } 

    float getShadowSize() { 
     return mRawShadowSize; 
    } 

    float getMaxShadowSize() { 
     return mRawMaxShadowSize; 
    } 

    float getMinWidth() { 
     final float content = 2 * 
       Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize/2); 
     return content + (mRawMaxShadowSize + mInsetShadow) * 2; 
    } 

    float getMinHeight() { 
     final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow 
       + mRawMaxShadowSize * SHADOW_MULTIPLIER/2); 
     return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2; 
    } 

    public void setColor(int color) { 
     mPaint.setColor(color); 
     invalidateSelf(); 
    } 

    static interface RoundRectHelper { 
     void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint); 
    } 
} 

回答

0

所以,問題是,我是畫奧雅納整個視圖的矩形。矩形需要在專用於陰影效果的單獨視圖上繪製。

此外,陰影視圖的高度不應超過6dp,否則矩形將顯示,如果有意義的話。

最後一個音符,使用這些值,密度和高度

float elevation = 2; 
float density = getResources().getDisplayMetrics().density; 

這裏是我的佈局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    tools:context="info.androidhive.materialtabs.fragments.OneFragment"> 

    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:text="This Is A Fragment" 
     android:gravity="center" 
     android:textSize="40dp" 
     android:textStyle="bold" 
     android:layout_centerInParent="true" 
     android:layout_above="@+id/bottomView"/> 


    <View 
     android:id="@+id/bottomView" 
     android:layout_width="match_parent" 
     android:layout_height="6dp" 
     android:layout_above="@+id/other"/> 

    <View 
     android:id="@id/other" 
     android:layout_alignParentBottom="true" 
     android:layout_width="match_parent" 
     android:layout_height="175dp" 
     android:background="?attr/colorPrimary"/> 

</RelativeLayout> 
+1

密度設備相關的,你應該使用這樣的:浮體密度= getResources()。 。getDisplayMetrics()密度; – mdiener

+0

也許這只是一種印象,但是陰影看起來比標籤帶陰影更暗一些。 –