关于项目中圆角及特殊圆角的实际使用问题

语言: CN / TW / HK

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

目前我们日常使用的APP中大部分控件已经赋予了圆角的特性,圆角实际上的视觉体验也比较好,不像默认那么锋利,给人一种舒适的感觉.

以前我对控件使用圆角无非就三种方式.

  1. xml文件中shape的方式 ````

<ImageView
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_centerInParent="true"
    android:background="@drawable/shape_circle"
    />

```` shape_circle

````

<corners android:radius="8dp" />
<solid android:color="@color/teal_200"/>

````

image.png

  1. cardview ```

<androidx.cardview.widget.CardView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    app:cardCornerRadius="10dp"
    app:cardElevation="10dp">

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/ic_launcher_background"/>

</androidx.cardview.widget.CardView>

```

image.png

这种方式使用起来更为简单,而且CardView还自带阴影效果,直接设置属性即可,相较于xml减少了包体大小(聊胜于无). 3. 自定义View

```` /* * 自定义圆角view / public class RoundImageView extends AppCompatImageView {

private float width, height;
// 全部角度
private float radius, leftRadius, rightRadius;
// 是否是半圆
private boolean isRound = false;
// 底部圆角
private boolean isBottomRound = false;

public RoundImageView(Context context) {
    this(context, null);
}

public RoundImageView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}

/**
 * 初始化
 *
 * @param context
 * @param attrs
 */
private void init(Context context, AttributeSet attrs) {
    TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);
    radius = array.getDimensionPixelOffset(R.styleable.RoundImageView_radius, 0);
    leftRadius = array.getDimensionPixelOffset(R.styleable.RoundImageView_left_radius, 0);
    rightRadius = array.getDimensionPixelOffset(R.styleable.RoundImageView_right_radius, 0);
    if (radius == 0 && leftRadius == 0 && rightRadius == 0) {
        isRound = true;
    }
    if (leftRadius != 0 && rightRadius != 0) {
        isBottomRound = true;
    }
    array.recycle();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    width = getWidth();
    height = getHeight();
}

@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
    // 抗锯齿
    canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
    Path path = new Path();
    if (isRound) {
        radius = Math.min(width, height) / 2;
    }
    // 设置背景颜色透明
    canvas.drawColor(Color.TRANSPARENT);
    Drawable drawable = getDrawable();
    int w = this.getWidth();
    int h = this.getHeight();
    if (null != drawable && w > 0 && h > 0) {
        float[] rids;
        // 圆角的半径,依次为左上角xy半径,右上角,右下角,左下角
        if (isBottomRound) {
            rids = new float[]{0, 0, 0, 0,
                    rightRadius, rightRadius, leftRadius, leftRadius};
        } else {
            rids = new float[]{radius, radius, radius, radius,
                    radius, radius, radius, radius};
        }
        path.addRoundRect(new RectF(0, 0, w, h), rids, Path.Direction.CW);
        canvas.clipPath(path);
    }
    super.onDraw(canvas);
}

public void setRadius(int radius) {
    this.radius = radius;
    invalidate();
}

} ```` attrs

```

```

使用 ```

<com.jsxr.kotlintest.RoundImageView
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:src="@drawable/ic_launcher_background"
    app:radius="10dp"
    android:layout_centerInParent="true"/>

``` 效果

image.png

这种方式实用性上和第二种差不多,就是一开始麻烦点,你要么自己写,要么去百度复制,但是相较于官方提供的View,这种方式自定义性比较强,你个别角圆都行.

上面三种是我以前用过的方式,最近项目重构,为了简化代码,降低系统复杂度,就另外寻找了一个功能强大的View.

GradientDrawable

image.png

这是官方对于它的解释.在实际的使用上十分得劲,少了好多跟UI扯皮,自己做重复性工作的时间,下面请看代码

工具类

``` public class MyShape {

private static final ConcurrentHashMap<String, GradientDrawable> hashMap = new ConcurrentHashMap();

/**
 * 设置圆角
 * @param context
 * @param radius
 * @return
 */
public static GradientDrawable setMyShape(Context context, int radius) {
    int realRadius = ImageUtil.dp2px(context, radius);
    String radiusKey = String.valueOf(radius);
    if (hashMap.contains(radiusKey)) {
        return hashMap.get(radiusKey);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        if (radius != 0) {
            drawable.setCornerRadius(realRadius);
        }
        hashMap.put(radiusKey, drawable);
        return drawable;
    }
}


/**
 * @param context
 * @param radius
 * @param bg
 * @return 自定义圆角、背景图形
 */
public static GradientDrawable setMyShape(Context context, int radius, int bg) {
    int realRadius = ImageUtil.dp2px(context, radius);
    String key = String.valueOf(realRadius + bg);
    if (hashMap.contains(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        if (radius != 0) {
            drawable.setCornerRadius(realRadius);
        }
        if (bg != 0) {
            drawable.setColor(bg);
        }
        hashMap.put(key, drawable);
        return drawable;
    }
}

/**
 * 设置圆角、背景、透明度
 *
 * @param radius
 * @param bg
 * @param alpha
 * @return
 */
public static GradientDrawable setMyShapeWithAlpha(Context activity,int radius, int bg, int alpha) {
    int realRadius = ImageUtil.dp2px(activity, radius);
    String key = String.valueOf(realRadius + bg + alpha);
    if (hashMap.containsKey(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setCornerRadius(radius);
        drawable.setColor(bg);
        drawable.setAlpha(alpha);
        hashMap.put(key, drawable);
        return drawable;
    }
}

/**
 * @param activity
 * @param topLeft,topRight,bottomLeft,bottomRight
 * @param bg
 * @return 自定义圆角、背景布局
 */
public static GradientDrawable setMyShapeRadiusWithBg(Context activity, int topLeft, int topRight, int bottomLeft, int bottomRight, int bg) {
    topLeft = ImageUtil.dp2px(activity, topLeft);
    topRight = ImageUtil.dp2px(activity, topRight);
    bottomLeft = ImageUtil.dp2px(activity, bottomLeft);
    bottomRight = ImageUtil.dp2px(activity, bottomRight);
    String key = String.valueOf(topLeft + topRight + bottomLeft + bottomRight + bg);

    if (hashMap.containsKey(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        drawable.setCornerRadii(new float[]{topLeft, topLeft,
                topRight, topRight,
                bottomLeft, bottomLeft,
                bottomRight, bottomRight});
        if (bg != 0) {
            drawable.setColor(bg);
        }
        hashMap.put(key, drawable);
        return drawable;
    }
}

/**
 * @param activity
 * @param radius
 * @param width
 * @param bg
 * @return 设置带边框的椭圆+圆角+自定义边框颜色
 */
public static GradientDrawable setMyShapeStroke(Context activity, int radius, int width, int bg) {
    int realRadius = ImageUtil.dp2px(activity, radius);
    int realWidth = ImageUtil.dp2px(activity, width);
    String key = String.valueOf(realRadius + realWidth + bg);
    if (hashMap.containsKey(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        if (radius != 0) {
            drawable.setCornerRadius(realRadius);
        }
        if (width != 0 && bg != 0) {
            drawable.setStroke(realWidth, bg);
        }
        hashMap.put(key, drawable);
        return drawable;
    }

}


/**
 * @param activity
 * @param radius
 * @param width
 * @param strokeBg
 * @param bg
 * @return 设置带边框的椭圆,自定义内部颜色+自定义边框颜色
 */
public static GradientDrawable setMyshapeStroke(Context activity, int radius, int width, int strokeBg, int bg) {
    int realRadius = ImageUtil.dp2px(activity, radius);
    int realWidth = ImageUtil.dp2px(activity, width);
    String key = String.valueOf(realRadius + realRadius + strokeBg);
    if (hashMap.containsKey(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = new GradientDrawable();
        if (radius != 0) {
            drawable.setCornerRadius(realRadius);
        }
        if (width != 0 && strokeBg != 0) {
            drawable.setStroke(realWidth, strokeBg);
        }
        if (bg != 0) {
            drawable.setColor(bg);
        }
        hashMap.put(key, drawable);
        return drawable;
    }
}

/**
 * @param activity
 * @param topLeftRadius
 * @param topRightRadius
 * @param bottomRightRadius
 * @param bottomLeftRadius
 * @return 设置椭圆,并可以自定义内部颜色
 */
public static GradientDrawable setMyShapeRadius(Context activity, int bgColor, int topLeftRadius, int topRightRadius,
                                                int bottomRightRadius, int bottomLeftRadius) {
    GradientDrawable drawable = new GradientDrawable();
    if (bgColor != 0) {
        drawable.setColor(bgColor);
    }
    drawable.setCornerRadii(new float[]{ImageUtil.dp2px(activity, topLeftRadius), ImageUtil.dp2px(activity, topLeftRadius),
            ImageUtil.dp2px(activity, topRightRadius), ImageUtil.dp2px(activity, topRightRadius),
            ImageUtil.dp2px(activity, bottomRightRadius), ImageUtil.dp2px(activity, bottomRightRadius),
            ImageUtil.dp2px(activity, bottomLeftRadius), ImageUtil.dp2px(activity, bottomLeftRadius)});
    return drawable;
}

/**
 * @param startColor
 * @param endColor
 * @param angle
 * @return 设置渐变色
 */
@SuppressLint("WrongConstant")
public static GradientDrawable setGradient(Context activity, int startColor, int endColor, int topLeftRadius, int topRightRadius,
                                           int bottomRightRadius, int bottomLeftRadius, int angle) {
    int[] colors = {startColor, endColor};
    topLeftRadius = ImageUtil.dp2px(activity, topLeftRadius);
    topRightRadius = ImageUtil.dp2px(activity, topRightRadius);
    bottomLeftRadius = ImageUtil.dp2px(activity, bottomLeftRadius);
    bottomRightRadius = ImageUtil.dp2px(activity, bottomRightRadius);
    String key = String.valueOf(topLeftRadius + topRightRadius + bottomLeftRadius + bottomRightRadius + angle);
    if (hashMap.containsKey(key)) {
        return hashMap.get(key);
    } else {
        GradientDrawable drawable = null;
        if (angle == 0) {
            drawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors);
        } else if (angle == 90) {
            drawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, colors);
        } else if (angle == 180) {
            drawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, colors);
        } else if (angle == 270) {
            drawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors);
        } else if (angle == 315) {
            drawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR, colors);
        } else {
            drawable = new GradientDrawable();
        }
        drawable.setCornerRadii(new float[]{topLeftRadius, topLeftRadius,
                topRightRadius, topRightRadius,
                bottomRightRadius, bottomRightRadius,
                bottomLeftRadius, bottomLeftRadius});
        drawable.setGradientType(GradientDrawable.RECTANGLE);
        hashMap.put(key, drawable);
        return drawable;
    }
}

} ```

简单用哈希表存了下参数,这个应该叫享元模式,我觉得意义不是很大

适配器

``` class TestAdapter(val context: Context, val list: MutableList) : RecyclerView.Adapter() {

class ViewHolder(item: View) : RecyclerView.ViewHolder(item) {
    val layout: ConstraintLayout = item.findViewById(R.id.item_layout)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val inflate = LayoutInflater.from(context).inflate(R.layout.item, parent, false)
    return ViewHolder(inflate)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    when (position) {
        1 -> holder.layout.background = MyShape.setMyShape(context,
            10,
            ContextCompat.getColor(context, R.color.design_default_color_error)
        )
        2 -> holder.layout.background = MyShape.setMyShapeStroke(context,
            15,
            3,
            ContextCompat.getColor(context, R.color.design_default_color_primary_dark)
        )
        3 -> holder.layout.background = MyShape.setMyShapeRadius(context,
            ContextCompat.getColor(context, R.color.design_default_color_secondary_variant),
            0,
            10,
            0,
            10
        )

        4 -> holder.layout.background = MyShape.setMyShapeWithAlpha(context,
            10,
            ContextCompat.getColor(context, android.R.color.holo_green_dark),
            50
        )

        5 -> holder.layout.background = MyShape.setMyShapeRadiusWithBg(context,
            15,
            0,
            15,
            0,
            ContextCompat.getColor(context,
                android.R.color.holo_red_light))

        6 -> holder.layout.background = MyShape.setGradient(context,
            ContextCompat.getColor(context, android.R.color.holo_purple),
            ContextCompat.getColor(context, R.color.black),
            15,
            15,
            15,
            15,
            180)
    }
}

override fun getItemCount(): Int {
    return list.size
}

} ```

item ```

```

最终效果

image.png

上面的工具类是我在我们公司实际项目中经常使用的,实际上你们可以根据官方提供的API进行更多的创作.

这里面有一个坑,我不修改,我只在这里说哈 就是工具类中 setCornerRadii 他的圆角顺序应该是 左上,右上,右下,左下,里面有一个搞错了,你们使用的时候应该会发现,我就不修了,锻炼下你们的小脑瓜,哈哈哈哈哈.

我这里随便介绍一下API ``` setCornerRadius 设置圆角 setColor 设置背景颜色 setAlpha 设置透明度 setCornerRadii 设置多角度圆角 setStroke 设置边框+边框颜色 setGradientType 设置渐变风格 有四种

GradientDrawable.RECTANGLE 矩形 GradientDrawable.LINE 线 GradientDrawable.RING 圆环 GradientDrawable.OVAL 圆 ```

好啦!常用的大概就是这么多,其实工具类中我也写了点注释,大家不明白的直接评论区提问就好了,如果有时间的话我会尽量回复给大家.

下班!回家!!!