LoginSignup
7
8

More than 5 years have passed since last update.

Lolipop より前のバージョンでカスタムビューに影をつける

Last updated at Posted at 2014-12-24

今現在 Lolipop より前のバージョンで開発を行ってるとsetElevationが反映されません。
フラットですが、随分とノッペリとしたデザインになってしまいますね。

noshadow_1.jpg

View に影を付ける方法は、ここに記載されている様に 9-patch や shape を使う方法がありますが、中でもカスタムビュー内でonDrawをオーバーライドする方法が一番手頃だと思います。

上記サイトでも紹介されている Shadow.java は自分の影の描画を行うのですが、描画順によっては折角描画した影が上書きされてしまいました。

なので、描画が後の View が前の View の影を描画する必要があります。

shadows.java
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.util.SparseArray;
import android.view.View;

import static android.graphics.drawable.GradientDrawable.Orientation;
import static android.graphics.drawable.GradientDrawable.Orientation.LEFT_RIGHT;
import static android.graphics.drawable.GradientDrawable.Orientation.TOP_BOTTOM;

/**
 * Improved PeterAttardo/Shadow.java
 * @see{https://gist.github.com/PeterAttardo/cc722b7649d0e62274b2}
 */
public class Shadows {

    private static final int START_COLOR = Color.parseColor("#55000000");

    private static final int END_COLOR = Color.parseColor("#00000000");

    private static final int SHADOW_LENGTH = (int) (5 * Resources.getSystem()
            .getDisplayMetrics().density);

    private static final Orientation[] shadowOrientations =
            new Orientation[]{TOP_BOTTOM, LEFT_RIGHT};

    private static int[] colors = new int[]{START_COLOR, END_COLOR};

    private static SparseArray linearGradients = new SparseArray() {{
        for (Orientation orientation : shadowOrientations) {
            put(orientation.ordinal(), new GradientDrawable(orientation, colors));
        }
    }};

    private static GradientDrawable radialGradient = new GradientDrawable() {{
        setGradientType(RADIAL_GRADIENT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setColors(colors);
        } else {
            setColor(END_COLOR);
        }
        setGradientRadius(SHADOW_LENGTH);
    }};


    static public void bindShadow(Canvas canvas, View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return;
        }

        int height = view.getHeight();
        int width = view.getWidth();

        Rect bottomBounds = new Rect(SHADOW_LENGTH, height, width, height + SHADOW_LENGTH);
        drawShadow(canvas, TOP_BOTTOM, bottomBounds);

        Rect rightBounds = new Rect(width, SHADOW_LENGTH, width + SHADOW_LENGTH, height);
        drawShadow(canvas, LEFT_RIGHT, rightBounds);

        Rect cornerBLBounds = new Rect(0, height, SHADOW_LENGTH, height + SHADOW_LENGTH);
        radialGradient.setBounds(cornerBLBounds);
        radialGradient.setGradientCenter(1, 0);
        radialGradient.draw(canvas);

        Rect cornerBRBounds = new Rect(width, height, width + SHADOW_LENGTH,
                height + SHADOW_LENGTH);
        radialGradient.setBounds(cornerBRBounds);
        radialGradient.setGradientCenter(0, 0);
        radialGradient.draw(canvas);

        Rect cornerTRBounds = new Rect(width, 0, width + SHADOW_LENGTH, SHADOW_LENGTH);
        radialGradient.setBounds(cornerTRBounds);
        radialGradient.setGradientCenter(0, 1);
        radialGradient.draw(canvas);
    }

    static public void bindAboveShadow(Canvas canvas, View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return;
        }

        int height = 0;
        int width = view.getWidth();

        Rect bottomBounds = new Rect(SHADOW_LENGTH, height, width, height + SHADOW_LENGTH);
        drawShadow(canvas, TOP_BOTTOM, bottomBounds);

        Rect rightBounds = new Rect(width, SHADOW_LENGTH, width + SHADOW_LENGTH, height);
        drawShadow(canvas, LEFT_RIGHT, rightBounds);

        Rect cornerBLBounds = new Rect(0, height, SHADOW_LENGTH, height + SHADOW_LENGTH);
        radialGradient.setBounds(cornerBLBounds);
        radialGradient.setGradientCenter(1, 0);
        radialGradient.draw(canvas);

        Rect cornerBRBounds = new Rect(width, height, width + SHADOW_LENGTH,
                height + SHADOW_LENGTH);
        radialGradient.setBounds(cornerBRBounds);
        radialGradient.setGradientCenter(0, 0);
        radialGradient.draw(canvas);

        Rect cornerTRBounds = new Rect(width, 0, width + SHADOW_LENGTH, SHADOW_LENGTH);
        radialGradient.setBounds(cornerTRBounds);
        radialGradient.setGradientCenter(0, 1);
        radialGradient.draw(canvas);
    }

    static private void drawShadow(Canvas canvas, Orientation orientation,
            Rect bounds) {
        GradientDrawable linearGradient = (GradientDrawable) linearGradients
                .get(orientation.ordinal());
        linearGradient.setBounds(bounds);
        linearGradient.draw(canvas);
    }
}

新しく定義したbindAboveShadowメソッドを利用して、onDraw内で描画を行います。
下記は Toolbar と PagerSlidingTabStrip、ViewPager を利用している場合の例です。

<AwesomeViewPagerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:orientation="vertical">

    <include android:id="@+id/toolbar"
             layout="@layout/layout_toolbar"/>

    <!-- PagerSlidingTabStrip を継承して onDrow をオーバーライド -->
    <com.example.ShadowablePagerSlidingTabStrip
            android:id="@+id/tab_strip"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"/>

    <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>


</AwesomeViewPagerView>
com.example.ShadowablePagerSlidingTabStrip.java
public class ShadowablePagerSlidingTabStrip extends PagerSlidingTabStrip {

    public ShadowablePagerSlidingTabStrip(Context context) {
        super(context);
    }

    public ShadowablePagerSlidingTabStrip(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ShadowablePagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // getChildAt(0) でタブ本体の View に設定をおこなう
        Shadows.bindAboveShadow(canvas, getChildAt(0));
    }
}

ViewPager にバインドした View も同様に PagerSlidingTabStrip の影を描画するようにすれば、しっかりと表示されます。
onDraw内ではなくてdispatchDraw内で描画しなければいけない場合があります。)

shadow_1.jpg

マテリアルデザインですが、やはり影が無くてはマテリアルデザインらしさが出ないのでしっかりと作りこんで行きたいですね。

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8