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

news2024/11/26 18:23:43

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

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

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

    • 效果总览
    • shape 伪阴影
    • layer-list 伪阴影
    • elevation 阴影
    • CardView 阴影
    • .9图 阴影
    • 自定义控件 阴影
    • GradientDrawable 阴影(项目自用)
    • xml 总览

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

效果总览

以下均为真机测试效果

在这里插入图片描述

shape 伪阴影

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

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

效果
在这里插入图片描述

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 中写控件,因为这个也是组装图层

效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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 阴影色

效果

在这里插入图片描述

在这里插入图片描述

使用方式

    <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卡片化效果,可以快速实现圆角化、阴影等效果等

效果

在这里插入图片描述
使用方式

    <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图可自动拉伸

在这里插入图片描述


自定义控件 阴影

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

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

官方效果图

在这里插入图片描述

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 设置,特记录于此

<?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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/718474.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vector - CAPL - 数据库和CAPL_02

DBLookup 动态读取数据库中的信息 //Transmitter 属性以及数据库属性只能通过 DBLookup 动态读取。 //返回数据库中存储的 DLC on message * {int myAttributeValue;myAttributeValue DBLookup(this).MyAttribute;write(this.Transmitter); // compiler errorwrite(DBLookup(…

AR试穿试戴相关SDK或平台

1.火山引擎 链接 咨询过平台收费比较高几十万一年而且还是起步价 2.Geenee 链接 geenee在衣服、裤子、头饰以及鞋子方面可以实现试穿。 3.Wanna 链接 Wanna 试衣、试包、试鞋及手表都可以&#xff0c;我试过鞋子的试穿效果还不错 4.DeepAR …

sqlite维护命令复习学习

前面已经看了一些sqlite命令&#xff0c;例如查看表名&#xff0c;查看表结构等&#xff1b;下面继续看一下&#xff1b; 查看全部表名&#xff1b; 查看单个或全部的表结构&#xff1b; 输出表结构和数据&#xff1b; 使用.output 把查询结果定向到1.txt&#xff1b; 重新定向…

Java设计模式系列--观察者模式写法2:JDK

原文网址&#xff1a;Java设计模式系列--观察者模式写法2&#xff1a;JDK_IT利刃出鞘的博客-CSDN博客 简介 说明 本文用示例介绍观察者模式的一种写法&#xff1a;JDK。 JDK的观察者模式简介 在 Java 中&#xff0c;java.util.Observable 类和 java.util.Observer 接口定义…

Elsevier: Expert Systems With Applications 经验分享

目录 序时间线投稿返稿录用模板下载链接 序 这是一篇Elsevier旗下Expert Systems With Applications(ESWA)期刊的投稿经验分享。虽然是ESWA&#xff0c;但对于Elsevier旗下的其它期刊也适用&#xff0c;他们都可以用一套模板。 需要注意的是不同期刊会有一些特别的要求&#…

13 直接存储器访问DMA(基于STM32HAL库)

目录 DMA-直接存储器访问控制器 DMA概览 DMA的作用 DMA框图 DMA外设要点概括 DMA功能对比 STMF10x DMA具体内容 DMA主要特性 DMA中断 DMA请求映像 DMA的使用步骤 HAL库中的DMA功能实例 句柄结构体介绍&#xff08;以DMA为例&#xff09; 外设初始化结构体介绍 具体…

多元回归预测 | Matlab基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于基于粒子群算法优化深度置信网络(PSO-DBN)的数据回归预测,matlab代码回归预测,多变量输入模型,多变量输入模型 评价

聊聊不同集群的微服务如何通过feign调用

前言 之前业务部门的某项目微服务调用关系如下图 后因业务改造需要&#xff0c;该项目需要将服务A部署到另外一个集群&#xff0c;但服务A仍然需要能调用到服务B&#xff0c;调用关系如下图 之前调用方式是负责服务B的开发团队提供相应的feign客户端包给到服务A开发团队&…

三种Linux内核代码在线阅读工具

记录一下 1 . 可在线阅读uboot&#xff0c;kernel&#xff0c;busybox(rootfs)&#xff0c;可搜索字符串&#xff0c;函数跳 https://lxr.missinglinkelectronics.com/ 界面如下&#xff1a; 2. 显示界面跟代码编辑器很像&#xff0c;同样可以函数跳转 https://elixir.boot…

如何让罗技29方向盘像视频中的那样转动起来?

​​​​​​​[vlog]Autoware Carla G29 自动驾驶仿真_哔哩哔哩_bilibili 话接上文&#xff0c;在我之前一篇博客中已经讲解了如何给罗技29方向盘装上力反馈&#xff0c;也就是在拨动方向盘的时候感觉有一个力组织你过度的拨动方向盘&#xff0c;其实它真正的用处是用于实现对…

【Web3】认识区块链

目录 区块链特征 区块链类型 区块链的概念 区块链特征 去中心化&#xff1a;区块链是由一个分布在多个参与者之间的网络组成&#xff0c;没有中央机构或中介控制整个系统。所有参与者共同维护和验证账本的完整性&#xff0c;减少了单点故障和集中式控制的风险。共识机制&…

【HTTPS】采用的加密策略, 什么是中间人攻击? 什么是证书?

文章目录 前言一、认识 HTTPS 协议1, 对称加密2, 非对称加密 二、HTTPS 加密策略1, 只采用对称加密 : 不安全2, 引入非对称加密3, 中间人攻击之偷梁换柱4, 引入证书4.1 什么是证书4.2, 证书如何能解决"中间人攻击" 总结 前言 各位读者好, 我是小陈, 这是我的个人主页…

日历与时钟

目录 公历 黑色星期五 生物韵律 公历 在公历中&#xff0c;当年份为4的整数倍&#xff0c;但不是100的整数倍时&#xff0c;会出现闰年的现象。 y40 mod(y,4) 0 && mod(y,100)||mod(y,400)0 输出当时的年、月、日、时、分、秒 f%6d %6d %6d %6d %6d %9.3f\n cclock …

MySQL学习基础篇(八)---聚合函数

MySQL学习基础篇(八)—聚合函数 聚合&#xff08;或聚集、分组&#xff09;函数&#xff0c;它是对一组数据进行汇总的函数&#xff0c;输入的是一组数据的集合&#xff0c;输出的是单个值。 1. 聚合函数介绍 什么是聚合函数&#xff1a;聚合函数作用于一组数据&#xff0c;…

前端实战——尚品汇(网页开发)

/* 基础设置 */ .container {width: 1190px;margin: 0 auto; } /* #region顶部导航条start */ .topbar {height: 30px;background-color: #ececec; } .welcome {height: 30px;line-height: 30px;font-size: 0;color: #666; } .welcome span,.welcome a {font-size: 12px; } .we…

AIGC - Stable Diffusion WebUI 图像生成工具的环境配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131528224 Stable Diffusion WebUI 是一款基于深度学习的图像生成工具&#xff0c;根据用户的输入文本或图像&#xff0c;生成高质量的新图像&…

关于VMware虚拟空间的创建、Linux系统的安装

文章目录 前言一、Windows用户安装VMware软件1.1 下载VMware1.2 正常安装VMware后&#xff0c;该软件是要收费的&#xff0c;但是下面的链接可以让你使用很久 二、Mac用户安装VMware软件2.1 下载macOS版本&#xff1a;VMware Fusion2.2 正常安装VMware后&#xff0c;该软件是要…

「2024」预备研究生mem-形式逻辑强化:推矛盾

一、推矛盾 易错题&#xff1a;重点 重点&#xff1a; 二、课后题 三、每日一练

新版本vscode使用配置文件功能,解决不同项目使用不同的插件

如果你同时有vue2,vue3的项目。一定会遇到插件的问题。因为vue2项目插件是使用vetur的&#xff0c;vue3是使用volar的。 以前vscode为了在不同项目中能使用不同的配置文件&#xff0c;是使用工作区的概念去解决的&#xff0c;但是比较复杂而且不好用。 现在新版本的vscode&…

【温故而知新】Android架构模式

Android项目工程中常用的架构模式有MVC, MVP, MVVM以及现在新出的MVI。 下面一起温故而知新。 MVC MVC&#xff08;Model-View-Controller&#xff09;是一种在Android应用程序中使用的架构模式&#xff0c;用于实现松耦合、可测试和可维护的应用程序。 MVC架构模式包括三个…