一、Android中实现View的阴影效果有以下四种方法:
1.使用系统自带的CardView.
2.使用shape多层次颜色渐变实现.
3.找UI切一张带阴影效果的图.
4.自定义View实现阴影效果.
二、各种方式的代码如下:
1.使用CardView:
<androidx.cardview.widget.CardView android:id="@+id/cardview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_margin="20dp" card:cardCornerRadius="4dp" card:cardElevation="10dp" card:cardMaxElevation="10dp" app:layout_constraintTop_toBottomOf="@+id/rl_image" tools:ignore="MissingConstraints"> <TextView android:layout_width="match_parent" android:layout_height="100dp" android:layout_gravity="center" android:gravity="center" android:text="黄鹤楼\n昔人已乘黄鹤去,此地空余黄鹤楼。\n黄鹤一去不复返,白云千载空悠悠。\n晴川历历汉阳树,芳草萋萋鹦鹉洲。\n日暮乡关何处是?烟波江上使人愁。" /> </androidx.cardview.widget.CardView>2.使用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="#8003DAC5" /> <corners android:radius="10dp" /> </shape> </item> <item android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp"> <shape android:shape="rectangle"> <solid android:color="#6003DAC5" /> <corners android:radius="8dp" /> </shape> </item> <item android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp"> <shape android:layout_width="wrap_content" android:shape="rectangle"> <stroke android:width="1dp" android:color="#03DAC5" /> </shape> </item> <item android:bottom="6dp" android:left="6dp" android:right="6dp" android:top="6dp"> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="#03DAC5" /> </shape> </item> <item android:bottom="6dp" android:left="6dp" android:right="6dp" android:top="6dp"> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="#03DAC5" /> </shape> </item> <item android:bottom="7dp" android:left="7dp" android:right="7dp" android:top="7dp"> <color android:color="#141843"/> </item> </layer-list>3.ui切图很简单就是一张图片这里就不写了 .
4.自定义view实现阴影效果:
package com.example.focusimageview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; /** * @author: njb * @date: 2020/7/2 0002 0:08 * @desc: */ public class ShadowLayerView extends ViewGroup { private final float deltaLength; private final float cornerRadius; private final Paint mShadowPaint; private boolean drawShadow; public ShadowLayerView(Context context) { this(context, null); } public ShadowLayerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ShadowLayerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShadowLayerView); int shadowColor = a.getColor(R.styleable.ShadowLayerView_containerShadowColor, Color.RED); // int shadowColor = Color.RED; float shadowRadius = a.getDimension(R.styleable.ShadowLayerView_containerShadowRadius, 0); deltaLength = a.getDimension(R.styleable.ShadowLayerView_containerDeltaLength, 0); cornerRadius = a.getDimension(R.styleable.ShadowLayerView_containerCornerRadius, 0); float dx = a.getDimension(R.styleable.ShadowLayerView_deltaX, 0); float dy = a.getDimension(R.styleable.ShadowLayerView_deltaY, 0); drawShadow = a.getBoolean(R.styleable.ShadowLayerView_enable, true); a.recycle(); mShadowPaint = new Paint(); mShadowPaint.setStyle(Paint.Style.FILL); mShadowPaint.setAntiAlias(true); mShadowPaint.setColor(shadowColor); mShadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor); } @Override protected void onFinishInflate() { super.onFinishInflate(); } @Override protected void dispatchDraw(Canvas canvas) { if (drawShadow) { if (getLayerType() != LAYER_TYPE_SOFTWARE) { setLayerType(LAYER_TYPE_SOFTWARE, null); } View child = getChildAt(0); int left = child.getLeft(); int top = child.getTop(); int right = child.getRight(); int bottom = child.getBottom(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, mShadowPaint); } else { Path drawablePath = new Path(); drawablePath.moveTo(left + cornerRadius, top); drawablePath.arcTo(new RectF(left, top, left + 2 * cornerRadius, top + 2 * cornerRadius), -90, -90, false); drawablePath.lineTo(left, bottom - cornerRadius); drawablePath.arcTo(new RectF(left, bottom - 2 * cornerRadius, left + 2 * cornerRadius, bottom), 180, -90, false); drawablePath.lineTo(right - cornerRadius, bottom); drawablePath.arcTo(new RectF(right - 2 * cornerRadius, bottom - 2 * cornerRadius, right, bottom), 90, -90, false); drawablePath.lineTo(right, top + cornerRadius); drawablePath.arcTo(new RectF(right - 2 * cornerRadius, top, right, top + 2 * cornerRadius), 0, -90, false); drawablePath.close(); canvas.drawPath(drawablePath, mShadowPaint); } } super.dispatchDraw(canvas); } public void setDrawShadow(boolean drawShadow){ if (this.drawShadow == drawShadow){ return; } this.drawShadow = drawShadow; postInvalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (getChildCount() != 1) { throw new IllegalStateException("子View只能有一个"); } int measuredWidth = getMeasuredWidth(); int measuredHeight = getMeasuredHeight(); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); View child = getChildAt(0); LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); int childBottomMargin = (int) (Math.max(deltaLength, layoutParams.bottomMargin) + 1); int childLeftMargin = (int) (Math.max(deltaLength, layoutParams.leftMargin) + 1); int childRightMargin = (int) (Math.max(deltaLength, layoutParams.rightMargin) + 1); int childTopMargin = (int) (Math.max(deltaLength, layoutParams.topMargin) + 1); int widthMeasureSpecMode; int widthMeasureSpecSize; int heightMeasureSpecMode; int heightMeasureSpecSize; if (widthMode == MeasureSpec.UNSPECIFIED){ widthMeasureSpecMode = MeasureSpec.UNSPECIFIED; widthMeasureSpecSize = MeasureSpec.getSize(widthMeasureSpec); }else { if (layoutParams.width == LayoutParams.MATCH_PARENT) { widthMeasureSpecMode = MeasureSpec.EXACTLY; widthMeasureSpecSize = measuredWidth - childLeftMargin - childRightMargin; } else if (LayoutParams.WRAP_CONTENT == layoutParams.width) { widthMeasureSpecMode = MeasureSpec.AT_MOST; widthMeasureSpecSize = measuredWidth - childLeftMargin - childRightMargin; } else { widthMeasureSpecMode = MeasureSpec.EXACTLY; widthMeasureSpecSize = layoutParams.width; } } if (heightMode == MeasureSpec.UNSPECIFIED){ heightMeasureSpecMode = MeasureSpec.UNSPECIFIED; heightMeasureSpecSize = MeasureSpec.getSize(heightMeasureSpec); }else { if (layoutParams.height == LayoutParams.MATCH_PARENT) { heightMeasureSpecMode = MeasureSpec.EXACTLY; heightMeasureSpecSize = measuredHeight - childBottomMargin - childTopMargin; } else if (LayoutParams.WRAP_CONTENT == layoutParams.height) { heightMeasureSpecMode = MeasureSpec.AT_MOST; heightMeasureSpecSize = measuredHeight - childBottomMargin - childTopMargin; } else { heightMeasureSpecMode = MeasureSpec.EXACTLY; heightMeasureSpecSize = layoutParams.height; } } measureChild(child, MeasureSpec.makeMeasureSpec(widthMeasureSpecSize, widthMeasureSpecMode), MeasureSpec.makeMeasureSpec(heightMeasureSpecSize, heightMeasureSpecMode)); int parentWidthMeasureSpec = MeasureSpec.getMode(widthMeasureSpec); int parentHeightMeasureSpec = MeasureSpec.getMode(heightMeasureSpec); int height = measuredHeight; int width = measuredWidth; int childHeight = child.getMeasuredHeight(); int childWidth = child.getMeasuredWidth(); if (parentHeightMeasureSpec == MeasureSpec.AT_MOST){ height = childHeight + childTopMargin + childBottomMargin; } if (parentWidthMeasureSpec == MeasureSpec.AT_MOST){ width = childWidth + childRightMargin + childLeftMargin; } if (width < childWidth + 2 * deltaLength){ width = (int) (childWidth + 2 * deltaLength); } if (height < childHeight + 2 * deltaLength){ height = (int) (childHeight + 2 * deltaLength); } if (height != measuredHeight || width != measuredWidth){ setMeasuredDimension(width, height); } } static class LayoutParams extends MarginLayoutParams{ public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(MarginLayoutParams source) { super(source); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { View child = getChildAt(0); int measuredWidth = getMeasuredWidth(); int measuredHeight = getMeasuredHeight(); int childMeasureWidth = child.getMeasuredWidth(); int childMeasureHeight = child.getMeasuredHeight(); child.layout((measuredWidth - childMeasureWidth) / 2, (measuredHeight - childMeasureHeight) / 2, (measuredWidth + childMeasureWidth) / 2, (measuredHeight + childMeasureHeight) / 2); } } 三、以上是各种方式的实现代码:1.CardView实现简单,但是颜色不能自定义,2.shape层级方式也很简单,颜色也可以自定义,但是实现的效果不是很满意,3.ui切图方式效果很好,4.自定义view扩展性高。你以为这样就完了吗?NO!NO!NO!小伙子!你还是太天真了!CardView、自定义view方式都不能满足我的焦点阴影效果,因为我目前是开发TV项目,响应的是焦点事件,自定义view和CardView都只能实现单纯的阴影效果,焦点效果我尝试了没反应,于是结合上一篇也是用一个父布局嵌套一个图片,来设置焦点阴影效果,具体代码如下: <RelativeLayout android:id="@+id/rl_update" android:layout_width="88dp" android:layout_height="100dp" android:layout_marginTop="10dp" android:background="@drawable/bg_recyclerview_item" android:clipChildren="false" android:clipToPadding="false" android:focusable="true" android:focusableInTouchMode="true" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/cardview"> <ImageView android:id="@+id/iv_update" android:layout_width="76dp" android:layout_height="match_parent" android:layout_centerInParent="true" android:scaleType="fitCenter" android:src="@mipmap/ic_version_update" /> </RelativeLayout>四、完整的布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:card="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="200dp" android:layout_height="40dp" android:background="@drawable/shape_vip_select_bg" android:focusable="true" android:focusableInTouchMode="true" android:gravity="center" android:text="文本" android:textColor="@color/colorPrimary" app:layout_constraintLeft_toRightOf="parent" app:layout_constraintRight_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="200dp" android:layout_height="40dp" android:layout_marginTop="20dp" android:background="@drawable/shape_vip_select_bg" android:focusable="true" android:focusableInTouchMode="true" android:gravity="center" android:text="按钮" android:textColor="@color/colorPrimary" app:layout_constraintLeft_toRightOf="parent" app:layout_constraintRight_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" /> <RelativeLayout android:id="@+id/rl_image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginTop="20dp" android:background="@drawable/shape_vip_select_bg" android:focusable="true" android:focusableInTouchMode="true" app:layout_constraintLeft_toRightOf="parent" app:layout_constraintRight_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/button"> <ImageView android:layout_width="90dp" android:layout_height="90dp" android:layout_centerInParent="true" android:scaleType="fitXY" android:src="@mipmap/index_play_3" /> </RelativeLayout> <androidx.cardview.widget.CardView android:id="@+id/cardview" android:layout_width="300dp" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_margin="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/rl_image" card:cardCornerRadius="4dp" card:cardElevation="10dp" card:cardMaxElevation="10dp" tools:ignore="MissingConstraints"> <TextView android:layout_width="match_parent" android:layout_height="100dp" android:layout_gravity="center" android:gravity="center" android:text="黄鹤楼\n昔人已乘黄鹤去,此地空余黄鹤楼。\n黄鹤一去不复返,白云千载空悠悠。\n晴川历历汉阳树,芳草萋萋鹦鹉洲。\n日暮乡关何处是?烟波江上使人愁。" /> </androidx.cardview.widget.CardView> <RelativeLayout android:id="@+id/rl_update" android:layout_width="88dp" android:layout_height="100dp" android:layout_marginTop="10dp" android:background="@drawable/bg_recyclerview_item" android:clipChildren="false" android:clipToPadding="false" android:focusable="true" android:focusableInTouchMode="true" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/cardview"> <ImageView android:id="@+id/iv_update" android:layout_width="76dp" android:layout_height="match_parent" android:layout_centerInParent="true" android:scaleType="fitCenter" android:src="@mipmap/ic_version_update" /> </RelativeLayout> <com.example.focusimageview.ShadowLayerView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="80dp" android:layout_marginTop="50dp" app:containerCornerRadius="5dp" app:containerDeltaLength="5dp" app:containerShadowColor="@color/color_f6d8b9" app:containerShadowRadius="5dp" app:layout_constraintLeft_toRightOf="@id/rl_image" tools:ignore="MissingConstraints"> <View android:layout_width="80dp" android:layout_height="80dp" android:background="@color/white" /> </com.example.focusimageview.ShadowLayerView> </androidx.constraintlayout.widget.ConstraintLayout>最后,放一张完整的各种实现方式效果截图:
总结一下:可能实现方式不是很好,但是总算是实现了,关于图片实现阴影效果切图还算比较好之外,上一篇只能算完成任务,目前没有找到更好的解决方法,关于view的阴影和焦点阴影效果还是有区别的,如果小伙伴们有更好的方式,可以提出来,我会及时更正,学习好的方法和经验,不断成长.
例子的源码地址如下:https://gitee.com/jackning_admin/ImagViewFocus