1. elevation设置投影

android api(限制 21 以上 ),不过我们还是无法自定义投影颜色,透明度,大小等

2. UI制作.9图片

3. paint.setShadowLayer(float radius, float dx, float dy, int shadowColor);

这个方法可以达到这样一个效果,在使用canvas画图时给视图顺带上一层阴影效果。
简单介绍一下这几个参数:

  • radius: 阴影半径,主要可以控制阴影的模糊效果以及阴影扩散出去的大小。
  • dx:阴影在X轴方向上的偏移量
  • dy: 阴影在Y轴方向上的偏移量
  • shadowColor: 阴影颜色

思路:

  1. 通过在父布局中绘制整个阴影背景,子布局为所要显示的View
  2. 给控件设置背景,在背景上做投影效果

3.1 自定义ShadowLayout

public class ShadowLayout extends FrameLayout {         public static final String TAG = "ShadowLayout";    // 阴影颜色    private int mShadowColor;    // 阴影范围大小    private float mShadowRadius;    // 阴影圆角光滑度    private float mCornerRadius;    // 阴影x轴偏移量    private float mDx;    // 阴影y轴偏移量    private float mDy;    private boolean mInvalidateShadowOnSizeChanged = true;    private boolean mForceInvalidateShadow = false;    public ShadowLayout(Context context) {             super(context);        initView(context, null);    }    public ShadowLayout(Context context, AttributeSet attrs) {             super(context, attrs);        initView(context, attrs);    }    public ShadowLayout(Context context, AttributeSet attrs, int defStyleAttr) {             super(context, attrs, defStyleAttr);        initView(context, attrs);    }    @Override    protected int getSuggestedMinimumWidth() {             return 0;    }    @Override    protected int getSuggestedMinimumHeight() {             return 0;    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {             super.onSizeChanged(w, h, oldw, oldh);        if (w > 0 && h > 0 && (getBackground() == null || mInvalidateShadowOnSizeChanged || mForceInvalidateShadow)) {                 mForceInvalidateShadow = false;            setBackgroundCompat(w, h);        }    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {             super.onLayout(changed, left, top, right, bottom);        if (mForceInvalidateShadow) {                 mForceInvalidateShadow = false;            setBackgroundCompat(right - left, bottom - top);        }    }    public void setInvalidateShadowOnSizeChanged(boolean invalidateShadowOnSizeChanged) {             mInvalidateShadowOnSizeChanged = invalidateShadowOnSizeChanged;    }    public void invalidateShadow() {             mForceInvalidateShadow = true;        requestLayout();        invalidate();    }    private void initView(Context context, AttributeSet attrs) {             initAttributes(context, attrs);        /** x偏离量 **/        int xPadding = (int) (mShadowRadius + Math.abs(mDx));        /** y偏离量 **/        int yPadding = (int) (mShadowRadius + Math.abs(mDy));        setPadding(xPadding, yPadding, xPadding, yPadding);    }    private void initAttributes(Context context, AttributeSet attrs) {             TypedArray attr = getTypedArray(context, attrs, R.styleable.ShadowLayout);        if (attr == null) {                 return;        }        try {                 mCornerRadius = attr.getDimension(R.styleable.ShadowLayout_sl_cornerRadius, 0);            mShadowRadius = attr.getDimension(R.styleable.ShadowLayout_sl_shadowRadius, 10);            mDx = attr.getDimension(R.styleable.ShadowLayout_sl_dx, 0);            mDy = attr.getDimension(R.styleable.ShadowLayout_sl_dy, 0);            mShadowColor = attr.getColor(R.styleable.ShadowLayout_sl_shadowColor, Color.parseColor("#88757575"));        } finally {                 attr.recycle();        }    }    @SuppressWarnings("deprecation")    private void setBackgroundCompat(int w, int h) {             Log.i(TAG, "setBackgroundCompat: w=" + w + ", h=" + h);        Bitmap bitmap = createShadowBitmap(w, h, mCornerRadius, mShadowRadius, mDx, mDy, mShadowColor, Color.TRANSPARENT);        BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {                 setBackgroundDrawable(drawable);        } else {                 setBackground(drawable);        }    }    private TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr) {             return context.obtainStyledAttributes(attributeSet, attr, 0, 0);    }    private Bitmap createShadowBitmap(int shadowWidth, int shadowHeight, float cornerRadius, float shadowRadius,                                      float dx, float dy, int shadowColor, int fillColor) {             Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(output);        RectF shadowRect = new RectF(                shadowRadius,                shadowRadius,                shadowWidth - shadowRadius,                shadowHeight - shadowRadius);        if (dy > 0) {                 shadowRect.top += dy;            shadowRect.bottom -= dy;        } else if (dy < 0) {                 shadowRect.top += Math.abs(dy);            shadowRect.bottom -= Math.abs(dy);        }        if (dx > 0) {                 shadowRect.left += dx;            shadowRect.right -= dx;        } else if (dx < 0) {                 shadowRect.left += Math.abs(dx);            shadowRect.right -= Math.abs(dx);        }        Paint shadowPaint = new Paint();        shadowPaint.setAntiAlias(true);        shadowPaint.setColor(fillColor);        shadowPaint.setStyle(Paint.Style.FILL);        if (!isInEditMode()) {                 // 阴影的区域默认在绘制区域,阴影移动均相对于绘制区域            shadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor);        }        canvas.drawRoundRect(shadowRect, cornerRadius, cornerRadius, shadowPaint);        return output;    }}

attrs.xml

<declare-styleable name="ShadowLayout">   <attr name="sl_cornerRadius" format="dimension" />    <attr name="sl_shadowRadius" format="dimension" />    <attr name="sl_shadowColor" format="color" />    <attr name="sl_dx" format="dimension" />    <attr name="sl_dy" format="dimension" />declare-styleable>

例如要给TextView添加投影效果,使用如下

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:id="@+id/ll_content"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.base.module.customviewexer.ShadowLayout        android:layout_width="200dp"        android:layout_height="100dp"        android:layout_centerInParent="true"        app:sl_dy="5dp">        <TextView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="#ffffff"            android:gravity="center"            android:text="hello world!" />    </com.base.module.customviewexer.ShadowLayout></RelativeLayout>

显示效果

3.2 自定义背景ShadowDrawable

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, 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);    }    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 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);    }    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 = 0;        private int mOffsetY = 0;        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);        }    }}

布局文件

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/ll_content"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <TextView        android:id="@+id/tv_text"        android:layout_width="200dp"        android:layout_height="100dp"        android:layout_centerInParent="true"        android:background="#ffffff"        android:gravity="center"        android:text="hello world!" />RelativeLayout>

Activity中使用

public class MainActivity extends AppCompatActivity {         private TextView mTvText;    @Override    protected void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTvText = findViewById(R.id.tv_text);        /*         1. 需要设置的view        2. 背景主题颜色        3. 背景弧度半径        4. shadow的颜色        5. shadow的扩散半径大小        6. shadow的x轴偏移        7. shadow的y周偏移*/        ShadowDrawable.setShadowDrawable(mTvText, Color.parseColor("#ffffff"), 10, Color.parseColor("#25000000"), 10, 0, 10);    }}

实现效果

这种目前的限制是正常背景只能纯色,无法设置图片,选择器等,不过理解了原理,如果有需要也可以进行定制。

更多相关文章

  1. Android手势识别ViewFlipper触摸动画
  2. Android(安卓)Selector
  3. Android(安卓)ListView滑动后背景变黑问题解决方法
  4. Android实现导航菜单左右滑动效果
  5. Android(安卓)ListView滑动后背景变黑问题解决方法
  6. 在android中实现手势翻页效果
  7. Android实现导航菜单左右滑动效果
  8. android layout_weight 使用总结
  9. [android]关于开发无触摸屏(智能电视)的android selector无法实

随机推荐

  1. 如何将dict转换为spark map输出
  2. 当使用一个传送到另一个的python文件时,我
  3. matplotlib绘制符合论文要求的图片
  4. classmethod,staticmethod,还有类里面一般
  5. Python文件操作大全,随机删除文件夹内的任
  6. 获取错误“ValueError:int()的无效文字,基数
  7. C++各大有名库的介绍——准标准库Boost
  8. 从Python中的列表元素中删除URL
  9. python传递列表作为函数参数
  10. Python读取修改ini配置文件[ConfigParser