Android universal custom shadow control instance code

Directory introduction

01. What are the implementation methods of shadow effect

What are the implementation methods of shadow effect

Analysis of reasons for negating the first two schemes above?

Introduce some shadow effect schemes on the Internet

Does the shadow occupy space

02. Achieve shadow effect API

Think about how to achieve the view shadow effect?

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

Briefly introduce these parameters:

Finally, the method of setting the color is found. The shadow color of the view is controlled by setting the shadowcolor.

03. What should I pay attention to when setting shadows

Several attributes are involved, such as the width of the shadow and the distance from the view to the ViewGroup. If the view is as large as the parent layout, the shadow will not be easy to display. If you want to display it, you must set clipchildren = false.

In addition, the view comes with rounded corners. Most backgrounds have rounded corners. For example, the rounded corners in the above figure need to achieve the effect of highly restoring the shadow, that is, the rounded corners of the shadow are consistent with the background.

04. Common shapes achieve shadow effect

Multiple drawable overlays

The shadow effect code is shown below

<?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="@color/indexShadowColor_1" />
      <corners android:radius="5dip" />
      <padding
        android:bottom="1dp"
        android:left="1dp"
        android:right="1dp"
        android:top="1dp" />
    </shape>
  </item>
  <item>
    <shape android:shape="rectangle">
      <solid android:color="@color/indexShadowColor_2" />
      <corners android:radius="5dip" />
      <padding
        android:bottom="1dp"
        android:left="1dp"
        android:right="1dp"
        android:top="1dp" />
    </shape>
  </item>

  ……

  <item>
    <shape android:shape="rectangle">
      <corners android:radius="5dip" />
      <solid android:color="@color/indexColor" />
    </shape>
  </item>
</layer-list>

05. Custom shadow effect control

First, customize the properties

<declare-styleable name="ShadowLayout">
  <!--阴影的圆角大小-->
  <attr name="yc_cornerRadius" format="dimension" />
  <!--阴影的扩散范围(也可以理解为扩散程度)-->
  <attr name="yc_shadowLimit" format="dimension" />
  <!--阴影颜色-->
  <attr name="yc_shadowColor" format="color" />
  <!--x轴的偏移量-->
  <attr name="yc_dx" format="dimension" />
  <!--y轴的偏移量-->
  <attr name="yc_dy" format="dimension" />
  <!--左边是否显示阴影-->
  <attr name="yc_leftShow" format="boolean" />
  <!--右边是否显示阴影-->
  <attr name="yc_rightShow" format="boolean" />
  <!--上边是否显示阴影-->
  <attr name="yc_topShow" format="boolean" />
  <!--下面是否显示阴影-->
  <attr name="yc_bottomShow" format="boolean" />
</declare-styleable>

The code is shown below

/**
 * <pre>
 *   @author yangchong
 *   blog : https://github.com/yangchong211
 *   time : 2018/7/20
 *   desc : 自定义阴影
 *   revise:
 */
public class ShadowLayout extends FrameLayout {

  /**
   * 阴影颜色
   */
  private int mShadowColor;
  /**
   * 阴影的扩散范围(也可以理解为扩散程度)
   */
  private float mShadowLimit;
  /**
   * 阴影的圆角大小
   */
  private float mCornerRadius;
  /**
   * x轴的偏移量
   */
  private float mDx;
  /**
   * y轴的偏移量
   */
  private float mDy;
  /**
   * 左边是否显示阴影
   */
  private boolean leftShow;
  /**
   * 右边是否显示阴影
   */
  private boolean rightShow;
  /**
   * 上边是否显示阴影
   */
  private boolean topShow;
  /**
   * 下面是否显示阴影
   */
  private boolean bottomShow;

  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);

    int xPadding = (int) (mShadowLimit + Math.abs(mDx));
    int yPadding = (int) (mShadowLimit + Math.abs(mDy));
    int left;
    int right;
    int top;
    int bottom;
    if (leftShow) {
      left = xPadding;
    } else {
      left = 0;
    }

    if (topShow) {
      top = yPadding;
    } else {
      top = 0;
    }

    if (rightShow) {
      right = xPadding;
    } else {
      right = 0;
    }

    if (bottomShow) {
      bottom = yPadding;
    } else {
      bottom = 0;
    }

    setPadding(left,bottom);
  }

  @SuppressWarnings("deprecation")
  private void setBackgroundCompat(int w,int h) {
    Bitmap bitmap = createShadowBitmap(w,mCornerRadius,mShadowLimit,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 void initAttributes(Context context,AttributeSet attrs) {
    TypedArray attr = getTypedArray(context,R.styleable.ShadowLayout);
    if (attr == null) {
      return;
    }

    try {
      //认是显示
      leftShow = attr.getBoolean(R.styleable.ShadowLayout_yc_leftShow,true);
      rightShow = attr.getBoolean(R.styleable.ShadowLayout_yc_rightShow,true);
      bottomShow = attr.getBoolean(R.styleable.ShadowLayout_yc_bottomShow,true);
      topShow = attr.getBoolean(R.styleable.ShadowLayout_yc_topShow,true);

      mCornerRadius = attr.getDimension(R.styleable.ShadowLayout_yc_cornerRadius,0);
      mShadowLimit = attr.getDimension(R.styleable.ShadowLayout_yc_shadowLimit,0);
      mDx = attr.getDimension(R.styleable.ShadowLayout_yc_dx,0);
      mDy = attr.getDimension(R.styleable.ShadowLayout_yc_dy,0);
      mShadowColor = attr.getColor(R.styleable.ShadowLayout_yc_shadowColor,getResources().getColor(R.color.default_shadow_color));
    } finally {
      attr.recycle();
    }
  }

  private TypedArray getTypedArray(Context context,AttributeSet attributeSet,int[] attr) {
    return context.obtainStyledAttributes(attributeSet,attr,0);
  }

  private Bitmap createShadowBitmap(int shadowWidth,int shadowHeight,float cornerRadius,float shadowRadius,int shadowColor,int fillColor) {

    //根据宽高创建bitmap背景
    Bitmap output = Bitmap.createBitmap(shadowWidth,shadowHeight,Bitmap.Config.ARGB_8888);
    //用画板canvas进行绘制
    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,shadowPaint);
    return output;
  }
}
```

06. How to use the shadow control

Very simple, as shown below

<com.ns.yc.yccardviewlib.shadow.ShadowLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal"
  android:layout_marginTop="10dp"
  app:yc_cornerRadius="18dp"
  app:yc_dx="0dp"
  app:yc_dy="0dp"
  app:yc_shadowColor="#2a000000"
  app:yc_shadowLimit="5dp">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="36dp"
    android:background="@drawable/shape_show_"
    android:gravity="center"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:text="完全圆形圆角"
    android:textColor="#000" />

</com.ns.yc.yccardviewlib.shadow.ShadowLayout>

07. Precautions for using in recyclerview

In the createshadowbitmap method, you can actually see that you need to create a bitmap object. As we all know, bitmap is easy to cause excessive memory. If you set the shadow effect for the item in recyclerview, how to avoid repeated creation? You can use the cache at this time. So you can optimize the code based on the above.

First create the key, which is mainly used for the map set. Here, why use the object key as the key of the map? This is based on the idea of caching pictures in glide. You can pass in the bitmap name and width and height attribute when creating the key object, and you need to rewrite the hashcode and equals methods.

public class Key {

  private final String name;
  private final int width;
  private final int height;

  public Key(String name,int width,int height) {
    this.name = name;
    this.width = width;
    this.height = height;
  }

  public String getName() {
    return name;
  }

  public int getWidth() {
    return width;
  }

  public int getHeight() {
    return height;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Key key = (Key) o;
    if (width != key.width) {
      return false;
    }
    if (height != key.height) {
      return false;
    }
    return name != null ? name.equals(key.name) : key.name == null;
  }

  @Override
  public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + width;
    result = 31 * result + height;
    return result;
  }
}

Then the access operation is as follows

Key key = new Key("bitmap",shadowWidth,shadowHeight);
Bitmap output = cache.get(key);
if(output == null){
  //根据宽高创建bitmap背景
  output = Bitmap.createBitmap(shadowWidth,Bitmap.Config.ARGB_8888);
  cache.put(key,output);
  LogUtil.v("bitmap对象-----","----直接创建对象,然后存入缓存之中---");
} else {
  LogUtil.v("bitmap对象-----","----从缓存中取出对象---");
}

summary

The above is the whole content of this article. I hope the content of this article has a certain reference value for your study or work. Thank you for your support.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>