老规矩,无图言 × ,先上图

好像看不清,换个颜色再来一张吧,再加宽点

这颜色好丑哈哈哈哈哈哈,这是从黑到白色,你们用的时候一定要用有透明度的色值啊哈哈哈

接下来上代码!

import android.content.Context;import android.graphics.Color;import android.graphics.drawable.GradientDrawable;import android.support.constraint.ConstraintLayout;import android.view.View;import android.view.ViewGroup;import android.view.ViewParent;import android.widget.RelativeLayout;import org.jetbrains.annotations.NotNull;import java.lang.ref.WeakReference;import javax.annotation.Nullable;/** * @author lzd * * 阴影工具类 */public class ShadowUtil {    private WeakReference<View> needShadowView;    private WeakReference<ViewGroup> shadowView;    private int scaleUnitHor, scaleUnitVer;    private int lastWidth, lastHeight;    private static ShadowUtil Create(){        return Create(0, 0);    }    /**     * 在需要设置阴影的 view 尺寸变小 时,自动缩小的速度     * Warning 注意:传入的数值不可大于 设置阴影的 view 尺寸,否则会出问题     */    public static ShadowUtil Create(int scaleUnitHor, int scaleUnitVer) {        return new ShadowUtil(scaleUnitHor, scaleUnitVer);    }    private ShadowUtil (int scaleUnitHor, int scaleUnitVer) {        this.scaleUnitHor = scaleUnitHor;        this.scaleUnitVer = scaleUnitVer;    }    private void initListener(WeakReference<View> view, WeakReference<ViewGroup> shadowView) {        if (view != null && view.get() != null) {            view.get().getViewTreeObserver().addOnGlobalLayoutListener(() -> {                if (shadowView == null || shadowView.get() == null) {                    return;                }                setViewFamilyClipChildren(shadowView.get());                updateParentViewSize(shadowView.get(), view.get());            });        }    }    /**     * 获取当前 阴影 shadowView     */    public View getShadowView() {        return shadowView == null ? null : shadowView.get();    }    /**     * 获取当前 被添加阴影的 shadowView     */    public View getNeedShadowView() {        return needShadowView == null ? null : needShadowView.get();    }    /**     * 更新阴影尺寸信息     * @param parentView 阴影父布局     * @param view 需要设置阴影,正在监听尺寸 的 view     */    private void updateParentViewSize(ViewGroup parentView, View view) {        if (lastWidth != Math.max(view.getWidth() - scaleUnitHor, 0) ||                lastHeight != Math.max(view.getHeight() - scaleUnitVer, 0)) {            lastWidth = Math.max(view.getWidth() - scaleUnitHor, 0);            lastHeight = Math.max(view.getHeight() - scaleUnitVer, 0);            if (view instanceof RelativeLayout) {                parentView.setLayoutParams(new RelativeLayout.LayoutParams(lastWidth, lastHeight));            } else if (view instanceof ConstraintLayout) {                parentView.setLayoutParams(new ConstraintLayout.LayoutParams(lastWidth, lastHeight));            }        }    }    private void setViewFamilyClipChildren(ViewGroup view) {        if (view == null) {            return;        }        view.setClipChildren(false);        view.setClipToPadding(false);        ViewParent rooter = view.getParent();        while (rooter instanceof ViewGroup) {            ((ViewGroup) rooter).setClipChildren(false);            ((ViewGroup) rooter).setClipToPadding(false);            rooter = rooter.getParent();        }    }    /**     * 阴影 Group 数据结构     * 作为 {@link ShadowUtil#initShadowView(Context, View)} 的返回值生成     */    public static class ShadowGroup{        /**         * {@link ShadowUtil#initShadowView(Context, View)} 后的新的 content View         */        View contentView;        /**         * {@link ShadowUtil#initShadowView(Context, View)} 后的 阴影容器         */        View shadowContainer;        View needShadow;        private ShadowGroup(View contentView, View shadowContainer, View needShadow) {            this.contentView = contentView;            this.shadowContainer = shadowContainer;            this.needShadow = needShadow;        }        public View getContentView() {            return contentView;        }        public View getShadowContainer() {            return shadowContainer;        }        public View getNeedShadow() {            return needShadow;        }    }    /**     * 初始化需要添加阴影的 View     * @param needShadow 需要添加阴影的View ,注意:这个 View 不能有 parent     * @return  阴影的容器,需要再调用 {@link ShadowUtil#setViewBoundShadow(View)}     *          方法对此返回值设置阴影     */    public static @Nullable ShadowGroup initShadowView(Context context, View needShadow) {        if (needShadow.getParent() != null) {            return null;        }        ConstraintLayout contentView = new ConstraintLayout(context);        contentView.setLayoutParams(new ConstraintLayout.LayoutParams(                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));        // region 初始化需要添加阴影的 view 的相关参数        ConstraintLayout.LayoutParams needShadowViewParams;        if (needShadow.getLayoutParams() instanceof ConstraintLayout.LayoutParams) {            needShadowViewParams = (ConstraintLayout.LayoutParams) needShadow.getLayoutParams();        } else {            needShadowViewParams = new ConstraintLayout.LayoutParams(                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);        }        needShadowViewParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;        needShadowViewParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;        needShadowViewParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;        needShadowViewParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;        needShadowViewParams.setMargins(                DisplayUtils.dip2px(context, 8),                DisplayUtils.dip2px(context, 8),                DisplayUtils.dip2px(context, 8),                DisplayUtils.dip2px(context, 8)        );        needShadow.setLayoutParams(needShadowViewParams);        if (needShadow.getId() == View.NO_ID) {            needShadow.setId(View.generateViewId());        }        contentView.addView(needShadow);        // endregion        // region 初始化 shadow container        RelativeLayout shadowContainer = new RelativeLayout(context);        ConstraintLayout.LayoutParams shadowParams = new ConstraintLayout.LayoutParams(0, 0);        shadowParams.startToStart = needShadow.getId();        shadowParams.endToEnd = needShadow.getId();        shadowParams.topToTop = needShadow.getId();        shadowParams.bottomToBottom = needShadow.getId();        shadowContainer.setLayoutParams(shadowParams);        contentView.addView(shadowContainer);        // endregion        return new ShadowGroup(contentView, shadowContainer, needShadow);    }    /**     * 默认 设置阴影的方法     * @return 阴影相关结果集     */    public static ShadowUtil setViewBoundShadow(@NotNull View view) {        return setViewBoundShadow(view, ShadowUtil.Create());    }    /**     * 默认 设置阴影的方法     * @param shadowUtilValues 可使用 {@link ShadowUtil#Create(int, int)} 传入 hor 和 ver     * @return 阴影相关结果集     */    public static ShadowUtil setViewBoundShadow(@NotNull View view, @NotNull ShadowUtil shadowUtilValues) {        //region 默认缩放速度        if (shadowUtilValues.scaleUnitVer == 0) {            shadowUtilValues.scaleUnitVer = DisplayUtils.dip2px(view.getContext(), 2);        }        if (shadowUtilValues.scaleUnitHor == 0) {            shadowUtilValues.scaleUnitHor = DisplayUtils.dip2px(view.getContext(), 2);        }        //endregion        int shadowSize = DisplayUtils.dip2px(view.getContext(), 8);        // 默认阴影颜色        int startColor = Color.parseColor("#00000000");        int endColor = Color.parseColor("#050F0F0F");        return setViewBoundShadow(view, shadowSize, startColor, endColor, shadowUtilValues);    }    /**     * 为 view 设置阴影     * #Warning: 使用此方法后 viewTree的父容器 的 ClipChildren 属性会置true     * 这里可能会导致某些父布局里的元素超出容器,后期需要优化     *     * @param view       需要设置阴影的 view     *                   如果是 ViewGroup 需要是 relativeLayout 或 ConstrainLayout     *     * @param shadowSize 阴影宽度     * @param startColor 远离 view 一侧的色值     * @param endColor   贴近 view 一侧的色值     * @param shadowUtilValues 在需要设置阴影的 view 尺寸变小 时,自动缩小的速度 信息     * @return 失败 null ; 成功则返回阴影 所属的 parentView,可通过设置 visibility 控制是否显示     * 为 view 设置阴影     *     * #Note 使用此方法后 若导致某一父容器下的子控件超出,可在适当的位置 将容器 ClipChildren 属性置 false     */    public static ShadowUtil setViewBoundShadow(@NotNull View view, int shadowSize            , int startColor, int endColor, @NotNull ShadowUtil shadowUtilValues) {        if (shadowSize < 0) {            return null;        }        if (shadowUtilValues.getShadowView() != null) {            // 已有阴影,移除之前的阴影,并刷新数据            ((ViewGroup) shadowUtilValues.getShadowView().getParent())                    .removeView(shadowUtilValues.getShadowView());            shadowUtilValues.shadowView = null;            shadowUtilValues.needShadowView = null;            shadowUtilValues.lastHeight = shadowUtilValues.lastWidth = 0;        }        ViewGroup parentView = new ConstraintLayout(view.getContext());        if (view instanceof RelativeLayout || view instanceof ConstraintLayout) {            ((ViewGroup) view).addView(parentView);            if (view instanceof ConstraintLayout) {                ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(0, 0);                params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;                params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;                parentView.setLayoutParams(params);            }        } else if (!(view instanceof ViewGroup)){            parentView.addView(view);        } else {            return null;        }        class childValue {            int width;            int height;            int horPos;            int verPos;            GradientDrawable.Orientation orientation;            public childValue(int width, int height, int horPos, int verPos, GradientDrawable.Orientation orientation) {                this.width = width;                this.height = height;                this.horPos = horPos;                this.verPos = verPos;                this.orientation = orientation;            }        }        childValue[] childValues = new childValue[]{                new childValue(shadowSize, shadowSize, 1, 1, null),                new childValue(shadowSize, shadowSize, -1, 1, null),                new childValue(shadowSize, shadowSize, -1, -1, null),                new childValue(shadowSize, shadowSize, 1, -1, null),                new childValue(shadowSize, 0, 1, 0                        , GradientDrawable.Orientation.RIGHT_LEFT),                new childValue(shadowSize, 0, -1, 0                        , GradientDrawable.Orientation.LEFT_RIGHT),                new childValue(0, shadowSize, 0, -1                        , GradientDrawable.Orientation.TOP_BOTTOM),                new childValue(0, shadowSize, 0, 1                        , GradientDrawable.Orientation.BOTTOM_TOP),        };        int[] dirViewsId = new int[4];        for (int i = 0; i < childValues.length; i++) {            // region 尺寸            ConstraintLayout.LayoutParams childParams                    = new ConstraintLayout.LayoutParams(childValues[i].width, childValues[i].height);            View child = new View(view.getContext());            //endregion            // region 位置            if (childValues[i].horPos == 1) {                childParams.startToEnd = ConstraintLayout.LayoutParams.PARENT_ID;                childParams.leftMargin = shadowUtilValues.scaleUnitHor;            } else if (childValues[i].horPos == -1) {                childParams.endToStart = ConstraintLayout.LayoutParams.PARENT_ID;            }            if (childValues[i].verPos == 1) {                childParams.topToBottom = ConstraintLayout.LayoutParams.PARENT_ID;                childParams.topMargin = shadowUtilValues.scaleUnitVer;            } else if (childValues[i].verPos == -1) {                childParams.bottomToTop = ConstraintLayout.LayoutParams.PARENT_ID;            }            if (i < dirViewsId.length) {                // 前四个为四个角                child.setId(View.generateViewId());                dirViewsId[i] = child.getId();            } else {                // 后四个以四个角为基准来显示                if (childValues[i].horPos == 1 && childValues[i].verPos == 0) {                    childParams.bottomToTop = dirViewsId[0];                    childParams.topToBottom = dirViewsId[3];                } else if (childValues[i].horPos == -1 && childValues[i].verPos == 0) {                    childParams.topToBottom = dirViewsId[2];                    childParams.bottomToTop = dirViewsId[1];                } else if (childValues[i].horPos == 0 && childValues[i].verPos == -1) {                    childParams.endToStart = dirViewsId[3];                    childParams.startToEnd = dirViewsId[2];                } else if (childValues[i].horPos == 0 && childValues[i].verPos == 1) {                    childParams.endToStart = dirViewsId[0];                    childParams.startToEnd = dirViewsId[1];                }            }            //endregion            // region 背景图            GradientDrawable childBg = new GradientDrawable();            if (childValues[i].orientation != null) {                childBg.setColors(new int[]{startColor, endColor});                childBg.setOrientation(childValues[i].orientation);            } else {                childBg.setColors(new int[]{endColor, startColor});                childBg.setGradientRadius(shadowSize);                childBg.setGradientCenter(Math.max(0, -childValues[i].horPos)                        , Math.max(0, -childValues[i].verPos));                childBg.setGradientType(GradientDrawable.RADIAL_GRADIENT);                childBg.setCornerRadii(new float[]{                        childValues[i].horPos + childValues[i].verPos == -2 ? shadowSize : 0,                        childValues[i].horPos + childValues[i].verPos == -2 ? shadowSize : 0,                        childValues[i].horPos == 1 && childValues[i].verPos == -1 ? shadowSize : 0,                        childValues[i].horPos == 1 && childValues[i].verPos == -1 ? shadowSize : 0,                        childValues[i].horPos + childValues[i].verPos == 2 ? shadowSize : 0,                        childValues[i].horPos + childValues[i].verPos == 2 ? shadowSize : 0,                        childValues[i].horPos == -1 && childValues[i].verPos == 1 ? shadowSize : 0,                        childValues[i].horPos == -1 && childValues[i].verPos == 1 ? shadowSize : 0                });            }            //endregion            child.setBackground(childBg);            child.setLayoutParams(childParams);            parentView.addView(child);        }        shadowUtilValues.shadowView = new WeakReference<>(parentView);        shadowUtilValues.needShadowView = new WeakReference<>(view);        shadowUtilValues.initListener(new WeakReference<>(view)                , new WeakReference<>(parentView));        return shadowUtilValues;    }    /**     * 重新注册阴影的尺寸监听,用处:     * 例如:在 popupWindow 被 dismiss 后,视图的 GlobalLayoutListener 会被移除,     *      如果重新 show 后尺寸会发生改变,则无法监听到,建议在 show 时调用此方法重新监听;     *      如果重新 show 后尺寸不再改变,则无需调用此方法。     *     * @param shadowUtilValues 需要传入 {@link ShadowUtil#setViewBoundShadow(View)} 等方法的返回值     */    public static void reInit(ShadowUtil shadowUtilValues) {        if (shadowUtilValues.needShadowView == null || shadowUtilValues.needShadowView.get() == null                || shadowUtilValues.shadowView == null || shadowUtilValues.shadowView.get() == null) {            return;        }        shadowUtilValues.lastHeight = shadowUtilValues.lastWidth = 0;        shadowUtilValues.initListener(new WeakReference<>(shadowUtilValues.getNeedShadowView())                , new WeakReference<>((ViewGroup)shadowUtilValues.getShadowView()));        shadowUtilValues.needShadowView.get().requestLayout();    }}

使用方法示例:

//region 已经是 RelativeLayout 或 ConstrainLayoutShadowUtil shadowUtil = ShadowUtil.setViewBoundShadow(shadowContainer);View shadowView = shadowUtil.getShadowView();shadowView.setVisiblity(显示或隐藏);//endregion//region 不是 RelativeLayout 或 ConstrainLayoutShadowUtil.ShadowGroup shadowGroup = ShadowUtil.initShadowView(context, textView);if (shadowGroup == null) {    return;}ShadowUtil.setViewBoundShadow(shadowGroup.getShadowContainer());//endregion//region 使用自定义的收缩速度ShadowUtil shadowUtil = ShadowUtil.Create(DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 24));ShadowUtil.setViewBoundShadow(needShowShadow, shadowUtil);//endregion//region 布局变化后,监听失效后ShadowUtil.reInit(shadowUtil);//endregion

导致就这样吧,好累了上了一天班,关于注释里提到的 “可能会导致某些父布局里的元素超出容器” 的问题,如果各位有什么解决方案的话,请务必留言交流!!!

更多相关文章

  1. android设置中的Preferencescreen使用方法介绍与分析
  2. android shape的使用 自定义 控件形状
  3. Android(安卓)CoordinatorLayout使用 标题由图片变纯色
  4. Android全屏 去除标题栏和状态栏
  5. Android(安卓)WebView使用经验总结
  6. XUI 一个简洁而优雅的Android原生UI框架,解放你的双手!
  7. Android各版本间API的差异 - ActionBar
  8. 【android之锚定视图】
  9. Android(安卓)Design Support 介绍

随机推荐

  1. android 聊天室
  2. Android(安卓)Studio 抽屉效果控件Toolba
  3. android的快捷方式——应用程序界面
  4. Android TextView解析HTML内容
  5. Android Studio引入FFmpeg的方法
  6. Android studio -SVN 使用笔记
  7. Android直接执行shell命令
  8. Android之通过ContentProvider共享文件
  9. Android Studio选择默认运行build varian
  10. android byte[]数组,bitmap,drawable之间的