• Android实现自定义圆角边框渐变


    1.定义全部圆角的通用接口

    1. public interface IRadiusLayout {
    2. int DEFAULT_RADIUS = 0; // 默认没有圆角
    3. int SOLID_TYPE_SOLID = 0; // 实线
    4. int SOLID_TYPE_DASH = 1; // 虚线
    5. /**
    6. * 设置背景颜色状态列表
    7. *
    8. * @param bgColorStateList 背景颜色状态列表
    9. */
    10. void setBackgroundColor(ColorStateList bgColorStateList);
    11. /**
    12. * 设置边框虚线样式
    13. *
    14. * @param dashPathEffect 边框虚线样式
    15. */
    16. void setSolidDashPathEffect(DashPathEffect dashPathEffect);
    17. /**
    18. * 设置边框线颜色
    19. *
    20. * @param color 边框线颜色
    21. */
    22. void setSolidColor(int color);
    23. /**
    24. * 设置边框颜色状态列表
    25. *
    26. * @param solidColorStateList 边框颜色状态列表
    27. */
    28. void setSolidColor(ColorStateList solidColorStateList);
    29. /**
    30. * 设置圆角,四个圆角大小一样
    31. *
    32. * @param radius 圆角大小
    33. */
    34. void setRadius(int radius);
    35. /**
    36. * 设置圆角大小,分别设置
    37. *
    38. * @param leftTopRadius 左上角圆角大小
    39. * @param rightTopRadius 右上角圆角大小
    40. * @param rightBottomRadius 左下角圆角大小
    41. * @param leftBottomRadius 右下角圆角大小
    42. */
    43. void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius);
    44. /**
    45. * 设置左上角圆角大小
    46. *
    47. * @param leftTopRadius 左上角圆角大小
    48. */
    49. void setLeftTopRadius(int leftTopRadius);
    50. /**
    51. * 设置右上角圆角大小
    52. *
    53. * @param rightTopRadius 右上角圆角大小
    54. */
    55. void setRightTopRadius(int rightTopRadius);
    56. /**
    57. * 设置右下角圆角大小
    58. *
    59. * @param rightBottomRadius 右下角圆角大小
    60. */
    61. void setRightBottomRadius(int rightBottomRadius);
    62. /**
    63. * 设置左下角圆角大小
    64. *
    65. * @param leftBottomRadius 左下角圆角大小
    66. */
    67. void setLeftBottomRadius(int leftBottomRadius);
    68. /**
    69. * 设置背景渐变信息
    70. *
    71. * @param shapeType 渐变类型
    72. * @param shapeColors 渐变颜色数组
    73. */
    74. void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors);
    75. /**
    76. * 设置背景渐变信息
    77. *
    78. * @param shapeType 渐变类型
    79. * @param shapeColors 渐变颜色数组
    80. * @param shaderLinearOrientation 渐变方向
    81. */
    82. void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation);
    83. /**
    84. * 设置边框渐变信息
    85. *
    86. * @param shapeType 渐变类型
    87. * @param shapeColors 渐变颜色数组
    88. */
    89. void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors);
    90. /**
    91. * 设置边框渐变信息
    92. *
    93. * @param shapeType 渐变类型
    94. * @param shapeColors 渐变颜色数组
    95. * @param shaderLinearOrientation 渐变方向
    96. */
    97. void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation);
    98. }

    2.定义渐变工具ShaderUtils

    1. public class ShaderUtils {
    2. // 定义超出颜色范围的值作为非颜色值
    3. public static final int COLOR_VALUE_NONE = -0xFFFFFFFF;
    4. // 渐变类型
    5. public static final int SHADER_TYPE_LINEAR = 10; // 线性渐变
    6. public static final int SHADER_TYPE_RADIAL = 11; // 圆形渐变
    7. public static final int SHADER_TYPE_SWEEP = 12; // 扫描渐变
    8. public static final int SHADER_TYPE_NONE = -1; // 不要渐变
    9. // 线性渐变方向
    10. public static final int LINEAR_ORIENTATION_TOP_TO_BOTTOM = 110; // 从上到下
    11. public static final int LINEAR_ORIENTATION_BOTTOM_TO_TOP = 111; // 从下到上
    12. public static final int LINEAR_ORIENTATION_LEFT_TO_RIGHT = 220; // 从左到右
    13. public static final int LINEAR_ORIENTATION_RIGHT_TO_LEFT = 221; // 从右到左
    14. public static final int LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM = 330; // 从左上到右下
    15. public static final int LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP = 331; // 从右下到左上
    16. public static final int LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM = 440; // 从右上到左下
    17. public static final int LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP = 441; // 从左下到右上
    18. @IntDef(value = {SHADER_TYPE_LINEAR, SHADER_TYPE_RADIAL, SHADER_TYPE_SWEEP})
    19. public @interface ShaderType {
    20. }
    21. @IntDef(value = {LINEAR_ORIENTATION_TOP_TO_BOTTOM, LINEAR_ORIENTATION_BOTTOM_TO_TOP,
    22. LINEAR_ORIENTATION_LEFT_TO_RIGHT, LINEAR_ORIENTATION_RIGHT_TO_LEFT,
    23. LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM, LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP,
    24. LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM, LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP})
    25. public @interface LinearOrientation {
    26. }
    27. private ShaderUtils() {
    28. }
    29. public static Shader createShader(@ShaderType int shaderType, int width, int height, int[] colors, @LinearOrientation int orientation) {
    30. if (shaderType == SHADER_TYPE_LINEAR) {
    31. return createLinearGradient(width, height, colors, orientation);
    32. } else if (shaderType == SHADER_TYPE_RADIAL) {
    33. return createRadialGradient(width, height, colors);
    34. } else if (shaderType == SHADER_TYPE_SWEEP) {
    35. return createSweepGradient(width, height, colors);
    36. } else {
    37. return createLinearGradient(width, height, colors, orientation);
    38. }
    39. }
    40. public static LinearGradient createLinearGradient(int width, int height, int[] colors, @LinearOrientation int orientation) {
    41. LinearGradient linearGradient;
    42. int halfWidth = width / 2;
    43. int halfHeight = height / 2;
    44. if (LINEAR_ORIENTATION_TOP_TO_BOTTOM == orientation) {
    45. linearGradient = new LinearGradient(halfWidth, 0, halfWidth, height, colors, null, Shader.TileMode.CLAMP);
    46. } else if (LINEAR_ORIENTATION_BOTTOM_TO_TOP == orientation) {
    47. linearGradient = new LinearGradient(halfWidth, height, halfWidth, 0, colors, null, Shader.TileMode.CLAMP);
    48. } else if (LINEAR_ORIENTATION_LEFT_TO_RIGHT == orientation) {
    49. linearGradient = new LinearGradient(0, halfHeight, width, halfHeight, colors, null, Shader.TileMode.CLAMP);
    50. } else if (LINEAR_ORIENTATION_RIGHT_TO_LEFT == orientation) {
    51. linearGradient = new LinearGradient(width, halfHeight, 0, halfHeight, colors, null, Shader.TileMode.CLAMP);
    52. } else if (LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM == orientation) {
    53. linearGradient = new LinearGradient(0, 0, width, height, colors, null, Shader.TileMode.CLAMP);
    54. } else if (LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP == orientation) {
    55. linearGradient = new LinearGradient(width, height, 0, 0, colors, null, Shader.TileMode.CLAMP);
    56. } else if (LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM == orientation) {
    57. linearGradient = new LinearGradient(width, 0, 0, height, colors, null, Shader.TileMode.CLAMP);
    58. } else if (LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP == orientation) {
    59. linearGradient = new LinearGradient(0, height, width, 0, colors, null, Shader.TileMode.CLAMP);
    60. } else {
    61. linearGradient = new LinearGradient(halfWidth, 0, halfWidth, height, colors, null, Shader.TileMode.CLAMP);
    62. }
    63. return linearGradient;
    64. }
    65. public static RadialGradient createRadialGradient(int width, int height, int[] colors) {
    66. int halfWidth = width / 2;
    67. int halfHeight = height / 2;
    68. return new RadialGradient(halfWidth, halfHeight, Math.min(halfWidth, halfHeight), colors, null, Shader.TileMode.CLAMP);
    69. }
    70. public static SweepGradient createSweepGradient(int width, int height, int[] colors) {
    71. int halfWidth = width / 2;
    72. int halfHeight = height / 2;
    73. return new SweepGradient(halfWidth, halfHeight, colors, null);
    74. }
    75. public static int[] createColorsArray(int startColor, int middleColor, int middleColor2, int middleColor3, int endColor) {
    76. List colors = new ArrayList<>();
    77. if (startColor != COLOR_VALUE_NONE) {
    78. colors.add(startColor);
    79. }
    80. if (middleColor != COLOR_VALUE_NONE) {
    81. colors.add(middleColor);
    82. }
    83. if (middleColor2 != COLOR_VALUE_NONE) {
    84. colors.add(middleColor2);
    85. }
    86. if (middleColor3 != COLOR_VALUE_NONE) {
    87. colors.add(middleColor3);
    88. }
    89. if (endColor != COLOR_VALUE_NONE) {
    90. colors.add(endColor);
    91. }
    92. if (colors.size() > 0) {
    93. int[] shaderColors = new int[colors.size()];
    94. for (int i = 0; i < colors.size(); i++) {
    95. shaderColors[i] = colors.get(i);
    96. }
    97. return shaderColors;
    98. }
    99. return null;
    100. }
    101. public static int[] createColorsArray(int startColor, int middleColor, int endColor) {
    102. List colors = new ArrayList<>();
    103. if (startColor != COLOR_VALUE_NONE) {
    104. colors.add(startColor);
    105. }
    106. if (middleColor != COLOR_VALUE_NONE) {
    107. colors.add(middleColor);
    108. }
    109. if (endColor != COLOR_VALUE_NONE) {
    110. colors.add(endColor);
    111. }
    112. if (colors.size() > 0) {
    113. int[] shaderColors = new int[colors.size()];
    114. for (int i = 0; i < colors.size(); i++) {
    115. shaderColors[i] = colors.get(i);
    116. }
    117. return shaderColors;
    118. }
    119. return null;
    120. }
    121. }

    3.圆角工具RadiusUtils

    1. public class RadiusUtils {
    2. public static final int TYPE_NONE = 0x0000; // 没有圆角
    3. public static final int TYPE_RADIUS = 0x0001; // 圆角形状
    4. public static final int TYPE_CIRCLE = 0x0002; // 整体为圆形
    5. public static final int TYPE_OVAL_LEFT = 0x0004; // 左边为椭圆
    6. public static final int TYPE_OVAL_TOP = 0x0008; // 上边为椭圆
    7. public static final int TYPE_OVAL_RIGHT = 0x0010; // 右边为椭圆
    8. public static final int TYPE_OVAL_BOTTOM = 0x0020; // 下边为椭圆
    9. /**
    10. * 计算背景路径 ConvexPath
    11. * ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)
    12. *
    13. * @param leftTopRadius 左上角圆角大小
    14. * @param rightTopRadius 右上角圆角大小
    15. * @param leftBottomRadius 左下角圆角大小
    16. * @param rightBottomRadius 右下角圆角大小
    17. * @param width 矩形宽
    18. * @param height 矩形高
    19. * @return 结果Path
    20. */
    21. public static Path calculateBgPath(int leftTopRadius, int rightTopRadius,
    22. int leftBottomRadius, int rightBottomRadius,
    23. int width, int height) {
    24. return calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, true);
    25. }
    26. /**
    27. * 计算背景路径 ConvexPath
    28. * ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)
    29. *
    30. * @param leftTopRadius 左上角圆角大小
    31. * @param rightTopRadius 右上角圆角大小
    32. * @param leftBottomRadius 左下角圆角大小
    33. * @param rightBottomRadius 右下角圆角大小
    34. * @param width 矩形宽
    35. * @param height 矩形高
    36. * @param isGetType 是否需要判断类型,当为 true 时可能返回非 Convex Path
    37. * @return 结果Path
    38. */
    39. public static Path calculateBgPath(int leftTopRadius, int rightTopRadius,
    40. int leftBottomRadius, int rightBottomRadius,
    41. int width, int height, boolean isGetType) {
    42. leftTopRadius = Math.max(leftTopRadius, 0);
    43. rightTopRadius = Math.max(rightTopRadius, 0);
    44. leftBottomRadius = Math.max(leftBottomRadius, 0);
    45. rightBottomRadius = Math.max(rightBottomRadius, 0);
    46. if (isGetType) {
    47. int type = getType(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    48. if (type == TYPE_NONE)
    49. return calculateRectBgPath(width, height);
    50. if (type == TYPE_RADIUS)
    51. return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    52. if (type == TYPE_CIRCLE) {
    53. Path resultPath = new Path();
    54. RectF rectF = new RectF();
    55. rectF.set(0, 0, width, height);
    56. resultPath.addCircle(rectF.centerX(), rectF.centerY(), Math.min(rectF.width(), rectF.height()) / 2f, Path.Direction.CW);
    57. return resultPath;
    58. }
    59. Path resultPath = new Path();
    60. if (((type & TYPE_OVAL_LEFT) != 0) && (type & TYPE_OVAL_RIGHT) != 0) {
    61. // 左右都半圆
    62. RectF arcRectF = new RectF();
    63. // 左边半圆
    64. arcRectF.set(0, 0, height, height);
    65. resultPath.addArc(arcRectF, 90, 180);
    66. // 上边的线
    67. resultPath.lineTo(width - height / 2f, 0);
    68. // 右边半圆
    69. RectF rightRectF = new RectF();
    70. rightRectF.set(width - height, 0, width, height);
    71. resultPath.addArc(rightRectF, -90, 180);
    72. // 下边的线
    73. resultPath.lineTo(height / 2f, height);
    74. } else if (((type & TYPE_OVAL_TOP) != 0) && (type & TYPE_OVAL_BOTTOM) != 0) {
    75. // 上下都半圆
    76. RectF arcRectF = new RectF();
    77. // 上边半圆
    78. arcRectF.set(0, 0, width, width);
    79. resultPath.addArc(arcRectF, 180, 180);
    80. // 右边的线
    81. resultPath.lineTo(width, height - width / 2f);
    82. // 下边半圆
    83. RectF rightRectF = new RectF();
    84. rightRectF.set(0, height - width, width, height);
    85. resultPath.addArc(rightRectF, 0, 180);
    86. // 左边的线
    87. resultPath.lineTo(0, width / 2f);
    88. } else if ((type & TYPE_OVAL_LEFT) != 0) {
    89. RectF arcRectF = new RectF();
    90. // 左半圆
    91. arcRectF.set(0, 0, height, height);
    92. resultPath.addArc(arcRectF, 90, 180);
    93. // 上边的线
    94. resultPath.lineTo(width - rightTopRadius, 0);
    95. // 右上角
    96. resultPath.quadTo(width, 0, width, rightTopRadius);
    97. // 右边线
    98. resultPath.lineTo(width, height - rightBottomRadius);
    99. // 右下角
    100. resultPath.quadTo(width, height, width - rightBottomRadius, height);
    101. // 下边的线
    102. resultPath.lineTo(height / 2f, height);
    103. } else if ((type & TYPE_OVAL_RIGHT) != 0) {
    104. // 上边的线
    105. resultPath.moveTo(leftTopRadius, 0);
    106. resultPath.lineTo(width - height / 2f, 0);
    107. // 右边半圆
    108. RectF rightRectF = new RectF();
    109. rightRectF.set(width - height, 0, width, height);
    110. resultPath.addArc(rightRectF, -90, 180);
    111. // 下边线
    112. resultPath.lineTo(leftBottomRadius, height);
    113. // 左下角
    114. resultPath.quadTo(0, height, 0, height - leftBottomRadius);
    115. // 左边线
    116. resultPath.lineTo(0, leftTopRadius);
    117. // 左上角
    118. resultPath.quadTo(0, 0, leftTopRadius, 0);
    119. } else if ((type & TYPE_OVAL_TOP) != 0) {
    120. RectF arcRectF = new RectF();
    121. // 上半圆
    122. arcRectF.set(0, 0, width, width);
    123. resultPath.addArc(arcRectF, 180, 180);
    124. // 右边的线
    125. resultPath.lineTo(width, height - rightBottomRadius);
    126. // 右下角
    127. resultPath.quadTo(width, height, width - rightBottomRadius, height);
    128. // 底部线
    129. resultPath.lineTo(leftBottomRadius, height);
    130. // 左下角
    131. resultPath.quadTo(0, height, 0, height - leftBottomRadius);
    132. // 左边的线
    133. resultPath.lineTo(0, width / 2f);
    134. } else if ((type & TYPE_OVAL_BOTTOM) != 0) {
    135. // 上边线
    136. resultPath.moveTo(leftTopRadius, 0);
    137. resultPath.lineTo(width - rightTopRadius, 0);
    138. // 右上角
    139. resultPath.quadTo(width, 0, width, rightTopRadius);
    140. // 右边线
    141. resultPath.lineTo(width, height - width / 2f);
    142. // 下半圆
    143. RectF rightRectF = new RectF();
    144. rightRectF.set(0, height - width, width, height);
    145. resultPath.addArc(rightRectF, 0, 180);
    146. // 左边线
    147. resultPath.lineTo(0, leftTopRadius);
    148. // 左上角
    149. resultPath.quadTo(0, 0, leftTopRadius, 0);
    150. } else {
    151. return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    152. }
    153. return resultPath;
    154. }
    155. return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    156. }
    157. /**
    158. * 计算圆角矩形背景路径
    159. */
    160. private static Path calculateRadiusBgPath(int leftTopRadius, int rightTopRadius, int leftBottomRadius,
    161. int rightBottomRadius, int width, int height) {
    162. float leftTopRadiusLeft, leftTopRadiusTop; // 左上角
    163. float leftBottomRadiusLeft, leftBottomRadiusBottom; // 左下角
    164. float rightTopRadiusRight, rightTopRadiusTop; // 右上角
    165. float rightBottomRadiusRight, rightBottomRadiusBottom; // 右下角
    166. int[] sideTop = calculateRadiusLength(leftTopRadius, rightTopRadius, width); // 上同边
    167. int[] sideBottom = calculateRadiusLength(leftBottomRadius, rightBottomRadius, width); // 下同边
    168. int[] sideLeft = calculateRadiusLength(leftTopRadius, leftBottomRadius, height); // 左同边
    169. int[] sideRight = calculateRadiusLength(rightTopRadius, rightBottomRadius, height); // 右同边
    170. leftTopRadiusTop = sideTop[0];
    171. rightTopRadiusTop = sideTop[1];
    172. leftBottomRadiusBottom = sideBottom[0];
    173. rightBottomRadiusBottom = sideBottom[1];
    174. leftTopRadiusLeft = sideLeft[0];
    175. leftBottomRadiusLeft = sideLeft[1];
    176. rightTopRadiusRight = sideRight[0];
    177. rightBottomRadiusRight = sideRight[1];
    178. Path resultPath = new Path();
    179. // 四个角:右上,右下,左下,左上
    180. resultPath.moveTo(leftTopRadiusTop, 0);
    181. resultPath.lineTo(width - rightTopRadiusTop, 0);
    182. resultPath.quadTo(width, 0, width, rightTopRadiusRight);
    183. resultPath.lineTo(width, height - rightBottomRadiusRight);
    184. resultPath.quadTo(width, height, width - rightBottomRadiusBottom, height);
    185. resultPath.lineTo(leftBottomRadiusBottom, height);
    186. resultPath.quadTo(0, height, 0, height - leftBottomRadiusLeft);
    187. resultPath.lineTo(0, leftTopRadiusLeft);
    188. resultPath.quadTo(0, 0, leftTopRadiusTop, 0);
    189. return resultPath;
    190. }
    191. /**
    192. * 计算直角矩形背景路径
    193. *
    194. * @param width 宽
    195. * @param height 高
    196. */
    197. private static Path calculateRectBgPath(int width, int height) {
    198. Path result = new Path();
    199. result.moveTo(0, 0);
    200. result.lineTo(width, 0);
    201. result.lineTo(width, height);
    202. result.lineTo(0, height);
    203. result.close();
    204. return result;
    205. }
    206. /**
    207. * 计算边框的边框路径 ConvexPath
    208. * ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)
    209. *
    210. * @param leftTopRadius 左上角圆角大小
    211. * @param rightTopRadius 右上角圆角大小
    212. * @param leftBottomRadius 左下角圆角大小
    213. * @param rightBottomRadius 右下角圆角大小
    214. * @param width 矩形宽
    215. * @param height 矩形高
    216. * @param solidWidth 边框宽度
    217. * @return 结果Path 数组,path[0]:四条边 path[1]:四个角的路径
    218. */
    219. public static Path[] calculateSocketPath(int leftTopRadius, int rightTopRadius,
    220. int leftBottomRadius, int rightBottomRadius,
    221. int width, int height, int solidWidth) {
    222. leftTopRadius = Math.max(leftTopRadius, 0);
    223. rightTopRadius = Math.max(rightTopRadius, 0);
    224. leftBottomRadius = Math.max(leftBottomRadius, 0);
    225. rightBottomRadius = Math.max(rightBottomRadius, 0);
    226. int type = getType(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    227. if (type == TYPE_NONE)
    228. return new Path[]{calculateRectSocketPath(width, height, solidWidth)};
    229. if (type == TYPE_RADIUS)
    230. return calculateRadiusSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    231. if (type == TYPE_CIRCLE) {
    232. Path[] result = new Path[1];
    233. Path resultPath = new Path();
    234. RectF rectF = new RectF();
    235. rectF.set(0, 0, width, height);
    236. // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
    237. // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
    238. // 矩形缩小边框的一半
    239. float newWidth = solidWidth / 2.0f;
    240. rectF.inset(newWidth, newWidth);
    241. resultPath.addCircle(rectF.centerX(), rectF.centerY(), Math.min(rectF.width(), rectF.height()) / 2f, Path.Direction.CW);
    242. result[0] = resultPath;
    243. return result;
    244. }
    245. Path[] result = new Path[1];
    246. Path resultPath = new Path();
    247. // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
    248. // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
    249. // 矩形缩小边框的一半
    250. float newWidth = solidWidth / 2.0f;
    251. if (((type & TYPE_OVAL_LEFT) != 0) && (type & TYPE_OVAL_RIGHT) != 0) {
    252. // 左边半圆
    253. RectF leftRectF = new RectF();
    254. leftRectF.set(newWidth, newWidth, height, height - newWidth);
    255. resultPath.addArc(leftRectF, 90, 180);
    256. // 增加上边的线
    257. resultPath.lineTo(width - height / 2f, newWidth);
    258. // 右边半圆
    259. RectF rightRectF = new RectF();
    260. rightRectF.set(width - height, newWidth, width - newWidth, height - newWidth);
    261. resultPath.addArc(rightRectF, -90, 180);
    262. // 增加下边的线
    263. resultPath.lineTo(height / 2f, height - newWidth);
    264. } else if (((type & TYPE_OVAL_TOP) != 0) && (type & TYPE_OVAL_BOTTOM) != 0) {
    265. // 上边半圆
    266. RectF leftRectF = new RectF();
    267. leftRectF.set(newWidth, newWidth, width - newWidth, width);
    268. resultPath.addArc(leftRectF, 180, 180);
    269. // 增加右边的线
    270. resultPath.lineTo(width - newWidth, height - width / 2f);
    271. // 下边半圆
    272. RectF rightRectF = new RectF();
    273. rightRectF.set(newWidth, height - width, width - newWidth, height - newWidth);
    274. resultPath.addArc(rightRectF, 0, 180);
    275. // 增加左边的线
    276. resultPath.lineTo(newWidth, width / 2f);
    277. } else if ((type & TYPE_OVAL_LEFT) != 0) {
    278. // 左半圆
    279. RectF arcRectF = new RectF();
    280. arcRectF.set(newWidth, newWidth, height, height - newWidth);
    281. resultPath.addArc(arcRectF, 90, 180);
    282. // 上边的线
    283. resultPath.lineTo(width - rightTopRadius, newWidth);
    284. // 右上角
    285. resultPath.quadTo(width, newWidth, width - newWidth, rightTopRadius);
    286. // 右边线
    287. resultPath.lineTo(width - newWidth, height - rightBottomRadius);
    288. // 右下角
    289. resultPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadius, height - newWidth);
    290. // 下边的线
    291. resultPath.lineTo(height / 2f, height - newWidth);
    292. } else if ((type & TYPE_OVAL_RIGHT) != 0) {
    293. // 上边的线
    294. resultPath.moveTo(leftTopRadius, newWidth);
    295. resultPath.lineTo(width - height / 2f, newWidth);
    296. // 右边半圆
    297. RectF rightRectF = new RectF();
    298. rightRectF.set(width - height, newWidth, width - newWidth, height - newWidth);
    299. resultPath.addArc(rightRectF, -90, 180);
    300. // 下边线
    301. resultPath.lineTo(leftBottomRadius, height - newWidth);
    302. // 左下角
    303. resultPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadius);
    304. // 左边线
    305. resultPath.lineTo(newWidth, leftTopRadius);
    306. // 左上角
    307. resultPath.quadTo(newWidth, newWidth, leftTopRadius, newWidth);
    308. } else if ((type & TYPE_OVAL_TOP) != 0) {
    309. // 上半圆
    310. RectF arcRectF = new RectF();
    311. arcRectF.set(newWidth, newWidth, width - newWidth, width);
    312. resultPath.addArc(arcRectF, 180, 180);
    313. // 右边的线
    314. resultPath.lineTo(width - newWidth, height - rightBottomRadius);
    315. // 右下角
    316. resultPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadius, height - newWidth);
    317. // 底部线
    318. resultPath.lineTo(leftBottomRadius, height - newWidth);
    319. // 左下角
    320. resultPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadius);
    321. // 左边的线
    322. resultPath.lineTo(newWidth, width / 2f);
    323. } else if ((type & TYPE_OVAL_BOTTOM) != 0) {
    324. // 上边线
    325. resultPath.moveTo(leftTopRadius, newWidth);
    326. resultPath.lineTo(width - rightTopRadius, newWidth);
    327. // 右上角
    328. resultPath.quadTo(width, newWidth, width - newWidth, rightTopRadius);
    329. // 右边线
    330. resultPath.lineTo(width - newWidth, height - width / 2f);
    331. // 下半圆
    332. RectF rightRectF = new RectF();
    333. rightRectF.set(newWidth, height - width, width - newWidth, height - newWidth);
    334. resultPath.addArc(rightRectF, 0, 180);
    335. // 左边线
    336. resultPath.lineTo(newWidth, leftTopRadius);
    337. // 左上角
    338. resultPath.quadTo(newWidth, newWidth, leftTopRadius, newWidth);
    339. } else {
    340. return calculateRadiusSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    341. }
    342. result[0] = resultPath;
    343. return result;
    344. }
    345. /**
    346. * 计算圆角矩形的边框路径
    347. */
    348. private static Path[] calculateRadiusSocketPath(int leftTopRadius, int rightTopRadius,
    349. int leftBottomRadius, int rightBottomRadius,
    350. int width, int height, int solidWidth) {
    351. Path[] result = new Path[2];
    352. Path solidPath = new Path();
    353. Path radiusPath = new Path();
    354. float leftTopRadiusLeft, leftTopRadiusTop; // 左上角
    355. float leftBottomRadiusLeft, leftBottomRadiusBottom; // 左下角
    356. float rightTopRadiusRight, rightTopRadiusTop; // 右上角
    357. float rightBottomRadiusRight, rightBottomRadiusBottom; // 右下角
    358. int[] sideTop = calculateRadiusLength(leftTopRadius, rightTopRadius, width); // 上同边
    359. int[] sideBottom = calculateRadiusLength(leftBottomRadius, rightBottomRadius, width); // 下同边
    360. int[] sideLeft = calculateRadiusLength(leftTopRadius, leftBottomRadius, height); // 左同边
    361. int[] sideRight = calculateRadiusLength(rightTopRadius, rightBottomRadius, height); // 右同边
    362. leftTopRadiusTop = sideTop[0];
    363. rightTopRadiusTop = sideTop[1];
    364. leftBottomRadiusBottom = sideBottom[0];
    365. rightBottomRadiusBottom = sideBottom[1];
    366. leftTopRadiusLeft = sideLeft[0];
    367. leftBottomRadiusLeft = sideLeft[1];
    368. rightTopRadiusRight = sideRight[0];
    369. rightBottomRadiusRight = sideRight[1];
    370. // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
    371. // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
    372. float newWidth = solidWidth / 2.0f;
    373. // 四条边路径
    374. solidPath.moveTo(leftTopRadiusTop, newWidth);
    375. solidPath.lineTo(width - rightTopRadiusTop, newWidth);
    376. solidPath.moveTo(width - newWidth, rightTopRadiusRight);
    377. solidPath.lineTo(width - newWidth, height - rightBottomRadiusRight);
    378. solidPath.moveTo(width - rightBottomRadiusBottom, height - newWidth);
    379. solidPath.lineTo(leftBottomRadiusBottom, height - newWidth);
    380. solidPath.moveTo(newWidth, height - leftBottomRadiusLeft);
    381. solidPath.lineTo(newWidth, leftTopRadiusLeft);
    382. // 四个角路径
    383. radiusPath.moveTo(newWidth, leftTopRadiusLeft);
    384. radiusPath.quadTo(newWidth, newWidth, leftTopRadiusTop, newWidth);
    385. radiusPath.moveTo(width - rightTopRadiusTop, newWidth);
    386. radiusPath.quadTo(width, newWidth, width - newWidth, rightTopRadiusRight);
    387. radiusPath.moveTo(width - newWidth, height - rightBottomRadiusRight);
    388. radiusPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadiusBottom, height - newWidth);
    389. radiusPath.moveTo(leftBottomRadiusBottom, height - newWidth);
    390. radiusPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadiusLeft);
    391. result[0] = solidPath;
    392. result[1] = radiusPath;
    393. return result;
    394. }
    395. /**
    396. * 计算直角矩形边框路径
    397. *
    398. * @param width 宽
    399. * @param height 高
    400. * @param solidWidth 线宽
    401. * @return
    402. */
    403. public static Path calculateRectSocketPath(int width, int height, int solidWidth) {
    404. float newWidth = solidWidth / 2.0f;
    405. Path result = new Path();
    406. result.moveTo(newWidth, newWidth);
    407. result.lineTo(width - newWidth, newWidth);
    408. result.lineTo(width - newWidth, height - newWidth);
    409. result.lineTo(newWidth, height - newWidth);
    410. result.close();
    411. return result;
    412. }
    413. /**
    414. * 根据圆角大小和边框长度将圆角矩形作为什么图形处理
    415. *
    416. * @return TYPE_CIRCLE:圆形 TYPE_RADIUS:圆角矩形 TYPE_OVAL:两端作为椭圆
    417. */
    418. private static int getType(int leftTopRadius, int rightTopRadius,
    419. int leftBottomRadius, int rightBottomRadius,
    420. int width, int height) {
    421. // 没有圆角
    422. if (leftTopRadius <= 0 && rightTopRadius <= 0 && leftBottomRadius <= 0 && rightBottomRadius <= 0)
    423. return TYPE_NONE;
    424. boolean topOval = leftTopRadius + rightTopRadius >= width;
    425. boolean bottomOval = leftBottomRadius + rightBottomRadius >= width;
    426. boolean leftOval = leftTopRadius + leftBottomRadius >= height;
    427. boolean rightOval = rightTopRadius + rightBottomRadius >= height;
    428. // 变为原型
    429. if (topOval && bottomOval && leftOval && rightOval)
    430. return TYPE_CIRCLE;
    431. // 变为圆角矩形
    432. if (!topOval && !bottomOval && !leftOval && !rightOval)
    433. return TYPE_RADIUS;
    434. if ((topOval || bottomOval) && (leftOval || rightOval)) return TYPE_RADIUS;
    435. // 处理半圆
    436. int result = TYPE_NONE;
    437. if (topOval) {
    438. result |= TYPE_OVAL_TOP;
    439. }
    440. if (bottomOval) {
    441. result |= TYPE_OVAL_BOTTOM;
    442. }
    443. if (leftOval) {
    444. result |= TYPE_OVAL_LEFT;
    445. }
    446. if (rightOval) {
    447. result |= TYPE_OVAL_RIGHT;
    448. }
    449. return result;
    450. }
    451. /**
    452. * 根据同边的两个圆角的分别长度和边的长度,重新计算两个圆角该有的长度(防止两个圆角的同边长度之后大于总的长度)
    453. * 如:给出左上角上边的长度和右上角上边的长度,以及矩形的上边边长(矩形的长),重新计算出左上角上边的长度和右上角上边的长度,
    454. * 防止左上角上边的长度和右上角上边的长度之和大于边长导致出错,如果大于边长时,根据比例计算。
    455. *
    456. * @param sameSide1 同边第一个值的原大小
    457. * @param sameSide2 同边第二个值的原大小
    458. * @param sameSideWidth 同边长度
    459. * @return int[],长度为2,int[0]:同边第一个值的最终大小 int[1]:同边第二个值的最终大小
    460. */
    461. private static int[] calculateRadiusLength(int sameSide1, int sameSide2, int sameSideWidth) {
    462. int[] result = new int[2];
    463. if (sameSide1 > 0 && sameSide2 > 0) {
    464. int topRadiusWidth = sameSide1 + sameSide2;
    465. if (topRadiusWidth > sameSideWidth) {
    466. result[0] = (int) ((sameSide1 * 1.0 / topRadiusWidth) * sameSideWidth);
    467. result[1] = (int) ((sameSide2 * 1.0 / topRadiusWidth) * sameSideWidth);
    468. } else {
    469. result[0] = sameSide1;
    470. result[1] = sameSide2;
    471. }
    472. } else if (sameSide1 > 0) {
    473. result[0] = Math.min(sameSide1, sameSideWidth);
    474. } else if (sameSide2 > 0) {
    475. result[1] = Math.min(sameSide2, sameSideWidth);
    476. }
    477. return result;
    478. }
    479. }

    4. 解决锯齿问题  自定义Drawable

    RadiusDrawable

    1. public class RadiusDrawable extends Drawable {
    2. // 背景颜色相关
    3. private ColorStateList mBgColorsList;
    4. private Shader mBgShader; // 渐变优先级更高
    5. private Path mBgPath;
    6. private Paint mBgPaint;
    7. private PorterDuffColorFilter mBgTintFilter;
    8. private ColorStateList mBgTint;
    9. private PorterDuff.Mode mBgTintMode;
    10. // 边框线相关
    11. private boolean mDrawSolid;
    12. private ColorStateList mSolidColorsList;
    13. private Shader mSolidShader; // 渐变优先级更高
    14. private List mSolidPath;
    15. private Paint mSolidPaint;
    16. private PorterDuffColorFilter mSolidTintFilter;
    17. private ColorStateList mSolidTint;
    18. private PorterDuff.Mode mSolidTintMode;
    19. public RadiusDrawable(ColorStateList bgColorsList, Shader bgShader, Path path) {
    20. this(bgColorsList, bgShader, path, 0, null, null, null, null);
    21. }
    22. public RadiusDrawable(ColorStateList bgColorsList, Shader bgShader, Path path, int solidWidth, ColorStateList solidColorsList,
    23. Shader solidShader, List solidPath, DashPathEffect dashPathEffect) {
    24. this.mBgPath = path;
    25. this.mBgTintMode = PorterDuff.Mode.SRC_IN;
    26. this.mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    27. this.mBgPaint.setDither(true);
    28. this.mBgShader = bgShader;
    29. this.mDrawSolid = solidWidth > 0;
    30. if (mDrawSolid) {
    31. this.mSolidPath = solidPath == null ? new ArrayList() : solidPath;
    32. this.mSolidTintMode = PorterDuff.Mode.SRC_IN;
    33. this.mSolidPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    34. this.mSolidPaint.setAntiAlias(true);
    35. this.mSolidPaint.setDither(true);
    36. this.mSolidPaint.setStyle(Paint.Style.STROKE);
    37. this.mSolidPaint.setStrokeWidth(solidWidth);
    38. this.mSolidPaint.setPathEffect(dashPathEffect);
    39. this.mSolidShader = solidShader;
    40. }
    41. this.setBackground(bgColorsList, solidColorsList);
    42. }
    43. void setBgShader(Shader shader) {
    44. if (shader != null) {
    45. this.mBgShader = shader;
    46. this.invalidateSelf();
    47. }
    48. }
    49. void setBackground(ColorStateList bgColorsList, ColorStateList solidColorsList) {
    50. if (mBgShader == null) {
    51. this.mBgColorsList = bgColorsList == null ? ColorStateList.valueOf(0) : bgColorsList;
    52. this.mBgPaint.setColor(this.mBgColorsList.getColorForState(this.getState(), this.mBgColorsList.getDefaultColor()));
    53. }
    54. if (mDrawSolid && mSolidShader == null) {
    55. this.mSolidColorsList = solidColorsList == null ? ColorStateList.valueOf(0) : solidColorsList;
    56. this.mSolidPaint.setColor(this.mSolidColorsList.getColorForState(this.getState(), this.mSolidColorsList.getDefaultColor()));
    57. }
    58. }
    59. @Override
    60. public void draw(Canvas canvas) {
    61. Paint bgPaint = this.mBgPaint;
    62. boolean bgClearColorFilter = false;
    63. boolean hasBgShader = mBgShader != null;
    64. if (hasBgShader) {
    65. bgPaint.setShader(mBgShader);
    66. } else {
    67. if (this.mBgTintFilter != null) {
    68. bgPaint.setColorFilter(this.mBgTintFilter);
    69. bgClearColorFilter = true;
    70. } else {
    71. bgClearColorFilter = false;
    72. }
    73. }
    74. canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
    75. canvas.drawPath(mBgPath, bgPaint);
    76. if (bgClearColorFilter) {
    77. bgPaint.setColorFilter(null);
    78. }
    79. if (mDrawSolid) {
    80. Paint solidPaint = this.mSolidPaint;
    81. boolean solidClearColorFilter = false;
    82. boolean hasSolidShader = mSolidShader != null;
    83. if (hasSolidShader) {
    84. solidPaint.setShader(mSolidShader);
    85. } else {
    86. if (this.mSolidTintFilter != null) {
    87. solidPaint.setColorFilter(this.mSolidTintFilter);
    88. solidClearColorFilter = true;
    89. } else {
    90. solidClearColorFilter = false;
    91. }
    92. }
    93. for (Path solidPath : mSolidPath) {
    94. canvas.drawPath(solidPath, solidPaint);
    95. }
    96. if (solidClearColorFilter) {
    97. solidPaint.setColorFilter(null);
    98. }
    99. }
    100. }
    101. @Override
    102. public void setAlpha(int alpha) {
    103. if (mBgShader == null) {
    104. this.mBgPaint.setAlpha(alpha);
    105. }
    106. if (mDrawSolid) {
    107. this.mSolidPaint.setAlpha(alpha);
    108. }
    109. }
    110. @Override
    111. public void setColorFilter(ColorFilter cf) {
    112. if (mBgShader == null) {
    113. this.mBgPaint.setColorFilter(cf);
    114. }
    115. if (mDrawSolid) {
    116. this.mSolidPaint.setColorFilter(cf);
    117. }
    118. }
    119. @Override
    120. public int getOpacity() {
    121. return PixelFormat.TRANSLUCENT;
    122. }
    123. public void setColor(@Nullable ColorStateList backgroundColor, @Nullable ColorStateList solidColorsList) {
    124. this.setBackground(backgroundColor, solidColorsList);
    125. this.invalidateSelf();
    126. }
    127. public ColorStateList getBackGroundColor() {
    128. return this.mBgColorsList;
    129. }
    130. public ColorStateList getSolidColor() {
    131. return this.mSolidColorsList;
    132. }
    133. @Override
    134. public void setTintList(ColorStateList tint) {
    135. if (mBgShader == null) {
    136. this.mBgTint = tint;
    137. this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mBgTintMode);
    138. }
    139. if (mDrawSolid) {
    140. this.mSolidTint = tint;
    141. this.mSolidTintFilter = this.createTintFilter(this.mSolidTint, this.mSolidTintMode);
    142. }
    143. this.invalidateSelf();
    144. }
    145. @Override
    146. public void setTintMode(PorterDuff.Mode tintMode) {
    147. if (mBgShader == null) {
    148. this.mBgTintMode = tintMode;
    149. this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mSolidTintMode);
    150. }
    151. if (mDrawSolid && mSolidShader == null) {
    152. this.mSolidTintMode = tintMode;
    153. this.mSolidTintFilter = this.createTintFilter(this.mBgTint, this.mSolidTintMode);
    154. }
    155. this.invalidateSelf();
    156. }
    157. @Override
    158. protected boolean onStateChange(int[] stateSet) {
    159. boolean bgColorChanged = false;
    160. if (mBgShader == null) {
    161. int newBgColor = this.mBgColorsList.getColorForState(stateSet, this.mBgColorsList.getDefaultColor());
    162. bgColorChanged = newBgColor != this.mBgPaint.getColor();
    163. if (bgColorChanged) {
    164. this.mBgPaint.setColor(newBgColor);
    165. }
    166. }
    167. boolean solidColorChanged = false;
    168. if (mDrawSolid && mSolidShader == null) {
    169. int newSolidColor = this.mSolidColorsList.getColorForState(stateSet, this.mSolidColorsList.getDefaultColor());
    170. solidColorChanged = newSolidColor != this.mSolidPaint.getColor();
    171. if (solidColorChanged) {
    172. this.mSolidPaint.setColor(newSolidColor);
    173. }
    174. }
    175. if (this.mBgShader == null && this.mBgTint != null && this.mBgTintMode != null) {
    176. this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mBgTintMode);
    177. this.invalidateSelf();
    178. return true;
    179. } else if (this.mSolidShader == null && this.mSolidTint != null && this.mSolidTintMode != null) {
    180. this.mSolidTintFilter = this.createTintFilter(this.mSolidTint, this.mSolidTintMode);
    181. return true;
    182. } else {
    183. return bgColorChanged || solidColorChanged;
    184. }
    185. }
    186. @Override
    187. public boolean isStateful() {
    188. if (mDrawSolid) {
    189. return this.mBgTint != null && this.mBgTint.isStateful()
    190. || this.mBgColorsList != null && this.mBgColorsList.isStateful()
    191. || this.mSolidTint != null && this.mSolidTint.isStateful()
    192. || this.mSolidColorsList != null && this.mSolidColorsList.isStateful()
    193. || super.isStateful();
    194. } else {
    195. return this.mBgTint != null && this.mBgTint.isStateful()
    196. || this.mBgColorsList != null && this.mBgColorsList.isStateful()
    197. || super.isStateful();
    198. }
    199. }
    200. private PorterDuffColorFilter createTintFilter(ColorStateList tint, PorterDuff.Mode tintMode) {
    201. if (tint != null && tintMode != null) {
    202. int color = tint.getColorForState(this.getState(), 0);
    203. return new PorterDuffColorFilter(color, tintMode);
    204. } else {
    205. return null;
    206. }
    207. }
    208. }

    4.1创建attrs自定义属性

    1. <declare-styleable name="RadiusView">
    2. <attr name="rv_radius_all" format="dimension" />
    3. <attr name="rv_radius_leftTop" format="dimension" />
    4. <attr name="rv_radius_rightTop" format="dimension" />
    5. <attr name="rv_radius_rightBottom" format="dimension" />
    6. <attr name="rv_radius_leftBottom" format="dimension" />
    7. <attr name="rv_background_color" format="color" />
    8. <attr name="rv_shader_start_color" format="color" />
    9. <attr name="rv_shader_middle_color" format="color" />
    10. <attr name="rv_shader_middle_color2" format="color" />
    11. <attr name="rv_shader_middle_color3" format="color" />
    12. <attr name="rv_shader_end_color" format="color" />
    13. <attr name="rv_shader_type" format="enum">
    14. <enum name="linear" value="10" />
    15. <enum name="radial" value="11" />
    16. <enum name="sweep" value="12" />
    17. attr>
    18. <attr name="rv_shader_linear_orientation" format="enum">
    19. <enum name="top_to_bottom" value="110" />
    20. <enum name="bottom_to_top" value="111" />
    21. <enum name="left_to_right" value="220" />
    22. <enum name="right_to_left" value="221" />
    23. <enum name="leftTop_to_rightBottom" value="330" />
    24. <enum name="rightBottom_to_leftTop" value="331" />
    25. <enum name="rightTop_to_leftBottom" value="440" />
    26. <enum name="leftBottom_to_rightTop" value="441" />
    27. attr>
    28. <attr name="rv_solid_width" format="dimension" />
    29. <attr name="rv_solid_color" format="color" />
    30. <attr name="rv_solid_dashWidth" format="dimension" />
    31. <attr name="rv_solid_dashGap" format="dimension" />
    32. <attr name="rv_solid_type" format="enum">
    33. <enum name="solid" value="0" />
    34. <enum name="dash" value="1" />
    35. attr>
    36. <attr name="rv_solid_shader_start_color" format="color" />
    37. <attr name="rv_solid_shader_middle_color" format="color" />
    38. <attr name="rv_solid_shader_middle_color2" format="color" />
    39. <attr name="rv_solid_shader_middle_color3" format="color" />
    40. <attr name="rv_solid_shader_end_color" format="color" />
    41. <attr name="rv_solid_shader_type" format="enum">
    42. <enum name="linear" value="10" />
    43. <enum name="radial" value="11" />
    44. <enum name="sweep" value="12" />
    45. attr>
    46. <attr name="rv_solid_shader_linear_orientation" format="enum">
    47. <enum name="top_to_bottom" value="110" />
    48. <enum name="bottom_to_top" value="111" />
    49. <enum name="left_to_right" value="220" />
    50. <enum name="right_to_left" value="221" />
    51. <enum name="leftTop_to_rightBottom" value="330" />
    52. <enum name="rightBottom_to_leftTop" value="331" />
    53. <enum name="rightTop_to_leftBottom" value="440" />
    54. <enum name="leftBottom_to_rightTop" value="441" />
    55. attr>
    56. declare-styleable>

    4.2创建自动适配布局  公共接口IAutoLayout

    1. public interface IAutoLayout {
    2. int AUTO_TYPE_WIDTH = 0;
    3. int AUTO_TYPE_HEIGHT = 1;
    4. @IntDef(value = {AUTO_TYPE_WIDTH, AUTO_TYPE_HEIGHT})
    5. @interface AutoType {
    6. }
    7. /**
    8. * 设置控件宽高信息,并重新布局
    9. *
    10. * @param autoWidth
    11. * @param autoHeight
    12. */
    13. void setAutoViewInfo(int autoWidth, int autoHeight);
    14. /**
    15. * 设置控件宽高信息,并重新布局
    16. *
    17. * @param autoWidth
    18. * @param autoHeight
    19. */
    20. void setAutoViewInfo(@AutoType int autoType, int autoWidth, int autoHeight);
    21. }

    4.3创建账单适配布局AutoRelativeLayout

    1. public class AutoRelativeLayout extends RelativeLayout implements IAutoLayout {
    2. // 自动适配的类型,0:宽适配 1:高适配
    3. private int auto_type = 1;
    4. private int auto_width;
    5. private int auto_height;
    6. public AutoRelativeLayout(Context context) {
    7. this(context, null);
    8. }
    9. public AutoRelativeLayout(Context context, AttributeSet attrs) {
    10. this(context, attrs, 0);
    11. }
    12. public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    13. super(context, attrs, defStyleAttr);
    14. init(context, attrs);
    15. }
    16. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    17. public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    18. super(context, attrs, defStyleAttr, defStyleRes);
    19. init(context, attrs);
    20. }
    21. protected void init(Context context, AttributeSet attrs) {
    22. TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoWidthHeightView);
    23. auto_height = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_height, 0);
    24. auto_width = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_width, 0);
    25. auto_type = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_type, -1);
    26. typedArray.recycle();
    27. }
    28. /**
    29. * @docRoot
    30. */
    31. @Override
    32. public void setAutoViewInfo(int autoWidth, int autoHeight) {
    33. this.auto_width = autoWidth;
    34. this.auto_height = autoHeight;
    35. requestLayout();
    36. }
    37. /**
    38. * @docRoot
    39. */
    40. @Override
    41. public void setAutoViewInfo(@AutoType int autoType, int autoWidth, int autoHeight) {
    42. this.auto_type = autoType;
    43. this.auto_width = autoWidth;
    44. this.auto_height = autoHeight;
    45. requestLayout();
    46. }
    47. @Override
    48. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    49. if (auto_type == -1) {
    50. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    51. } else {
    52. setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
    53. measureChildren(widthMeasureSpec, heightMeasureSpec);
    54. int viewWidth = getMeasuredWidth();
    55. int viewHeight = getMeasuredHeight();
    56. switch (auto_type) {
    57. case 0: // 动态计算出控件宽
    58. viewWidth = (int) (viewHeight * ((auto_width * 1.0f) / auto_height));
    59. break;
    60. case 1: // 动态计算出控件高
    61. viewHeight = (int) (viewWidth * ((auto_height * 1.0f) / auto_width));
    62. break;
    63. }
    64. widthMeasureSpec = MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY);
    65. heightMeasureSpec = MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY);
    66. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    67. }
    68. }
    69. }

    其他AutoLinearLayout,AutoImageView,AutoFrameLayout布局

    都是一样的

    4.4创建自定义布局适配attrs

    1. <declare-styleable name="AutoWidthHeightView">
    2. <attr name="auto_view_width" format="integer" />
    3. <attr name="auto_view_height" format="integer" />
    4. <attr name="auto_view_type" format="enum">
    5. <enum name="auto_view_width" value="0" />
    6. <enum name="auto_view_height" value="1" />
    7. attr>
    8. declare-styleable>

    5.圆角相对布局 RadiusRelativeLayout

    1. public class RadiusRelativeLayout extends AutoRelativeLayout implements IRadiusLayout {
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusRelativeLayout(Context context) {
    26. this(context, null);
    27. }
    28. public RadiusRelativeLayout(Context context, AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    36. public RadiusRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    37. super(context, attrs, defStyleAttr, defStyleRes);
    38. init(context, attrs);
    39. }
    40. @Override
    41. protected void init(Context context, AttributeSet attrs) {
    42. super.init(context, attrs);
    43. setWillNotDraw(false);
    44. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    45. // 读取圆角配置
    46. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    47. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    48. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    49. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    50. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    51. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    52. // 获取背景信息
    53. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    54. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    55. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    56. int middleColor2 = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color2, ShaderUtils.COLOR_VALUE_NONE);
    57. int middleColor3 = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color3, ShaderUtils.COLOR_VALUE_NONE);
    58. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    59. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    60. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    61. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, middleColor2, middleColor3, endColor);
    62. if (bgShaderColors == null)
    63. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    64. // 获取边框信息
    65. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    66. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    67. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    68. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    69. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    70. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    71. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    72. int solidMiddleColor2 = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color2, ShaderUtils.COLOR_VALUE_NONE);
    73. int solidMiddleColor3 = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color3, ShaderUtils.COLOR_VALUE_NONE);
    74. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    75. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    76. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    77. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidMiddleColor2, solidMiddleColor3, solidEndColor);
    78. if (solidShaderColors == null)
    79. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    80. radiusType.recycle();
    81. // 角度边长不能小于0
    82. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    83. //如果四个角的值没有设置,那么就使用通用的radius的值。
    84. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    85. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    86. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    87. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    88. if (bgColorStateList == null) {
    89. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    90. }
    91. if (solidColorStateList == null) {
    92. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    93. }
    94. if (lineType == SOLID_TYPE_DASH) {
    95. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    96. } else {
    97. dashPathEffect = null;
    98. }
    99. }
    100. @Override
    101. public void setBackgroundColor(int color) {
    102. this.bgColorStateList = ColorStateList.valueOf(color);
    103. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    104. if (radiusDrawable != null) {
    105. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    106. }
    107. forceRefreshLayout();
    108. }
    109. @Override
    110. public void setBackgroundColor(ColorStateList bgColorStateList) {
    111. this.bgColorStateList = bgColorStateList;
    112. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    113. if (radiusDrawable != null) {
    114. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    115. }
    116. forceRefreshLayout();
    117. }
    118. @Override
    119. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    120. if (dashPathEffect != null) {
    121. this.dashPathEffect = dashPathEffect;
    122. forceRefreshLayout();
    123. }
    124. }
    125. @Override
    126. public void setSolidColor(int color) {
    127. this.solidColorStateList = ColorStateList.valueOf(color);
    128. if (radiusDrawable != null) {
    129. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    130. }
    131. forceRefreshLayout();
    132. }
    133. @Override
    134. public void setSolidColor(ColorStateList solidColorStateList) {
    135. this.solidColorStateList = solidColorStateList;
    136. if (radiusDrawable != null) {
    137. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    138. }
    139. forceRefreshLayout();
    140. }
    141. @Override
    142. public void setRadius(int radius) {
    143. if (radius >= 0) {
    144. leftTopRadius = radius;
    145. rightTopRadius = radius;
    146. rightBottomRadius = radius;
    147. leftBottomRadius = radius;
    148. forceRefreshLayout();
    149. }
    150. }
    151. @Override
    152. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    153. this.leftTopRadius = leftTopRadius;
    154. this.rightTopRadius = rightTopRadius;
    155. this.rightBottomRadius = rightBottomRadius;
    156. this.leftBottomRadius = leftBottomRadius;
    157. forceRefreshLayout();
    158. }
    159. @Override
    160. public void setLeftTopRadius(int leftTopRadius) {
    161. this.leftTopRadius = leftTopRadius;
    162. forceRefreshLayout();
    163. }
    164. @Override
    165. public void setRightTopRadius(int rightTopRadius) {
    166. this.rightTopRadius = rightTopRadius;
    167. forceRefreshLayout();
    168. }
    169. @Override
    170. public void setRightBottomRadius(int rightBottomRadius) {
    171. this.rightBottomRadius = rightBottomRadius;
    172. forceRefreshLayout();
    173. }
    174. @Override
    175. public void setLeftBottomRadius(int leftBottomRadius) {
    176. this.leftBottomRadius = leftBottomRadius;
    177. forceRefreshLayout();
    178. }
    179. @Override
    180. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    181. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    182. }
    183. @Override
    184. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    185. if (shapeColors == null || shapeColors.length <= 0)
    186. return;
    187. this.bgShaderType = shapeType;
    188. this.bgShaderColors = shapeColors;
    189. this.bgShaderLinearOrientation = shaderLinearOrientation;
    190. forceRefreshLayout();
    191. }
    192. @Override
    193. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    194. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    195. }
    196. @Override
    197. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    198. if (shapeColors == null || shapeColors.length <= 0)
    199. return;
    200. this.solidShaderType = shapeType;
    201. this.solidShaderColors = shapeColors;
    202. this.solidShaderLinearOrientation = shaderLinearOrientation;
    203. forceRefreshLayout();
    204. }
    205. @Override
    206. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    207. super.onLayout(changed, left, top, right, bottom);
    208. // 没有发生改变,并且不需要强制刷新就不在重新layout
    209. if (!changed && !this.forceRefreshLayout) {
    210. return;
    211. }
    212. this.forceRefreshLayout = false;
    213. width = getWidth();
    214. height = getHeight();
    215. final Path bgPath = setBackground();
    216. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    217. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    218. float elevation = Math.max(getElevation(), getTranslationZ());
    219. if (elevation > 0) {
    220. setElevation(elevation);
    221. setOutlineProvider(new ViewOutlineProvider() {
    222. @Override
    223. public void getOutline(View view, Outline outline) {
    224. if (bgPath.isConvex()) {
    225. outline.setConvexPath(bgPath);
    226. } else {
    227. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    228. leftBottomRadius, rightBottomRadius, width, height, false));
    229. }
    230. }
    231. });
    232. setClipToOutline(true);
    233. }
    234. }
    235. }
    236. private void forceRefreshLayout() {
    237. this.forceRefreshLayout = true;
    238. requestLayout();
    239. }
    240. private Path setBackground() {
    241. Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    242. leftBottomRadius, rightBottomRadius, width, height);
    243. Shader bgShader = null;
    244. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    245. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    246. }
    247. // 边框
    248. if (solidWidth > 0) {
    249. Shader solidShader = null;
    250. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    251. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    252. }
    253. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    254. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    255. List solidPath = Arrays.asList(solidPathArray);
    256. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    257. } else {
    258. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    259. }
    260. setBackground(radiusDrawable);
    261. return bgPath;
    262. }
    263. }

    6.圆角线性布局  RadiusLinearLayout

    1. public class RadiusLinearLayout extends AutoLinearLayout implements IRadiusLayout {
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusLinearLayout(Context context) {
    26. this(context, null, 0);
    27. }
    28. public RadiusLinearLayout(Context context, @Nullable AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    36. public RadiusLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    37. super(context, attrs, defStyleAttr, defStyleRes);
    38. init(context, attrs);
    39. }
    40. @Override
    41. protected void init(Context context, AttributeSet attrs) {
    42. super.init(context, attrs);
    43. setWillNotDraw(false);
    44. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    45. // 读取圆角配置
    46. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    47. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    48. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    49. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    50. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    51. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    52. // 获取背景信息
    53. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    54. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    55. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    56. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    57. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    58. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    59. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
    60. if (bgShaderColors == null)
    61. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    62. // 获取边框信息
    63. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    64. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    65. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    66. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    67. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    68. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    69. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    70. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    71. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    72. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    73. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
    74. if (solidShaderColors == null)
    75. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    76. radiusType.recycle();
    77. // 角度边长不能小于0
    78. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    79. //如果四个角的值没有设置,那么就使用通用的radius的值。
    80. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    81. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    82. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    83. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    84. if (bgColorStateList == null) {
    85. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    86. }
    87. if (solidColorStateList == null) {
    88. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    89. }
    90. if (lineType == SOLID_TYPE_DASH) {
    91. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    92. } else {
    93. dashPathEffect = null;
    94. }
    95. }
    96. @Override
    97. public void setBackgroundColor(int color) {
    98. this.bgColorStateList = ColorStateList.valueOf(color);
    99. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    100. if (radiusDrawable != null) {
    101. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    102. }
    103. forceRefreshLayout();
    104. }
    105. @Override
    106. public void setBackgroundColor(ColorStateList bgColorStateList) {
    107. this.bgColorStateList = bgColorStateList;
    108. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    109. if (radiusDrawable != null) {
    110. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    111. }
    112. forceRefreshLayout();
    113. }
    114. @Override
    115. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    116. if (dashPathEffect != null) {
    117. this.dashPathEffect = dashPathEffect;
    118. forceRefreshLayout();
    119. }
    120. }
    121. @Override
    122. public void setSolidColor(int color) {
    123. this.solidColorStateList = ColorStateList.valueOf(color);
    124. if (radiusDrawable != null) {
    125. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    126. }
    127. forceRefreshLayout();
    128. }
    129. @Override
    130. public void setSolidColor(ColorStateList solidColorStateList) {
    131. this.solidColorStateList = solidColorStateList;
    132. if (radiusDrawable != null) {
    133. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    134. }
    135. forceRefreshLayout();
    136. }
    137. @Override
    138. public void setRadius(int radius) {
    139. if (radius >= 0) {
    140. leftTopRadius = radius;
    141. rightTopRadius = radius;
    142. rightBottomRadius = radius;
    143. leftBottomRadius = radius;
    144. forceRefreshLayout();
    145. }
    146. }
    147. @Override
    148. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    149. this.leftTopRadius = leftTopRadius;
    150. this.rightTopRadius = rightTopRadius;
    151. this.rightBottomRadius = rightBottomRadius;
    152. this.leftBottomRadius = leftBottomRadius;
    153. forceRefreshLayout();
    154. }
    155. @Override
    156. public void setLeftTopRadius(int leftTopRadius) {
    157. this.leftTopRadius = leftTopRadius;
    158. forceRefreshLayout();
    159. }
    160. @Override
    161. public void setRightTopRadius(int rightTopRadius) {
    162. this.rightTopRadius = rightTopRadius;
    163. forceRefreshLayout();
    164. }
    165. @Override
    166. public void setRightBottomRadius(int rightBottomRadius) {
    167. this.rightBottomRadius = rightBottomRadius;
    168. forceRefreshLayout();
    169. }
    170. @Override
    171. public void setLeftBottomRadius(int leftBottomRadius) {
    172. this.leftBottomRadius = leftBottomRadius;
    173. forceRefreshLayout();
    174. }
    175. @Override
    176. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    177. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    178. }
    179. @Override
    180. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    181. if (shapeColors == null || shapeColors.length <= 0)
    182. return;
    183. this.bgShaderType = shapeType;
    184. this.bgShaderColors = shapeColors;
    185. this.bgShaderLinearOrientation = shaderLinearOrientation;
    186. forceRefreshLayout();
    187. }
    188. @Override
    189. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    190. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    191. }
    192. @Override
    193. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    194. if (shapeColors == null || shapeColors.length <= 0)
    195. return;
    196. this.solidShaderType = shapeType;
    197. this.solidShaderColors = shapeColors;
    198. this.solidShaderLinearOrientation = shaderLinearOrientation;
    199. forceRefreshLayout();
    200. }
    201. @Override
    202. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    203. super.onLayout(changed, left, top, right, bottom);
    204. // 没有发生改变,并且不需要强制刷新就不在重新layout
    205. if (!changed && !this.forceRefreshLayout) {
    206. return;
    207. }
    208. this.forceRefreshLayout = false;
    209. width = getWidth();
    210. height = getHeight();
    211. final Path bgPath = setBackground();
    212. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    213. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    214. float elevation = Math.max(getElevation(), getTranslationZ());
    215. if (elevation > 0) {
    216. setElevation(elevation);
    217. setOutlineProvider(new ViewOutlineProvider() {
    218. @Override
    219. public void getOutline(View view, Outline outline) {
    220. if (bgPath.isConvex()) {
    221. outline.setConvexPath(bgPath);
    222. } else {
    223. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    224. leftBottomRadius, rightBottomRadius, width, height, false));
    225. }
    226. }
    227. });
    228. setClipToOutline(true);
    229. }
    230. }
    231. }
    232. private void forceRefreshLayout() {
    233. this.forceRefreshLayout = true;
    234. requestLayout();
    235. }
    236. private Path setBackground() {
    237. Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    238. leftBottomRadius, rightBottomRadius, width, height);
    239. Shader bgShader = null;
    240. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    241. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    242. }
    243. // 边框
    244. if (solidWidth > 0) {
    245. Shader solidShader = null;
    246. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    247. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    248. }
    249. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    250. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    251. List solidPath = Arrays.asList(solidPathArray);
    252. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    253. } else {
    254. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    255. }
    256. setBackground(radiusDrawable);
    257. return bgPath;
    258. }
    259. }

    7.圆角TextView   RadiusTextView

    1. public class RadiusTextView extends AppCompatTextView implements IRadiusLayout {
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusTextView(Context context) {
    26. this(context, null);
    27. }
    28. public RadiusTextView(Context context, AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. protected void init(Context context, AttributeSet attrs) {
    36. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    37. // 读取圆角配置
    38. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    39. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    40. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    41. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    42. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    43. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    44. // 获取背景信息
    45. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    46. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    47. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    48. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    49. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    50. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    51. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
    52. if (bgShaderColors == null)
    53. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    54. // 获取边框信息
    55. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    56. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    57. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    58. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    59. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    60. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    61. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    62. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    63. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    64. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    65. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
    66. if (solidShaderColors == null)
    67. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    68. radiusType.recycle();
    69. // 角度边长不能小于0
    70. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    71. //如果四个角的值没有设置,那么就使用通用的radius的值。
    72. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    73. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    74. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    75. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    76. if (bgColorStateList == null) {
    77. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    78. }
    79. if (solidColorStateList == null) {
    80. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    81. }
    82. if (lineType == SOLID_TYPE_DASH) {
    83. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    84. } else {
    85. dashPathEffect = null;
    86. }
    87. }
    88. @Override
    89. public void setBackgroundColor(int color) {
    90. this.bgColorStateList = ColorStateList.valueOf(color);
    91. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    92. if (radiusDrawable != null) {
    93. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    94. }
    95. forceRefreshLayout();
    96. }
    97. @Override
    98. public void setBackgroundColor(ColorStateList bgColorStateList) {
    99. this.bgColorStateList = bgColorStateList;
    100. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    101. if (radiusDrawable != null) {
    102. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    103. }
    104. forceRefreshLayout();
    105. }
    106. @Override
    107. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    108. if (dashPathEffect != null) {
    109. this.dashPathEffect = dashPathEffect;
    110. forceRefreshLayout();
    111. }
    112. }
    113. @Override
    114. public void setSolidColor(int color) {
    115. this.solidColorStateList = ColorStateList.valueOf(color);
    116. if (radiusDrawable != null) {
    117. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    118. }
    119. forceRefreshLayout();
    120. }
    121. @Override
    122. public void setSolidColor(ColorStateList solidColorStateList) {
    123. this.solidColorStateList = solidColorStateList;
    124. if (radiusDrawable != null) {
    125. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    126. }
    127. forceRefreshLayout();
    128. }
    129. @Override
    130. public void setRadius(int radius) {
    131. if (radius >= 0) {
    132. leftTopRadius = radius;
    133. rightTopRadius = radius;
    134. rightBottomRadius = radius;
    135. leftBottomRadius = radius;
    136. forceRefreshLayout();
    137. }
    138. }
    139. @Override
    140. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    141. this.leftTopRadius = leftTopRadius;
    142. this.rightTopRadius = rightTopRadius;
    143. this.rightBottomRadius = rightBottomRadius;
    144. this.leftBottomRadius = leftBottomRadius;
    145. forceRefreshLayout();
    146. }
    147. @Override
    148. public void setLeftTopRadius(int leftTopRadius) {
    149. this.leftTopRadius = leftTopRadius;
    150. forceRefreshLayout();
    151. }
    152. @Override
    153. public void setRightTopRadius(int rightTopRadius) {
    154. this.rightTopRadius = rightTopRadius;
    155. forceRefreshLayout();
    156. }
    157. @Override
    158. public void setRightBottomRadius(int rightBottomRadius) {
    159. this.rightBottomRadius = rightBottomRadius;
    160. forceRefreshLayout();
    161. }
    162. @Override
    163. public void setLeftBottomRadius(int leftBottomRadius) {
    164. this.leftBottomRadius = leftBottomRadius;
    165. forceRefreshLayout();
    166. }
    167. @Override
    168. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    169. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    170. }
    171. @Override
    172. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    173. if (shapeColors == null || shapeColors.length <= 0)
    174. return;
    175. this.bgShaderType = shapeType;
    176. this.bgShaderColors = shapeColors;
    177. this.bgShaderLinearOrientation = shaderLinearOrientation;
    178. forceRefreshLayout();
    179. }
    180. @Override
    181. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    182. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    183. }
    184. @Override
    185. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    186. if (shapeColors == null || shapeColors.length <= 0)
    187. return;
    188. this.solidShaderType = shapeType;
    189. this.solidShaderColors = shapeColors;
    190. this.solidShaderLinearOrientation = shaderLinearOrientation;
    191. forceRefreshLayout();
    192. }
    193. @Override
    194. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    195. super.onLayout(changed, left, top, right, bottom);
    196. // 没有发生改变,并且不需要强制刷新就不在重新layout
    197. if (!changed && !this.forceRefreshLayout) {
    198. return;
    199. }
    200. this.forceRefreshLayout = false;
    201. width = getWidth();
    202. height = getHeight();
    203. final Path bgPath = setBackground();
    204. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    205. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    206. float elevation = Math.max(getElevation(), getTranslationZ());
    207. if (elevation > 0) {
    208. setElevation(elevation);
    209. setOutlineProvider(new ViewOutlineProvider() {
    210. @Override
    211. public void getOutline(View view, Outline outline) {
    212. if (bgPath.isConvex()) {
    213. outline.setConvexPath(bgPath);
    214. } else {
    215. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    216. leftBottomRadius, rightBottomRadius, width, height, false));
    217. }
    218. }
    219. });
    220. setClipToOutline(true);
    221. }
    222. }
    223. }
    224. private void forceRefreshLayout() {
    225. this.forceRefreshLayout = true;
    226. requestLayout();
    227. }
    228. private Path setBackground() {
    229. Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    230. leftBottomRadius, rightBottomRadius, width, height);
    231. Shader bgShader = null;
    232. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    233. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    234. }
    235. // 边框
    236. if (solidWidth > 0) {
    237. Shader solidShader = null;
    238. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    239. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    240. }
    241. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    242. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    243. List solidPath = Arrays.asList(solidPathArray);
    244. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    245. } else {
    246. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    247. }
    248. setBackground(radiusDrawable);
    249. return bgPath;
    250. }
    251. }

    8.圆角Image  RadiusImageView

    1. public class RadiusImageView extends AutoImageView {
    2. // 默认没有圆角
    3. private final int DEFAULT_RADIUS = 0;
    4. private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    5. private static final int COLOR_DRAWABLE_DIMENSION = 2;
    6. public static final int TYPE_SOLID = 0; // 实线
    7. public static final int TYPE_DASH = 1; // 虚线
    8. // 控件宽高
    9. private int width, height;
    10. // 圆角参数
    11. private int leftTopRadius;
    12. private int rightTopRadius;
    13. private int rightBottomRadius;
    14. private int leftBottomRadius;
    15. // 画图片的画笔
    16. private Paint bitmapPaint;
    17. // 画边框的画笔
    18. private Paint solidPaint;
    19. // 3x3 矩阵,主要用于缩小放大
    20. private Matrix matrix;
    21. //渲染图像,使用图像为绘制图形着色
    22. private BitmapShader bitmapShader;
    23. // 边框参数
    24. private int solidWidth;
    25. private int solidColor;
    26. // 是否需要强制重新布局
    27. private boolean forceRefreshLayout;
    28. public RadiusImageView(Context context) {
    29. this(context, null);
    30. }
    31. public RadiusImageView(Context context, AttributeSet attrs) {
    32. this(context, attrs, 0);
    33. }
    34. public RadiusImageView(Context context, AttributeSet attrs, int defStyleAttr) {
    35. super(context, attrs, defStyleAttr);
    36. init(context, attrs);
    37. }
    38. @Override
    39. protected void init(Context context, AttributeSet attrs) {
    40. super.init(context, attrs);
    41. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    42. // 读取圆角配置
    43. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    44. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    45. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    46. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    47. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    48. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    49. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    50. solidColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_color, Color.TRANSPARENT);
    51. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    52. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    53. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, TYPE_SOLID);
    54. radiusType.recycle();
    55. // 角度边长不能小于0
    56. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    57. //如果四个角的值没有设置,那么就使用通用的radius的值。
    58. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    59. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    60. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    61. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    62. matrix = new Matrix();
    63. bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    64. bitmapPaint.setDither(true);
    65. solidPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    66. solidPaint.setDither(true);
    67. solidPaint.setStyle(Paint.Style.STROKE);
    68. solidPaint.setColor(solidColor);
    69. solidPaint.setStrokeWidth(solidWidth);
    70. if (lineType == TYPE_SOLID) {
    71. setLineTypeStyle(TYPE_SOLID, 0, 0, false);
    72. } else {
    73. setLineTypeStyle(TYPE_DASH, dashGap, dashWidth, false);
    74. }
    75. }
    76. // 设置线的类型和虚线样式
    77. private void setLineTypeStyle(int lineType, float dashGap, float dashWidth, boolean invalidate) {
    78. if (lineType == TYPE_DASH) {
    79. DashPathEffect dashPathEffect = null;
    80. if (dashWidth > 0) {
    81. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    82. }
    83. solidPaint.setPathEffect(dashPathEffect);
    84. } else {
    85. solidPaint.setPathEffect(null);
    86. }
    87. if (invalidate) invalidate();
    88. }
    89. public void setSolidColor(int color) {
    90. solidPaint.setColor(color);
    91. forceRefreshLayout();
    92. }
    93. public void setRadius(int radius) {
    94. if (radius >= 0) {
    95. leftTopRadius = radius;
    96. rightTopRadius = radius;
    97. rightBottomRadius = radius;
    98. leftBottomRadius = radius;
    99. forceRefreshLayout();
    100. }
    101. }
    102. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    103. this.leftTopRadius = leftTopRadius;
    104. this.rightTopRadius = rightTopRadius;
    105. this.rightBottomRadius = rightBottomRadius;
    106. this.leftBottomRadius = leftBottomRadius;
    107. forceRefreshLayout();
    108. }
    109. public void setLeftTopRadius(int leftTopRadius) {
    110. this.leftTopRadius = leftTopRadius;
    111. forceRefreshLayout();
    112. }
    113. public void setRightTopRadius(int rightTopRadius) {
    114. this.rightTopRadius = rightTopRadius;
    115. forceRefreshLayout();
    116. }
    117. public void setRightBottomRadius(int rightBottomRadius) {
    118. this.rightBottomRadius = rightBottomRadius;
    119. forceRefreshLayout();
    120. }
    121. public void setLeftBottomRadius(int leftBottomRadius) {
    122. this.leftBottomRadius = leftBottomRadius;
    123. forceRefreshLayout();
    124. }
    125. @Override
    126. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    127. super.onLayout(changed, left, top, right, bottom);
    128. // 没有发生改变,并且不需要强制刷新就不在重新layout
    129. if (!changed && !this.forceRefreshLayout) {
    130. return;
    131. }
    132. this.forceRefreshLayout = false;
    133. width = getWidth();
    134. height = getHeight();
    135. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    136. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    137. float elevation = Math.max(getElevation(), getTranslationZ());
    138. if (elevation > 0) {
    139. setElevation(elevation);
    140. setOutlineProvider(new ViewOutlineProvider() {
    141. @Override
    142. public void getOutline(View view, Outline outline) {
    143. Path path = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, false);
    144. outline.setConvexPath(path);
    145. }
    146. });
    147. setClipToOutline(true);
    148. }
    149. }
    150. }
    151. private void forceRefreshLayout() {
    152. this.forceRefreshLayout = true;
    153. requestLayout();
    154. }
    155. @Override
    156. protected void onDraw(Canvas canvas) {
    157. if (leftTopRadius <= DEFAULT_RADIUS && leftBottomRadius <= DEFAULT_RADIUS &&
    158. rightTopRadius <= DEFAULT_RADIUS && rightBottomRadius <= DEFAULT_RADIUS) {
    159. super.onDraw(canvas);
    160. // 边框
    161. if (solidWidth > 0)
    162. canvas.drawPath(RadiusUtils.calculateRectSocketPath(width, height, solidWidth), solidPaint);
    163. } else {
    164. Path path = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    165. Bitmap bitmap = getBitmapFromDrawable(getDrawable());
    166. if (bitmap != null) {
    167. bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    168. configureBounds(getDrawable());
    169. // 设置变换矩阵
    170. bitmapShader.setLocalMatrix(matrix);
    171. // 设置shader
    172. bitmapPaint.setShader(bitmapShader);
    173. canvas.drawPath(path, bitmapPaint);
    174. }
    175. // 边框
    176. if (solidWidth > 0) {
    177. Path[] result = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    178. canvas.drawPath(result[0], solidPaint);
    179. canvas.drawPath(result[1], solidPaint);
    180. }
    181. }
    182. }
    183. // 获取图片资源
    184. private Bitmap getBitmapFromDrawable(Drawable drawable) {
    185. if (drawable == null) {
    186. return null;
    187. }
    188. if (drawable instanceof BitmapDrawable) {
    189. return ((BitmapDrawable) drawable).getBitmap();
    190. }
    191. try {
    192. Bitmap bitmap;
    193. if (drawable instanceof ColorDrawable) {
    194. bitmap = Bitmap.createBitmap(COLOR_DRAWABLE_DIMENSION, COLOR_DRAWABLE_DIMENSION, BITMAP_CONFIG);
    195. } else {
    196. bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
    197. }
    198. Canvas canvas = new Canvas(bitmap);
    199. drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    200. drawable.draw(canvas);
    201. return bitmap;
    202. } catch (Exception e) {
    203. e.printStackTrace();
    204. return null;
    205. }
    206. }
    207. // 根据 ScaleType 进行矩阵变换
    208. private void configureBounds(Drawable drawable) {
    209. if (drawable == null) {
    210. return;
    211. }
    212. final ImageView.ScaleType scaleType = getScaleType();
    213. final int intrinsicWidth = drawable.getIntrinsicWidth();
    214. final int intrinsicHeight = drawable.getIntrinsicHeight();
    215. final int vWidth = width - getPaddingLeft() - getPaddingRight();
    216. final int vHeight = height - getPaddingTop() - getPaddingBottom();
    217. final boolean fits = (intrinsicWidth < 0 || vWidth == intrinsicWidth)
    218. && (intrinsicHeight < 0 || vHeight == intrinsicHeight);
    219. if (intrinsicWidth <= 0 || intrinsicHeight <= 0 || ImageView.ScaleType.FIT_XY == scaleType) {
    220. matrix = null;
    221. } else {
    222. if (ImageView.ScaleType.MATRIX == scaleType) {
    223. matrix = null;
    224. } else if (fits) {
    225. matrix = null;
    226. } else if (ImageView.ScaleType.CENTER == scaleType) {
    227. if (matrix == null)
    228. matrix = new Matrix();
    229. matrix.setTranslate(Math.round((vWidth - intrinsicWidth) * 0.5f),
    230. Math.round((vHeight - intrinsicHeight) * 0.5f));
    231. } else if (ImageView.ScaleType.CENTER_CROP == scaleType) {
    232. float scale;
    233. float dx = 0, dy = 0;
    234. if (intrinsicWidth * vHeight > vWidth * intrinsicHeight) {
    235. scale = (float) vHeight / (float) intrinsicHeight;
    236. dx = (vWidth - intrinsicWidth * scale) * 0.5f;
    237. } else {
    238. scale = (float) vWidth / (float) intrinsicWidth;
    239. dy = (vHeight - intrinsicHeight * scale) * 0.5f;
    240. }
    241. if (matrix == null)
    242. matrix = new Matrix();
    243. matrix.setScale(scale, scale);
    244. matrix.postTranslate(Math.round(dx), Math.round(dy));
    245. } else if (ImageView.ScaleType.CENTER_INSIDE == scaleType) {
    246. float scale;
    247. float dx;
    248. float dy;
    249. if (intrinsicWidth <= vWidth && intrinsicHeight <= vHeight) {
    250. scale = 1.0f;
    251. } else {
    252. scale = Math.min((float) vWidth / (float) intrinsicWidth,
    253. (float) vHeight / (float) intrinsicHeight);
    254. }
    255. dx = Math.round((vWidth - intrinsicWidth * scale) * 0.5f);
    256. dy = Math.round((vHeight - intrinsicHeight * scale) * 0.5f);
    257. if (matrix == null)
    258. matrix = new Matrix();
    259. matrix.setScale(scale, scale);
    260. matrix.postTranslate(dx, dy);
    261. } else {
    262. RectF mTempSrc = new RectF();
    263. RectF mTempDst = new RectF();
    264. mTempSrc.set(0, 0, intrinsicWidth, intrinsicHeight);
    265. mTempDst.set(0, 0, vWidth, vHeight);
    266. if (matrix == null)
    267. matrix = new Matrix();
    268. matrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER);
    269. }
    270. }
    271. }
    272. }

    9.圆角FrameLayout     RadiusFrameLayout

    1. public class RadiusFrameLayout extends AutoFrameLayout implements IRadiusLayout{
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusFrameLayout(Context context) {
    26. this(context, null);
    27. }
    28. public RadiusFrameLayout(Context context, AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    36. public RadiusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    37. super(context, attrs, defStyleAttr, defStyleRes);
    38. init(context, attrs);
    39. }
    40. @Override
    41. protected void init(Context context, AttributeSet attrs) {
    42. super.init(context, attrs);
    43. setWillNotDraw(false);
    44. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    45. // 读取圆角配置
    46. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    47. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    48. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    49. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    50. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    51. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    52. // 获取背景信息
    53. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    54. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    55. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    56. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    57. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    58. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    59. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
    60. if (bgShaderColors == null)
    61. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    62. // 获取边框信息
    63. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    64. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    65. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    66. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    67. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    68. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    69. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    70. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    71. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    72. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    73. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
    74. if (solidShaderColors == null)
    75. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    76. radiusType.recycle();
    77. // 角度边长不能小于0
    78. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    79. //如果四个角的值没有设置,那么就使用通用的radius的值。
    80. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    81. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    82. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    83. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    84. if (bgColorStateList == null) {
    85. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    86. }
    87. if (solidColorStateList == null) {
    88. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    89. }
    90. if (lineType == SOLID_TYPE_DASH) {
    91. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    92. } else {
    93. dashPathEffect = null;
    94. }
    95. }
    96. @Override
    97. public void setBackgroundColor(int color) {
    98. this.bgColorStateList = ColorStateList.valueOf(color);
    99. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    100. if (radiusDrawable != null) {
    101. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    102. }
    103. forceRefreshLayout();
    104. }
    105. @Override
    106. public void setBackgroundColor(ColorStateList bgColorStateList) {
    107. this.bgColorStateList = bgColorStateList;
    108. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    109. if (radiusDrawable != null) {
    110. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    111. }
    112. forceRefreshLayout();
    113. }
    114. @Override
    115. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    116. if (dashPathEffect != null) {
    117. this.dashPathEffect = dashPathEffect;
    118. forceRefreshLayout();
    119. }
    120. }
    121. @Override
    122. public void setSolidColor(int color) {
    123. this.solidColorStateList = ColorStateList.valueOf(color);
    124. if (radiusDrawable != null) {
    125. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    126. }
    127. forceRefreshLayout();
    128. }
    129. @Override
    130. public void setSolidColor(ColorStateList solidColorStateList) {
    131. this.solidColorStateList = solidColorStateList;
    132. if (radiusDrawable != null) {
    133. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    134. }
    135. forceRefreshLayout();
    136. }
    137. @Override
    138. public void setRadius(int radius) {
    139. if (radius >= 0) {
    140. leftTopRadius = radius;
    141. rightTopRadius = radius;
    142. rightBottomRadius = radius;
    143. leftBottomRadius = radius;
    144. forceRefreshLayout();
    145. }
    146. }
    147. @Override
    148. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    149. this.leftTopRadius = leftTopRadius;
    150. this.rightTopRadius = rightTopRadius;
    151. this.rightBottomRadius = rightBottomRadius;
    152. this.leftBottomRadius = leftBottomRadius;
    153. forceRefreshLayout();
    154. }
    155. @Override
    156. public void setLeftTopRadius(int leftTopRadius) {
    157. this.leftTopRadius = leftTopRadius;
    158. forceRefreshLayout();
    159. }
    160. @Override
    161. public void setRightTopRadius(int rightTopRadius) {
    162. this.rightTopRadius = rightTopRadius;
    163. forceRefreshLayout();
    164. }
    165. @Override
    166. public void setRightBottomRadius(int rightBottomRadius) {
    167. this.rightBottomRadius = rightBottomRadius;
    168. forceRefreshLayout();
    169. }
    170. @Override
    171. public void setLeftBottomRadius(int leftBottomRadius) {
    172. this.leftBottomRadius = leftBottomRadius;
    173. forceRefreshLayout();
    174. }
    175. @Override
    176. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    177. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    178. }
    179. @Override
    180. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    181. if (shapeColors == null || shapeColors.length <= 0)
    182. return;
    183. this.bgShaderType = shapeType;
    184. this.bgShaderColors = shapeColors;
    185. this.bgShaderLinearOrientation = shaderLinearOrientation;
    186. forceRefreshLayout();
    187. }
    188. @Override
    189. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    190. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    191. }
    192. @Override
    193. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    194. if (shapeColors == null || shapeColors.length <= 0)
    195. return;
    196. this.solidShaderType = shapeType;
    197. this.solidShaderColors = shapeColors;
    198. this.solidShaderLinearOrientation = shaderLinearOrientation;
    199. forceRefreshLayout();
    200. }
    201. @Override
    202. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    203. super.onLayout(changed, left, top, right, bottom);
    204. // 没有发生改变,并且不需要强制刷新就不在重新layout
    205. if (!changed && !this.forceRefreshLayout) {
    206. return;
    207. }
    208. this.forceRefreshLayout = false;
    209. width = getWidth();
    210. height = getHeight();
    211. final Path bgPath = setBackground();
    212. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    213. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    214. float elevation = Math.max(getElevation(), getTranslationZ());
    215. if (elevation > 0) {
    216. setElevation(elevation);
    217. setOutlineProvider(new ViewOutlineProvider() {
    218. @Override
    219. public void getOutline(View view, Outline outline) {
    220. if (bgPath.isConvex()) {
    221. outline.setConvexPath(bgPath);
    222. } else {
    223. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    224. leftBottomRadius, rightBottomRadius, width, height, false));
    225. }
    226. }
    227. });
    228. setClipToOutline(true);
    229. }
    230. }
    231. }
    232. private void forceRefreshLayout() {
    233. this.forceRefreshLayout = true;
    234. requestLayout();
    235. }
    236. private Path setBackground() {
    237. final Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    238. leftBottomRadius, rightBottomRadius, width, height);
    239. Shader bgShader = null;
    240. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    241. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    242. }
    243. // 边框
    244. if (solidWidth > 0) {
    245. Shader solidShader = null;
    246. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    247. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    248. }
    249. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    250. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    251. List solidPath = Arrays.asList(solidPathArray);
    252. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    253. } else {
    254. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    255. }
    256. setBackground(radiusDrawable);
    257. return bgPath;
    258. }
    259. }

    10.圆角EditText    RadiusEditText

    1. public class RadiusEditText extends ClearAbleEditText implements IRadiusLayout {
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusEditText(Context context) {
    26. this(context, null);
    27. }
    28. public RadiusEditText(Context context, AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. protected void init(Context context, AttributeSet attrs) {
    36. setFocusableInTouchMode(true);
    37. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    38. // 读取圆角配置
    39. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    40. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    41. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    42. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    43. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    44. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    45. // 获取背景信息
    46. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    47. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    48. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    49. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    50. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    51. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    52. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
    53. if (bgShaderColors == null)
    54. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    55. // 获取边框信息
    56. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    57. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    58. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    59. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    60. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    61. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    62. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    63. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    64. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    65. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    66. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
    67. if (solidShaderColors == null)
    68. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    69. radiusType.recycle();
    70. // 角度边长不能小于0
    71. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    72. //如果四个角的值没有设置,那么就使用通用的radius的值。
    73. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    74. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    75. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    76. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    77. if (bgColorStateList == null) {
    78. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    79. }
    80. if (solidColorStateList == null) {
    81. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    82. }
    83. if (lineType == SOLID_TYPE_DASH) {
    84. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    85. } else {
    86. dashPathEffect = null;
    87. }
    88. }
    89. @Override
    90. public void setBackgroundColor(int color) {
    91. this.bgColorStateList = ColorStateList.valueOf(color);
    92. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    93. if (radiusDrawable != null) {
    94. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    95. }
    96. forceRefreshLayout();
    97. }
    98. @Override
    99. public void setBackgroundColor(ColorStateList bgColorStateList) {
    100. this.bgColorStateList = bgColorStateList;
    101. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    102. if (radiusDrawable != null) {
    103. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    104. }
    105. forceRefreshLayout();
    106. }
    107. @Override
    108. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    109. if (dashPathEffect != null) {
    110. this.dashPathEffect = dashPathEffect;
    111. forceRefreshLayout();
    112. }
    113. }
    114. @Override
    115. public void setSolidColor(int color) {
    116. this.solidColorStateList = ColorStateList.valueOf(color);
    117. if (radiusDrawable != null) {
    118. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    119. }
    120. forceRefreshLayout();
    121. }
    122. @Override
    123. public void setSolidColor(ColorStateList solidColorStateList) {
    124. this.solidColorStateList = solidColorStateList;
    125. if (radiusDrawable != null) {
    126. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    127. }
    128. forceRefreshLayout();
    129. }
    130. @Override
    131. public void setRadius(int radius) {
    132. if (radius >= 0) {
    133. leftTopRadius = radius;
    134. rightTopRadius = radius;
    135. rightBottomRadius = radius;
    136. leftBottomRadius = radius;
    137. forceRefreshLayout();
    138. }
    139. }
    140. @Override
    141. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    142. this.leftTopRadius = leftTopRadius;
    143. this.rightTopRadius = rightTopRadius;
    144. this.rightBottomRadius = rightBottomRadius;
    145. this.leftBottomRadius = leftBottomRadius;
    146. forceRefreshLayout();
    147. }
    148. @Override
    149. public void setLeftTopRadius(int leftTopRadius) {
    150. this.leftTopRadius = leftTopRadius;
    151. forceRefreshLayout();
    152. }
    153. @Override
    154. public void setRightTopRadius(int rightTopRadius) {
    155. this.rightTopRadius = rightTopRadius;
    156. forceRefreshLayout();
    157. }
    158. @Override
    159. public void setRightBottomRadius(int rightBottomRadius) {
    160. this.rightBottomRadius = rightBottomRadius;
    161. forceRefreshLayout();
    162. }
    163. @Override
    164. public void setLeftBottomRadius(int leftBottomRadius) {
    165. this.leftBottomRadius = leftBottomRadius;
    166. forceRefreshLayout();
    167. }
    168. @Override
    169. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    170. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    171. }
    172. @Override
    173. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    174. if (shapeColors == null || shapeColors.length <= 0)
    175. return;
    176. this.bgShaderType = shapeType;
    177. this.bgShaderColors = shapeColors;
    178. this.bgShaderLinearOrientation = shaderLinearOrientation;
    179. forceRefreshLayout();
    180. }
    181. @Override
    182. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    183. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    184. }
    185. @Override
    186. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    187. if (shapeColors == null || shapeColors.length <= 0)
    188. return;
    189. this.solidShaderType = shapeType;
    190. this.solidShaderColors = shapeColors;
    191. this.solidShaderLinearOrientation = shaderLinearOrientation;
    192. forceRefreshLayout();
    193. }
    194. @Override
    195. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    196. super.onLayout(changed, left, top, right, bottom);
    197. // 没有发生改变,并且不需要强制刷新就不在重新layout
    198. if (!changed && !this.forceRefreshLayout) {
    199. return;
    200. }
    201. this.forceRefreshLayout = false;
    202. width = getWidth();
    203. height = getHeight();
    204. final Path bgPath = setBackground();
    205. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    206. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    207. float elevation = Math.max(getElevation(), getTranslationZ());
    208. if (elevation > 0) {
    209. setElevation(elevation);
    210. setOutlineProvider(new ViewOutlineProvider() {
    211. @Override
    212. public void getOutline(View view, Outline outline) {
    213. if (bgPath.isConvex()) {
    214. outline.setConvexPath(bgPath);
    215. } else {
    216. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    217. leftBottomRadius, rightBottomRadius, width, height, false));
    218. }
    219. }
    220. });
    221. setClipToOutline(true);
    222. }
    223. }
    224. }
    225. private void forceRefreshLayout() {
    226. this.forceRefreshLayout = true;
    227. requestLayout();
    228. }
    229. private Path setBackground() {
    230. Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    231. leftBottomRadius, rightBottomRadius, width, height);
    232. Shader bgShader = null;
    233. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    234. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    235. }
    236. // 边框
    237. if (solidWidth > 0) {
    238. Shader solidShader = null;
    239. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    240. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    241. }
    242. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    243. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    244. List solidPath = Arrays.asList(solidPathArray);
    245. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    246. } else {
    247. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    248. }
    249. setBackground(radiusDrawable);
    250. return bgPath;
    251. }
    252. }

    11.圆角Button  RadiusButton

    1. public class RadiusButton extends AppCompatButton implements IRadiusLayout {
    2. // 控件宽高
    3. private int width, height;
    4. // 圆角参数
    5. private int leftTopRadius;
    6. private int rightTopRadius;
    7. private int rightBottomRadius;
    8. private int leftBottomRadius;
    9. private RadiusDrawable radiusDrawable;
    10. private ColorStateList bgColorStateList;
    11. // 渐变背景
    12. private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    13. private int bgShaderType; // 渐变类型
    14. private int bgShaderLinearOrientation; // 线性渐变方向
    15. // 边框参数
    16. private int solidWidth;
    17. private ColorStateList solidColorStateList;
    18. private DashPathEffect dashPathEffect = null;
    19. // 渐变边框
    20. private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    21. private int solidShaderType; // 边框类型
    22. private int solidShaderLinearOrientation; // 线性渐变方向
    23. // 是否需要强制重新布局
    24. private boolean forceRefreshLayout;
    25. public RadiusButton(Context context) {
    26. this(context, null);
    27. }
    28. public RadiusButton(Context context, AttributeSet attrs) {
    29. this(context, attrs, 0);
    30. }
    31. public RadiusButton(Context context, AttributeSet attrs, int defStyleAttr) {
    32. super(context, attrs, defStyleAttr);
    33. init(context, attrs);
    34. }
    35. protected void init(Context context, AttributeSet attrs) {
    36. if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    37. // 读取圆角配置
    38. TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
    39. int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
    40. leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
    41. rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
    42. rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
    43. leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);
    44. // 获取背景信息
    45. bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
    46. int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    47. int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    48. int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    49. bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    50. bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    51. bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
    52. if (bgShaderColors == null)
    53. bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    54. // 获取边框信息
    55. solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
    56. solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
    57. int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
    58. int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
    59. int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
    60. int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
    61. int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
    62. int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
    63. solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
    64. solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    65. solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
    66. if (solidShaderColors == null)
    67. solidShaderType = ShaderUtils.SHADER_TYPE_NONE;
    68. radiusType.recycle();
    69. // 角度边长不能小于0
    70. if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
    71. //如果四个角的值没有设置,那么就使用通用的radius的值。
    72. if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
    73. if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
    74. if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
    75. if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;
    76. if (bgColorStateList == null) {
    77. bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    78. }
    79. if (solidColorStateList == null) {
    80. solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
    81. }
    82. if (lineType == SOLID_TYPE_DASH) {
    83. dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
    84. } else {
    85. dashPathEffect = null;
    86. }
    87. }
    88. @Override
    89. public void setBackgroundColor(int color) {
    90. this.bgColorStateList = ColorStateList.valueOf(color);
    91. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    92. if (radiusDrawable != null) {
    93. radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
    94. }
    95. forceRefreshLayout();
    96. }
    97. @Override
    98. public void setBackgroundColor(ColorStateList bgColorStateList) {
    99. this.bgColorStateList = bgColorStateList;
    100. this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
    101. if (radiusDrawable != null) {
    102. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    103. }
    104. forceRefreshLayout();
    105. }
    106. @Override
    107. public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
    108. if (dashPathEffect != null) {
    109. this.dashPathEffect = dashPathEffect;
    110. forceRefreshLayout();
    111. }
    112. }
    113. @Override
    114. public void setSolidColor(int color) {
    115. this.solidColorStateList = ColorStateList.valueOf(color);
    116. if (radiusDrawable != null) {
    117. radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
    118. }
    119. forceRefreshLayout();
    120. }
    121. @Override
    122. public void setSolidColor(ColorStateList solidColorStateList) {
    123. this.solidColorStateList = solidColorStateList;
    124. if (radiusDrawable != null) {
    125. radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
    126. }
    127. forceRefreshLayout();
    128. }
    129. @Override
    130. public void setRadius(int radius) {
    131. if (radius >= 0) {
    132. leftTopRadius = radius;
    133. rightTopRadius = radius;
    134. rightBottomRadius = radius;
    135. leftBottomRadius = radius;
    136. forceRefreshLayout();
    137. }
    138. }
    139. @Override
    140. public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
    141. this.leftTopRadius = leftTopRadius;
    142. this.rightTopRadius = rightTopRadius;
    143. this.rightBottomRadius = rightBottomRadius;
    144. this.leftBottomRadius = leftBottomRadius;
    145. forceRefreshLayout();
    146. }
    147. @Override
    148. public void setLeftTopRadius(int leftTopRadius) {
    149. this.leftTopRadius = leftTopRadius;
    150. forceRefreshLayout();
    151. }
    152. @Override
    153. public void setRightTopRadius(int rightTopRadius) {
    154. this.rightTopRadius = rightTopRadius;
    155. forceRefreshLayout();
    156. }
    157. @Override
    158. public void setRightBottomRadius(int rightBottomRadius) {
    159. this.rightBottomRadius = rightBottomRadius;
    160. forceRefreshLayout();
    161. }
    162. @Override
    163. public void setLeftBottomRadius(int leftBottomRadius) {
    164. this.leftBottomRadius = leftBottomRadius;
    165. forceRefreshLayout();
    166. }
    167. @Override
    168. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    169. setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    170. }
    171. @Override
    172. public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    173. if (shapeColors == null || shapeColors.length <= 0)
    174. return;
    175. this.bgShaderType = shapeType;
    176. this.bgShaderColors = shapeColors;
    177. this.bgShaderLinearOrientation = shaderLinearOrientation;
    178. forceRefreshLayout();
    179. }
    180. @Override
    181. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
    182. setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    183. }
    184. @Override
    185. public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
    186. if (shapeColors == null || shapeColors.length <= 0)
    187. return;
    188. this.solidShaderType = shapeType;
    189. this.solidShaderColors = shapeColors;
    190. this.solidShaderLinearOrientation = shaderLinearOrientation;
    191. forceRefreshLayout();
    192. }
    193. @Override
    194. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    195. super.onLayout(changed, left, top, right, bottom);
    196. // 没有发生改变,并且不需要强制刷新就不在重新layout
    197. if (!changed && !this.forceRefreshLayout) {
    198. return;
    199. }
    200. this.forceRefreshLayout = false;
    201. width = getWidth();
    202. height = getHeight();
    203. final Path bgPath = setBackground();
    204. // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
    205. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    206. float elevation = Math.max(getElevation(), getTranslationZ());
    207. if (elevation > 0) {
    208. setElevation(elevation);
    209. setOutlineProvider(new ViewOutlineProvider() {
    210. @Override
    211. public void getOutline(View view, Outline outline) {
    212. if (bgPath.isConvex()) {
    213. outline.setConvexPath(bgPath);
    214. } else {
    215. outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    216. leftBottomRadius, rightBottomRadius, width, height, false));
    217. }
    218. }
    219. });
    220. setClipToOutline(true);
    221. }
    222. }
    223. }
    224. private void forceRefreshLayout() {
    225. this.forceRefreshLayout = true;
    226. requestLayout();
    227. }
    228. private Path setBackground() {
    229. Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
    230. leftBottomRadius, rightBottomRadius, width, height);
    231. Shader bgShader = null;
    232. if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    233. bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
    234. }
    235. // 边框
    236. if (solidWidth > 0) {
    237. Shader solidShader = null;
    238. if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
    239. solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
    240. }
    241. Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
    242. leftBottomRadius, rightBottomRadius, width, height, solidWidth);
    243. List solidPath = Arrays.asList(solidPathArray);
    244. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
    245. } else {
    246. radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
    247. }
    248. setBackground(radiusDrawable);
    249. return bgPath;
    250. }
    251. }

    12.代码使用样式

    1. <com.sc.scxm.cs.radius.RadiusRelativeLayout
    2. android:layout_centerInParent="true"
    3. android:layout_width="@dimen/dp_100"
    4. android:layout_height="@dimen/dp_100"
    5. app:rv_radius_all="@dimen/dp_10"
    6. app:rv_background_color="@color/colorAccent"
    7. app:rv_solid_width="@dimen/dp_1"
    8. app:rv_solid_shader_start_color="#CDBC78"
    9. app:rv_solid_shader_middle_color="#F5FFE3"
    10. app:rv_solid_shader_middle_color2="#CCBA77"
    11. app:rv_solid_shader_middle_color3="@color/white"
    12. app:rv_solid_shader_end_color="#DDC66D"
    13. app:rv_solid_shader_type="linear"
    14. />

  • 相关阅读:
    JMeter 做接口性能测试,YYDS!
    L1-028 判断素数
    [数据结构] 图---图的邻接矩阵存储方式模拟实现,包括BFS广度优先遍历和DFS深度优先遍历(上)
    【WIFI】【WPS】如何从log角度判断WPS 已经连接上
    JAVA常见基础面试问题汇集
    麒麟V10服务器搭建FTP服务
    AI大模型的制作:RAG和向量数据库,分别是什么?
    binder通信之Messenger介绍
    第十七章:Java连接数据库jdbc(java和myql数据库连接)
    prometheus中PromQL查询语言
  • 原文地址:https://blog.csdn.net/qq_15059163/article/details/126974784