我被要求研究这种称为Squircle的形状。它非常类似圆角矩形或带圆角的正方形。然而,这不是理想的形状。我怎样才能做到这一点?
答案 0 :(得分:1)
https://github.com/MicroRJ/Android-Canvas-Squircle
我想出了自己的解决方案。我没有使用任何技巧,即使对于RecyclerViews,此方法也非常有效。
这就是我的做法。
首先,我制作了一个类,其中包含绘图的所有逻辑,并且具有 该过程更加有效。
object SuperEllipsePerformanceCalculations {
private val bitmapForSizes = ArrayList<Pair<Bitmap, Int>>()
fun getBitmap(w: Int, h: Int, p: Int, paint: Paint): Bitmap {
bitmapForSizes.forEach {
/**
* Check if there are other bitmaps with the same specifications, to avoid creating
* and recalculating, this increases performance drastically.
*/
if (it.first.width == w && it.first.height == h && it.second == p) {
return it.first
}
}
/**
* If no Bitmaps were found create a new one, and store for other views
* that might use it
*/
val newB = getSquircleBitmapBackground(w, h, p, paint)
bitmapForSizes.add(Pair(newB, p))
log("New bitmap added Total: ${bitmapForSizes.size}")
return newB
}
private fun getSquircleBitmapBackground(w: Int, h: Int, p: Int, paint: Paint): Bitmap {
val b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
val c = Canvas(b)
c.translate(w / 2f, h / 2f)
c.drawPath(getPath((w / 2) - p, (h / 2) - p), paint)
return b
}
//todo fix path not closing
private fun getPath(radX: Int, radY: Int): Path {
val corners = 0.6
var l = 0.0
var angle: Double
val path = Path()
for (i in 0 until 360) {
angle = Math.toRadians(l)
val x = getX(radX, angle, corners)
val y = getY(radY, angle, corners)
path.moveTo(x, y)
l++
angle = Math.toRadians(l)
val x2 = getX(radX, angle, corners)
val y2 = getY(radY, angle, corners)
path.lineTo(x2, y2)
}
path.close()
return path
}
private fun getX(radX: Int, angle: Double, corners: Double) =
(Math.pow(abs(cos(angle)), corners) * radX * sgn(cos(angle))).toFloat()
private fun getY(radY: Int, angle: Double, corners: Double) =
(Math.pow(abs(sin(angle)), corners) * radY * sgn(sin(angle))).toFloat()
private fun sgn(value: Double) = if (value > 0.0) 1.0 else if (value < 0.0) -1.0 else 0.0
/**
* I suggest you call this method on the activity's on destroy event
*/
fun release() {
bitmapForSizes.forEach { it.first.recycle() }
bitmapForSizes.clear()
}
然后我创建了一个自定义ImageView
open class SuperEllipseImageView : ImageView {
private val paint = Paint()
private var shapePadding = 0
private var w = 0
private var h = 0
private var squircleBitmap: Bitmap? = null
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
this.w = w
this.h = h
/**
* Request a new squircle bitmap, this is a smart method and it will only return a new Bitmap
* instance if it cannot find a previously created Bitmap with the same size (width and height),
* this will increase performance
*/
squircleBitmap = getBitmap(this.h, this.w, shapePadding, paint)
postInvalidate()
}
init {
/**
* You can change this to any color you want
*/
val typedValue = TypedValue()
val theme = context!!.theme
theme.resolveAttribute(R.attr.colorAccentTheme, typedValue, true)
paint.color = typedValue.data
paint.strokeWidth = 6f
/**
* Here's the problem, the shape is not being filled, only the stroke
* is drawn, I have a few theories of what's happening.
*/
paint.style = Paint.Style.FILL_AND_STROKE
paint.isAntiAlias = true
/**
* I suggest the shape padding be no less that the stroke width to prevent
* the shape borders/outskirts from going out of bounds
*/
shapePadding = paint.strokeWidth.toInt()
}
override fun onDraw(canvas: Canvas?) {
if (squircleBitmap == null) {
return
}
/**
* Draw a previously instantiated Bitmap instead of a path, this will increase performance drastically
*/
canvas?.drawBitmap(squircleBitmap!!, 0f, 0f, null)
super.onDraw(canvas)
}
}
一旦完成所有设置,便将自定义视图添加到布局中。
<?xml version="1.0" encoding="utf-8"?>
<com.devrj.helium.custom.SuperEllipseImageView
android:scaleX="0"
android:scaleY="0"
android:id="@+id/superEllipseImageView"
style="@style/settings_chip"
xmlns:android="http://schemas.android.com/apk/res/android"/>
就我而言,我在具有复杂动画且一切正常的回收站视图中使用了此布局。我认为这非常有效,因为它使用了保存的位图实例。
我强烈建议您查看销售代表,这一点更加清楚。
这是我的特定输出。
答案 1 :(得分:0)
您可以创建Shape Drawable并设置转角半径。