Android进阶之路 - 背景阴影、阴影背景

这篇具有很好参考价值的文章主要介绍了Android进阶之路 - 背景阴影、阴影背景。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

不知道你是不是也经常听到这些话:你这个没有阴影效果;你这个阴影太浓了;你这个阴影太粗了;你这个阴影太实了;你这个阴影颜色也不对,你这个阴影…

在正式开发中,临近上线前有个环节叫UI验收(产品验收在其前后均可),主要查看开发效果与设计图是否统一,当然很多UI可能也会临时在做修改…

话回最初,在验收环节中经常会提出关于阴影的问题,可以说是不胜其烦,正好最近有一些时间,特意记录一下我已知的阴影实现

篇中的每一种效果都经过了 demo 的考验,应该总有一种能满足设计的需求

效果总览

以下均为真机测试效果

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

shape 伪阴影

之所以记录这种伪阴影的效果是因为后续的一些阴影实现方式需要用到该处知识

对于shape不了解,或者不熟悉的可以直接去看 shape保姆级手册

效果
android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

shape_shadow(shape样式)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 这里是设置背景色-->
    <solid android:color="#ffffff" />
    <!-- 设置四周圆角,可统一设置,也可以单独设置某个位置为圆角-->
    <corners android:radius="5dp" />
    <!--    <corners-->
    <!--        android:bottomLeftRadius="5dp"-->
    <!--        android:bottomRightRadius="5dp"-->
    <!--        android:topLeftRadius="5dp"-->
    <!--        android:topRightRadius="5dp" />-->
    <!-- 这里设置边框 -->
    <stroke
        android:width="1dp"
        android:color="#eeeeee" />
</shape>

设置控件background

    <TextView
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:background="@drawable/shape_shadow"
        android:gravity="center"
        android:text="伪阴影" />

layer-list 伪阴影

说实话,我以前并没有用过layer-list的方式去组装shape,不过看阴影的时候也顺带学习了一波

采用 layer-list 的实现方式时,可以把它换位为 xml 中写控件,因为这个也是组装图层

效果

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

layer_shadow

  • 绘制俩个长方形的shape,上层视图添加内边距,就会形成视觉错觉,也是一种伪阴影效果
  • 对比前者,这种方式可以改变底部背景,类似修改伪阴影颜色
  • 对比前者,这种方式可以改变图层边距,类似修改伪阴影深度
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#CAEEEEEE" />
            <corners android:radius="2dp" />
        </shape>
    </item>

    <item
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp">
        <shape android:shape="rectangle">
            <solid android:color="@android:color/white" />
            <corners android:radius="2dp" />
        </shape>
    </item>
</layer-list>

设置控件background

    <TextView
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_margin="10dp"
        android:background="@drawable/layer_shadow"
        android:gravity="center"
        android:text="layer-list 阴影" />

elevation 阴影

elevationMaterial Design 提供的一种阴影效果,只有API21及以上才支持使用;以前我没怎么用过,我写demo时尝试了一下这些属性主要作用于 ViewGroup

对于验收环节不是很严格的话,这种实现也可以过关,使用也很简单,主要用到了 elevation + translationZ + outlineSpotShadowColor 属性,未设置outlineSpotShadowColor会用系统默认灰色

  • elevation 高度
  • translationZ 深度
  • outlineSpotShadowColor 阴影色

效果

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

使用方式

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:background="@color/white"
        android:elevation="5dp"
        android:orientation="vertical"
        android:outlineSpotShadowColor="#f00000"
        android:translationZ="1dp">

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:text="elevation 阴影" />
    </LinearLayout>

CardView 阴影

CardViewMaterial Design 提供的一种外层(ViewGroup)控件,只有API21及以上才支持使用;内部采用的方式好像也是elevation + translationZ 结合的方式

对于CardView不是太了解的话,可以去看看 CardView卡片化效果,可以快速实现圆角化、阴影等效果等

效果

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景
使用方式

    <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="10dp"
        android:background="@color/white"
        android:translationZ="3dp"
        app:cardElevation="5dp">

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:text="CardView 阴影" />
    </androidx.cardview.widget.CardView>

.9图 阴影

设计需求来源于设计,所以也要从设计中找方案;使用.9图首先需要设计提供可用于制作.9图的原图,原图中已经实现了阴影效果,我们只要负责做.9图即可

很多设计应该并不提供.9图,所以往往需要我们 自行制作.9图 ,这里我就不去制作了,因为每个人的原图都不同,所以最好是掌握 制作.9图的方法

项目:像我项目中这样的阴影背景布局(需要设计提供一张一半高度+自带阴影的背景图),.9图可自动拉伸

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景


自定义控件 阴影

我直接在百度找自定义阴影控件的时候,发现了github上的一个三方库,然后取了一个自定义控件类 ShadowDrawable,经测试也可以直接使用

这款控件支持动态设置阴影色、控件圆角、控件背景色等

官方效果图

android 背景阴影,Android进阶之路,# 项目开发知识点归纳,Android,阴影,背景阴影,阴影背景

ShadowDrawable 自定义类,可直接copy

package com.example.kotlindemo;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;

public class ShadowDrawable extends Drawable {

    private Paint mShadowPaint;
    private Paint mBgPaint;
    private int mShadowRadius;
    private int mShape;
    private int mShapeRadius;
    private int mOffsetX;
    private int mOffsetY;
    private int mBgColor[];
    private RectF mRect;

    public final static int SHAPE_ROUND = 1;
    public final static int SHAPE_CIRCLE = 2;

    private ShadowDrawable(int shape, int[] bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
        this.mShape = shape;
        this.mBgColor = bgColor;
        this.mShapeRadius = shapeRadius;
        this.mShadowRadius = shadowRadius;
        this.mOffsetX = offsetX;
        this.mOffsetY = offsetY;

        mShadowPaint = new Paint();
        mShadowPaint.setColor(Color.TRANSPARENT);
        mShadowPaint.setAntiAlias(true);
        mShadowPaint.setShadowLayer(shadowRadius, offsetX, offsetY, shadowColor);
        mShadowPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));

        mBgPaint = new Paint();
        mBgPaint.setAntiAlias(true);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        mRect = new RectF(left + mShadowRadius - mOffsetX, top + mShadowRadius - mOffsetY, right - mShadowRadius - mOffsetX,
                bottom - mShadowRadius - mOffsetY);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        if (mBgColor != null) {
            if (mBgColor.length == 1) {
                mBgPaint.setColor(mBgColor[0]);
            } else {
                mBgPaint.setShader(new LinearGradient(mRect.left, mRect.height() / 2, mRect.right,
                        mRect.height() / 2, mBgColor, null, Shader.TileMode.CLAMP));
            }
        }

        if (mShape == SHAPE_ROUND) {
            canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, mShadowPaint);
            canvas.drawRoundRect(mRect, mShapeRadius, mShapeRadius, mBgPaint);
        } else {
            canvas.drawCircle(mRect.centerX(), mRect.centerY(), Math.min(mRect.width(), mRect.height())/ 2, mShadowPaint);
            canvas.drawCircle(mRect.centerX(), mRect.centerY(), Math.min(mRect.width(), mRect.height())/ 2, mBgPaint);
        }
    }

    @Override
    public void setAlpha(int alpha) {
        mShadowPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mShadowPaint.setColorFilter(colorFilter);
    }

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

    public static void setShadowDrawable(View view, Drawable drawable) {
        view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        ViewCompat.setBackground(view, drawable);
    }

    /**
     * 为指定View添加阴影
     * @param view 目标View
     * @param shapeRadius View的圆角
     * @param shadowColor 阴影的颜色
     * @param shadowRadius 阴影的宽度
     * @param offsetX 阴影水平方向的偏移量
     * @param offsetY 阴影垂直方向的偏移量
     */
    public static void setShadowDrawable(View view, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
        ShadowDrawable drawable = new ShadowDrawable.Builder()
                .setShapeRadius(shapeRadius)
                .setShadowColor(shadowColor)
                .setShadowRadius(shadowRadius)
                .setOffsetX(offsetX)
                .setOffsetY(offsetY)
                .builder();
        view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        ViewCompat.setBackground(view, drawable);
    }

    /**
     * 为指定View设置带阴影的背景
     * @param view 目标View
     * @param bgColor View背景色
     * @param shapeRadius View的圆角
     * @param shadowColor 阴影的颜色
     * @param shadowRadius 阴影的宽度
     * @param offsetX 阴影水平方向的偏移量
     * @param offsetY 阴影垂直方向的偏移量
     */
    public static void setShadowDrawable(View view, int bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
        ShadowDrawable drawable = new ShadowDrawable.Builder()
                .setBgColor(bgColor)
                .setShapeRadius(shapeRadius)
                .setShadowColor(shadowColor)
                .setShadowRadius(shadowRadius)
                .setOffsetX(offsetX)
                .setOffsetY(offsetY)
                .builder();
        view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        ViewCompat.setBackground(view, drawable);
    }

    /**
     * 为指定View设置指定形状并带阴影的背景
     * @param view 目标View
     * @param shape View的形状 取值可为:GradientDrawable.RECTANGLE, GradientDrawable.OVAL, GradientDrawable.RING
     * @param bgColor View背景色
     * @param shapeRadius View的圆角
     * @param shadowColor 阴影的颜色
     * @param shadowRadius 阴影的宽度
     * @param offsetX 阴影水平方向的偏移量
     * @param offsetY 阴影垂直方向的偏移量
     */
    public static void setShadowDrawable(View view, int shape, int bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
        ShadowDrawable drawable = new ShadowDrawable.Builder()
                .setShape(shape)
                .setBgColor(bgColor)
                .setShapeRadius(shapeRadius)
                .setShadowColor(shadowColor)
                .setShadowRadius(shadowRadius)
                .setOffsetX(offsetX)
                .setOffsetY(offsetY)
                .builder();
        view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        ViewCompat.setBackground(view, drawable);
    }

    /**
     * 为指定View设置带阴影的渐变背景
     * @param view
     * @param bgColor
     * @param shapeRadius
     * @param shadowColor
     * @param shadowRadius
     * @param offsetX
     * @param offsetY
     */
    public static void setShadowDrawable(View view, int[] bgColor, int shapeRadius, int shadowColor, int shadowRadius, int offsetX, int offsetY) {
        ShadowDrawable drawable = new ShadowDrawable.Builder()
                .setBgColor(bgColor)
                .setShapeRadius(shapeRadius)
                .setShadowColor(shadowColor)
                .setShadowRadius(shadowRadius)
                .setOffsetX(offsetX)
                .setOffsetY(offsetY)
                .builder();
        view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        ViewCompat.setBackground(view, drawable);
    }

    public static class Builder {
        private int mShape;
        private int mShapeRadius;
        private int mShadowColor;
        private int mShadowRadius;
        private int mOffsetX;
        private int mOffsetY;
        private int[] mBgColor;

        public Builder() {
            mShape = ShadowDrawable.SHAPE_ROUND;
            mShapeRadius = 12;
            mShadowColor = Color.parseColor("#4d000000");
            mShadowRadius = 18;
            mOffsetX = 0;
            mOffsetY = 0;
            mBgColor = new int[1];
            mBgColor[0] = Color.TRANSPARENT;
        }

        public Builder setShape(int mShape) {
            this.mShape = mShape;
            return this;
        }

        public Builder setShapeRadius(int ShapeRadius) {
            this.mShapeRadius = ShapeRadius;
            return this;
        }

        public Builder setShadowColor(int shadowColor) {
            this.mShadowColor = shadowColor;
            return this;
        }

        public Builder setShadowRadius(int shadowRadius) {
            this.mShadowRadius = shadowRadius;
            return this;
        }

        public Builder setOffsetX(int OffsetX) {
            this.mOffsetX = OffsetX;
            return this;
        }

        public Builder setOffsetY(int OffsetY) {
            this.mOffsetY = OffsetY;
            return this;
        }

        public Builder setBgColor(int BgColor) {
            this.mBgColor[0] = BgColor;
            return this;
        }

        public Builder setBgColor(int[] BgColor) {
            this.mBgColor = BgColor;
            return this;
        }

        public ShadowDrawable builder() {
            return new ShadowDrawable(mShape, mBgColor, mShapeRadius, mShadowColor, mShadowRadius, mOffsetX, mOffsetY);
        }
    }
}

使用方式

   var testView = findViewById<TextView>(R.id.test_view)
   /* 为指定View设置带阴影的背景
   * @param view 目标View
   * @param bgColor View背景色
   * @param shapeRadius View的圆角
   * @param shadowColor 阴影的颜色
   * @param shadowRadius 阴影的宽度
   * @param offsetX 阴影水平方向的偏移量
   * @param offsetY 阴影垂直方向的偏移量
   */
   ShadowDrawable.setShadowDrawable(
       testView, Color.parseColor("#FFFFFF"), 8,
       Color.parseColor("#992979FF"), 6, 0, 0
   ); }

GradientDrawable 阴影(项目自用)

采用的是kt写的扩展函数,调用也很方便,目前在项目中使用,UI也验收通过

我简单的看了下,当前在项目中用的这种方式采用的是 Shape + GradientDrawable 的方式,因为是公司大佬写的,有部分源码我也还没细看,固将关键部分抽离出来,经测试后正常生效

主要分为三部分

  • shape 顶层扩展方法
  • view 顶层扩展方法
  • 使用方式

因为具体阴影设置是在代码中统一设置,固相关效果查看总效果图即可

shape 顶层方法

package com.example.kotlindemo

import android.graphics.drawable.GradientDrawable

typealias ColorInt = Int
typealias Px = Int
typealias FloatPx = Float

internal const val NO_GETTER = "Getter not available"

inline fun shapeDrawable(fill: GradientDrawable.() -> Unit): GradientDrawable =
    GradientDrawable().also {
        it.gradientType = GradientDrawable.LINEAR_GRADIENT
        it.fill()
    }

enum class Shape {
    RECTANGLE, OVAL, LINE, RING,
}

typealias ShapeInt = Int

fun toInt(s: Shape): ShapeInt = when (s) {
    Shape.RECTANGLE -> GradientDrawable.RECTANGLE
    Shape.OVAL -> GradientDrawable.OVAL
    Shape.LINE -> GradientDrawable.LINE
    Shape.RING -> GradientDrawable.RING
}

enum class Orientation {
    TOP_BOTTOM, TR_BL, RIGHT_LEFT, BR_TL, BOTTOM_TOP, BL_TR, LEFT_RIGHT, TL_BR,
}

private fun GradientDrawable.toOrientation(orientation: Orientation): GradientDrawable.Orientation =
    when (orientation) {
        Orientation.TOP_BOTTOM -> GradientDrawable.Orientation.TOP_BOTTOM
        Orientation.TR_BL -> GradientDrawable.Orientation.TR_BL
        Orientation.RIGHT_LEFT -> GradientDrawable.Orientation.RIGHT_LEFT
        Orientation.BR_TL -> GradientDrawable.Orientation.BR_TL
        Orientation.BOTTOM_TOP -> GradientDrawable.Orientation.BOTTOM_TOP
        Orientation.BL_TR -> GradientDrawable.Orientation.BL_TR
        Orientation.LEFT_RIGHT -> GradientDrawable.Orientation.LEFT_RIGHT
        Orientation.TL_BR -> GradientDrawable.Orientation.TL_BR
    }

var GradientDrawable.shapeEnum: Shape
    set(value) {
        shape = toInt(value)
    }
    @Deprecated(message = NO_GETTER, level = DeprecationLevel.HIDDEN) get() = error(NO_GETTER)

fun rectangleGradientShape(
    radius: FloatPx = Float.NaN,
    colors: IntArray,
    orientation: Orientation,
    fill: GradientDrawable.() -> Unit = {}
): GradientDrawable =
    shapeDrawable {
        shapeEnum = Shape.RECTANGLE
        setColors(colors)
        this.orientation = toOrientation(orientation)
        // DO NOT CHANGE
        // RADIUS AND COLOR ORDER IS IMPORTANT FOR RIPPLES!
        if (!radius.isNaN()) {
            cornerRadius = radius
        }
        fill.invoke(this)
    }

fun rectangleShape(
    radius: FloatPx = Float.NaN,
    color: ColorInt,
    size: Px? = null,
    fill: GradientDrawable.() -> Unit = {}
): GradientDrawable =
    shapeDrawable {
        shapeEnum = Shape.RECTANGLE
        solidColor = color
        size?.let {
            this.size = it
        }
        // DO NOT CHANGE
        // RADIUS AND COLOR ORDER IS IMPORTANT FOR RIPPLES!
        if (!radius.isNaN()) {
            cornerRadius = radius
        }
        fill.invoke(this)
    }

fun circleShape(color: ColorInt, size: Px? = null): GradientDrawable = shapeDrawable {
    shape = GradientDrawable.OVAL
    solidColor = color
    size?.let {
        this.size = it
    }
}

var GradientDrawable.solidColor: ColorInt
    set(value) = setColor(value)
    @Deprecated(message = NO_GETTER, level = DeprecationLevel.HIDDEN) get() = error(NO_GETTER)

var GradientDrawable.size: Px
    set(value) = setSize(value, value)
    get() = intrinsicWidth

class Stroke {
    var width: Px = -1
    var color: ColorInt = -1
    var dashWidth: FloatPx = 0F
    var dashGap: FloatPx = 0F
}

inline fun GradientDrawable.stroke(fill: Stroke.() -> Unit): Stroke = Stroke().also {
    it.fill()
    setStroke(it.width, it.color, it.dashWidth, it.dashGap)
}

class Size {
    var width: Px = -1
    var height: Px = -1
}

inline fun GradientDrawable.size(fill: Size.() -> Unit): Size = Size().also {
    fill(it)
    setSize(it.width, it.height)
}

class Corners {
    var radius: FloatPx = 0F

    var topLeft: FloatPx = Float.NaN
    var topRight: FloatPx = Float.NaN
    var bottomLeft: FloatPx = Float.NaN
    var bottomRight: FloatPx = Float.NaN

    internal fun FloatPx.orRadius(): FloatPx = takeIf { it >= 0 } ?: radius
}

fun Corners.render(): FloatArray = floatArrayOf(
    topLeft.orRadius(), topLeft.orRadius(),
    topRight.orRadius(), topRight.orRadius(),
    bottomRight.orRadius(), bottomRight.orRadius(),
    bottomLeft.orRadius(), bottomLeft.orRadius()
)

inline fun GradientDrawable.corners(fill: Corners.() -> Unit): Corners = Corners().also {
    it.fill()
    cornerRadii = it.render()
}

fun GradientDrawable.corners(
    radius: FloatPx = 0f,
    topLeft: FloatPx = Float.NaN,
    topRight: FloatPx = Float.NaN,
    bottomLeft: FloatPx = Float.NaN,
    bottomRight: FloatPx = Float.NaN
): Corners = Corners().also {
    it.radius = radius
    it.topLeft = topLeft
    it.topRight = topRight
    it.bottomLeft = bottomLeft
    it.bottomRight = bottomRight
    cornerRadii = it.render()
}

View 扩展函数

package com.example.kotlindemo

import android.view.View
import androidx.core.graphics.toColorInt


fun View.warpInWhiteShadow(radius: Float = 0f, topLeft: Float = Float.NaN, topRight: Float = Float.NaN, bottomLeft: Float = Float.NaN, bottomRight: Float = Float.NaN) {
    background = rectangleShape(color = "#1AFFFFFF".toColorInt()) {
        corners(radius, topLeft, topRight, bottomLeft, bottomRight)
    }
    translationZ = 6f
}


//application的上下文,我这边demo就不复杂化了,主要是为了尺寸适配更好看一些
//inline val Int.dp: Int
//    get() = (this * AppContext.resources.displayMetrics.density + 0.5f).toInt()
//
//inline val Float.dp: Float
//    get() = (this * AppContext.resources.displayMetrics.density + 0.5f).toInt().toFloat()

使用方式

   //先设置底层的阴影背景
   var selfView = findViewById<TextView>(R.id.self_view)
   selfView.warpInWhiteShadow(topLeft = 6f, topRight = 6f)
   //再设置控件的背景
   selfView.background = rectangleShape(color = Color.WHITE) {
       corners(topLeft = 4f, topRight = 4f)
   }

xml 总览

为了防止有的朋友查看 xml 设置,特记录于此文章来源地址https://www.toymoban.com/news/detail-761484.html

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">

    <TextView
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:background="@drawable/shape_shadow"
        android:gravity="center"
        android:text="伪阴影" />

    <TextView
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_margin="10dp"
        android:background="@drawable/layer_shadow"
        android:gravity="center"
        android:text="layer-list 阴影" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:background="@color/white"
        android:elevation="5dp"
        android:orientation="vertical"
        android:translationZ="1dp">

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:text="elevation 阴影" />
    </LinearLayout>

    <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="10dp"
        android:background="@color/white"
        android:translationZ="3dp"
        app:cardElevation="5dp">

        <TextView
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:text="CardView 阴影" />
    </androidx.cardview.widget.CardView>

    <TextView
        android:id="@+id/test_view"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="自定义 阴影" />

    <TextView
        android:id="@+id/self_view"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="项目 阴影" />

</LinearLayout>

到了这里,关于Android进阶之路 - 背景阴影、阴影背景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Studio开发小项目

    “莆仙小馆”——莆田文化展示APP 文化展示程序目的在于应用科学技术助推家乡优秀传统文化的展示与交流。通过图片、视频、音频等展示方式向用户立体地展示一个文化城邦。传统文化与科学技术的有效融合,顺应了社会发展的需要。传统文化与科学技术的有效融合是发展

    2024年02月15日
    浏览(30)
  • Unity Android Studio 混合开发实践(Unity工程导入Android项目进行开发)

    最近接到一个任务是将一个unity开发的游戏接入到现有的Android项目里,然后在现有的App实现点击一个按钮打开游戏,并且在游戏内提供一个可以退出到App的按钮。 整体需求是很明确的,难点主要有两个: 我们公司是做应用开发的,没有任何游戏开发的技能储备。 在游戏中需

    2024年02月11日
    浏览(43)
  • 小项目开发——Android 音乐播放器

    ◼ 音乐播放器 . ◼ 要求 : Activity 编程、 ListView 编程、 SeekBar 编程、 ExoPlayer 编程( 播放 、 暂停 、 停止 、 上一首 、 下一首 ),音乐文件放在 assets/music 目录下,界面自拟. ◼ 期望最终效果: ◼ 分别对应 activity_music_list.xml 、 activity_my_music_player.xml 的视图. ◼ 点击列表任

    2024年01月21日
    浏览(32)
  • Android开发-Android项目Jenkins自动化打包流程搭建与配置

    由于之前公司的 Android 项目需要 APK 自动打包的功能,所以需要搭建 Jenkins 自动化打包的功能。 Jenkins 是一个用于自动化构建、测试和部署软件项目的开源持续集成和持续交付(CI/CD)工具。它提供了一个可扩展的平台,可以帮助开发团队更快地构建、测试和交付他们的应用程

    2024年02月08日
    浏览(36)
  • Android进阶之路 - 存、取、读 本地 Json 文件

    最近在开发中又开始加载一些本地的json数据源,回头看之前竟然没记录,赶紧记录一波 ~ 如何准备一个合格的json文件? 准备一个txt格式的json串, 另存为(A)... 更改文件名后缀为json 、 更改编码格式为 UTF-8 - 保存后你就获取到了一个合格的json文件了 题外话 - 此处可不看 因

    2024年02月01日
    浏览(24)
  • Android相机开发实战,面试题+笔记+项目实战

    private SurfaceView mSurfaceView; private SurfaceHolder mHolder; private Size mPreviewSize; private Size adapterSize; //private List mSupportedPreviewSizes; private Camera mCamera; private boolean isSupportAutoFocus = false; private Camera.Parameters parameters = null; private Context mContext; //private int mCurrentCameraId = 0; private int screenWidth; pri

    2024年04月16日
    浏览(33)
  • Android开发的《大众设计App》项目介绍

    该《大众设计App》的功能介绍如下: 1、登录注册功能 登录、注册页面效果如下所示: 2、用户信息修改功能 (各个修改功能均已实现,因修改栏目较多不再逐一展示) 3、设计衣服的功能 (也是本App的核心功能) 页面效果如下图。分为上下两个区,上面部分是预览设计效果

    2024年02月02日
    浏览(28)
  • 如何合理使用 Jetpack 组件开发 Android 项目?

    Jetpack 是 Android 官方推出的一套开发库,其中包含众多的组件,可以让 Android 开发者更快更高效地开发应用程序。Jetpack 组件分为四大部分:架构、行为、UI 和基础组件。 下面详细阐述如何合理使用 Jetpack 组件开发 Android 项目。 在使用 Jetpack 组件之前,首先应熟悉几个常用的

    2024年02月02日
    浏览(32)
  • Android进阶之路 - ViewPager2 比 ViewPager 强在哪?

    我记得前年(2022)面试的时候有被问到 ViewPager 和 ViewPager2 有什么区别?当时因为之前工作一直在开发售货机相关的项目,使用的技术要求并不高,所以一直没去了解过 ViewPager2~ 去年的时候正好有相关的功能需求,索性直接用 ViewPager2 进行了 Tip :很多人可能比较关注俩者区

    2024年02月20日
    浏览(34)
  • 移动开发项目 Android Studio 健康助手APP

    健康助手系统是一款便捷软件,旨在通过提供多方面的的健康便捷的管理服务,让用户的生活更健康,更便捷。用户可以在健康助手APP上购买不同的体检套餐,预约医生,使用地图查找药房等的位置,浏览网页了解健康知识,传播健康文化。 (1)为了更好地了解自己的身体

    2024年02月03日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包