I need change bars view in barchart in MPAndroidChart like this... that's how it should be Is it possible to set custom bar drawable, or just smooth the corners, or something like this?
Now I have this
My code:
fun barChart(
activity: Activity,
chart: BarChart,
entries: ArrayList<BarEntry>,
columnsNames: ArrayList<String>,
colors: ArrayList<Int>,
) {
val colorsTemplate = intArrayOf(
Color.rgb(74, 160, 150), Color.rgb(74, 160, 150), Color.rgb(74, 160, 150),
Color.rgb(74, 160, 150), Color.rgb(74, 160, 150)
)
val colors: ArrayList<Int> = ArrayList()
for (color in colorsTemplate) {
colors.add(color)
}
val dataSet = BarDataSet(entries, "")
val decimalFormat = DecimalFormat("0.##")
dataSet.setValueFormatter(object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return decimalFormat.format(value)
}
})
dataSet.colors = colors
val data = BarData(dataSet)
data.barWidth = 0.5f
data.setDrawValues(true)
chart.setData(data)
chart.setAutoScaleMinMaxEnabled(true)
val xAxis = chart.getXAxis()
xAxis.valueFormatter = IndexAxisValueFormatter(columnsNames)
xAxis.position = (XAxis.XAxisPosition.BOTTOM)
xAxis.setDrawGridLines(false)
xAxis.setDrawAxisLine(false)
xAxis.granularity = 1f
xAxis.labelCount = columnsNames!!.size
xAxis.labelRotationAngle = 360f
chart.legend.isEnabled = false
chart.description.isEnabled = false
chart.axisRight.isEnabled = false
chart.axisLeft.labelCount = 3
chart.axisLeft.axisMinimum = 0f
chart.animateY(1000)
chart.invalidate()
}
Related
I would like to create a watermark effect in my app using text as shown in the picture below.
I achieved this by using canvas and bitmap, is there any other reliable way to do this?
Here is my composable function
#Composable
fun WaterMark(
modifier: Modifier = Modifier,
content: (#Composable BoxScope.() -> Unit)? = null,
) {
val watermarkText: String = "some mutable text"
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.textSize = LocalContext.current.dpToPx(24).toFloat()
paint.color = PSCoreColours.psCoreColours.onSurface.hashCode()
paint.textAlign = Paint.Align.LEFT
paint.alpha = (255 * 0.25).toInt()
val baseline: Float = -paint.ascent()
val image: Bitmap = Bitmap.createBitmap(paint.measureText(watermarkText).toInt(),
(baseline + paint.descent()).toInt(),
Bitmap.Config.ARGB_8888)
val canvas = android.graphics.Canvas(image)
canvas.drawText(watermarkText, 0f, baseline, paint)
val rotationMatrix: Matrix = Matrix().apply { postRotate(-45f) }
val rotatedImage: Bitmap = Bitmap.createBitmap(image, 0, 0, image.width, image.height, rotationMatrix, true)
val pattern: ImageBitmap = rotatedImage.asImageBitmap()
Box {
content?.let { it() }
Canvas(
modifier = modifier
) {
val totalWidth = size.width / pattern.width
val totalHeight = size.height / pattern.height
var x = 0f
var y = 0f
for (i in 0..totalHeight.toInt()) {
y = (i * pattern.height).toFloat()
for (j in 0..totalWidth.toInt()) {
x = (j * pattern.width).toFloat()
drawImage(
pattern,
colorFilter = null,
topLeft = Offset(x, y),
)
}
}
}
}
}
You can do custom layouts in compose for this
private const val SPACING = 100
#Composable
fun Watermark(
content: #Composable BoxScope.() -> Unit,
) {
Box {
content()
Layout(
content = {
// Repeating the placeables, 6 should do for now but we should be able to calculate this too
repeat(6) {
Text(
text = watermarkText,
..
)
}
}
) { measurables, constraints ->
// Measuring all the placables
val placeables: List<Placeable> = measurables
.map { measurable -> measurable.measure(constraints) }
layout(constraints.maxWidth, constraints.maxHeight) {
// Calculating the max width of a placable
val maxWidth: Double = placeables.maxOf { it.width }.toDouble()
// Calculating the max width of a tile given the text is rotated
val tileSize: Int = (constraints.maxWidth / atan(maxWidth)).toInt()
placeables
.chunked(2) // Placing 2 columns
.forEachIndexed { index, (first, second) ->
val indexedTileSize: Int = index * tileSize
first.placeRelativeWithLayer(-SPACING, indexedTileSize + SPACING) { rotationZ = -45f }
second.placeRelativeWithLayer(tileSize, indexedTileSize) { rotationZ = -45f }
}
}
}
}
}
Watermark function creates instance of Paint and Bitmap on each recomposition. You should wrap them with remember as in this answer.
However you might, i think, do what you do fully Compose way without Paint and Bitmap either using Modifier.drawWithContent{} and drawText function of DrawScope and using translate or rotate inside DrawScope.
This is a drawText sample to understand how you can create and store TextLayoutResult remember.
And another sample using Modifier.drawWithContent
You can also try using Modifier.drawWithCache to cache TextLayoutResult in layout phase instead of composition phase which is suggested by Google Compose developer works on Text here
Is there a way to make a box with a wavy top with Canvas?
I would like to know if this effect can be achieved directly with a Canvas, it is not necessary to have a scrolling animation.
It's not quite clear why you're talking about Canvas. To crop a view like this, you can use a custom Shape and apply it to your view with Modifier.clip. Here's a shape you can use:
class WavyShape(
private val period: Dp,
private val amplitude: Dp,
) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density,
) = Outline.Generic(Path().apply {
val wavyPath = Path().apply {
val halfPeriod = with(density) { period.toPx() } / 2
val amplitude = with(density) { amplitude.toPx() }
moveTo(x = -halfPeriod / 2, y = amplitude)
repeat(ceil(size.width / halfPeriod + 1).toInt()) { i ->
relativeQuadraticBezierTo(
dx1 = halfPeriod / 2,
dy1 = 2 * amplitude * (if (i % 2 == 0) 1 else -1),
dx2 = halfPeriod,
dy2 = 0f,
)
}
lineTo(size.width, size.height)
lineTo(0f, size.height)
}
val boundsPath = Path().apply {
addRect(Rect(offset = Offset.Zero, size = size))
}
op(wavyPath, boundsPath, PathOperation.Intersect)
})
}
If you really need to use this inside Canvas for some reason, you can pass the same Path that I create inside WavyShape to DrawScope.clipPath, so that the contents of the clipPath block will be clipped.
Apply custom shape to your Image or any other view:
Image(
painter = painterResource(id = R.drawable.my_image_1),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.clip(WavyShape(period = 100.dp, amplitude = 50.dp))
)
Result:
Since Jetpack Compose has this limitation I'm looking for workaround solution for this problem?
Maybe Canvas could do the trick? I really appreciate if someone can provide code snippets of how to render shadow in Jetpack Compose for Card, Box, Column, Row etc utilising additional parameters like X and Y offset, blur and opacity with custom implementation (Canvas or something else)?
I managed to find solution thanks to this code snippets
fun Modifier.advancedShadow(
color: Color = Color.Black,
alpha: Float = 1f,
cornersRadius: Dp = 0.dp,
shadowBlurRadius: Dp = 0.dp,
offsetY: Dp = 0.dp,
offsetX: Dp = 0.dp
) = drawBehind {
val shadowColor = color.copy(alpha = alpha).toArgb()
val transparentColor = color.copy(alpha = 0f).toArgb()
drawIntoCanvas {
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
frameworkPaint.color = transparentColor
frameworkPaint.setShadowLayer(
shadowBlurRadius.toPx(),
offsetX.toPx(),
offsetY.toPx(),
shadowColor
)
it.drawRoundRect(
0f,
0f,
this.size.width,
this.size.height,
cornersRadius.toPx(),
cornersRadius.toPx(),
paint
)
}
}
Based on the post above, I've changed the implementation to match the parameters of the site below
https://html-css-js.com/css/generator/box-shadow/
My current implementation is the following
internal fun Modifier.coloredShadow(
color: Color = Color.Black,
borderRadius: Dp = 0.dp,
blurRadius: Dp = 0.dp,
offsetY: Dp = 0.dp,
offsetX: Dp = 0.dp,
spread: Float = 0f,
modifier: Modifier = Modifier,
) = this.then(
modifier.drawBehind {
this.drawIntoCanvas {
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
val spreadPixel = spread.dp.toPx()
val leftPixel = (0f - spreadPixel) + offsetX.toPx()
val topPixel = (0f - spreadPixel) + offsetY.toPx()
val rightPixel = (this.size.width + spreadPixel)
val bottomPixel = (this.size.height + spreadPixel)
if (blurRadius != 0.dp) {
/*
The feature maskFilter used below to apply the blur effect only works
with hardware acceleration disabled.
*/
frameworkPaint.maskFilter =
(BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL))
}
frameworkPaint.color = color.toArgb()
it.drawRoundRect(
left = leftPixel,
top = topPixel,
right = rightPixel,
bottom = bottomPixel,
radiusX = borderRadius.toPx(),
radiusY = borderRadius.toPx(),
paint
)
}
}
)
Feel free to add comments and help to evolve this gist
https://gist.github.com/hernandazevedo/dfd41b39d0156c740a195f6f5866ce20
I've recently created shadow modifier allowing to set both radius and offsets.
#Immutable
data class Shadow(
#Stable val offsetX: Dp,
#Stable val offsetY: Dp,
#Stable val radius: Dp,
#Stable val color: Color,
)
fun Modifier.withShadow(
shadow: Shadow,
shape: Shape,
) = drawBehind {
drawIntoCanvas { canvas ->
val paint = Paint()
paint.asFrameworkPaint().apply {
this.color = Color.Transparent.toArgb()
setShadowLayer(
radius = shadow.radius.toPx(),
dx = shadow.offsetX.toPx(),
dy = shadow.offsetY.toPx(),
shadowColor = shadow.color,
)
}
val outline = shape.createOutline(size, layoutDirection, this)
canvas.drawOutline(outline, paint)
}
}
So I have a dumb question, how can you make a new line within the string, instead of using Java, I would want to use Kotlin instead, the purpose of this is I want to make the TextView to display a certain string, but I also want to make a new line within the same string, but don't know the command as I search in the net, it doesn't show. Such as:
Java:
System.out.println("Hello")
System.out.println("World!")
System.out.print("Hello\n World!")
C#:
Console.WriteLine("Hello")
Console.WriteLine("World!")
Console.Write("Hello\n World!")
Here is the code of what I am planning to do:
The method
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mImageView = findViewById(R.id.image_view)
val longitudeText = mLocation.longitude
val latitudeText = mLocation.latitude
val altitudeText = mLocation.altitude
val originalBitmap = BitmapFactory.decodeResource(resources, R.drawable.gefvqkbbslamdpkxf1zv_bigstock_aerial_view_of_blue_lakes_and__227291596)?.let { it ->
firstAddWatermark(
it,
"Longitude: ${resources.getString(R.string.value_of_7, longitudeText)}м, Latitude: ${resources.getString(R.string.value_of_7, latitudeText)}м" + "\n" +
"Altitude: ${{resources.getString(R.string.value_of_2, altitudeText)}}",
WatermarkOptions(
Corner.TOP_LEFT,
textSizeToWidthRatio = 0.015f,
paddingToWidthRatio = 0.03f,
Color.RED,
Color.BLACK,
typeface = null
)
)
}
mImageView.setImageBitmap(originalBitmap)
}
The functions:
private fun firstAddWatermark(bitmap: Bitmap, firstWatermarkText: String, options: WatermarkOptions = WatermarkOptions()): Bitmap {
val result = bitmap.copy(bitmap.config, true)
val canvas = Canvas(result)
val paint = Paint(ANTI_ALIAS_FLAG or DITHER_FLAG)
paint.textAlign = when (options.corner) {
Corner.TOP_LEFT,
Corner.BOTTOM_LEFT -> Paint.Align.LEFT
Corner.TOP_RIGHT,
Corner.BOTTOM_RIGHT -> Paint.Align.RIGHT
}
val textSize = result.width * options.textSizeToWidthRatio
paint.textSize = textSize
paint.color = options.textColor
if (options.shadowColor != null) {
paint.setShadowLayer(textSize / 2, 0f, 0f, options.shadowColor)
}
if (options.typeface != null) {
paint.typeface = options.typeface
}
val padding = result.width * options.paddingToWidthRatio
val coordinates = calculateCoordinates(firstWatermarkText, paint, options, canvas.width, canvas.height, padding)
canvas.drawText(firstWatermarkText, coordinates.x, coordinates.y, paint)
return result
}
private fun calculateCoordinates(watermarkText: String, paint: Paint, options: WatermarkOptions, width: Int, height: Int, padding: Float): PointF {
val x = when (options.corner) {
Corner.TOP_LEFT,
Corner.BOTTOM_LEFT -> {
padding
}
Corner.TOP_RIGHT,
Corner.BOTTOM_RIGHT -> {
width - padding
}
}
val y = when (options.corner) {
Corner.BOTTOM_LEFT,
Corner.BOTTOM_RIGHT -> {
height - padding
}
Corner.TOP_LEFT,
Corner.TOP_RIGHT -> {
val bounds = Rect()
paint.getTextBounds(watermarkText, 0, watermarkText.length, bounds)
val textHeight = bounds.height()
textHeight + padding
}
}
return PointF(x, y)
}
enum class Corner {
TOP_LEFT,
TOP_RIGHT,
BOTTOM_LEFT,
BOTTOM_RIGHT
}
data class WatermarkOptions(
val corner: Corner = Corner.BOTTOM_RIGHT,
val textSizeToWidthRatio: Float = 0.04f,
val paddingToWidthRatio: Float = 0.03f,
#ColorInt val textColor: Int = Color.WHITE,
#ColorInt val shadowColor: Int? = Color.BLACK,
val typeface: Typeface? = null
)
The result of that:
postimg.cc/75SF4v3F
You can use \n in the kotlin as well.
I created a custom view to draw a line, but progressively. I tried to use PathMeasure and getSegment, but the effect doesn't work. It just keeps drawing the line already with the final size.
private val paint = Paint().apply {
isAntiAlias = true
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 10f
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val path = Path().apply {
moveTo(width/2.toFloat(), height/2.toFloat())
lineTo(width/2.toFloat(), height/4.toFloat())
}
val measure = PathMeasure(path, false)
val length = measure.length
val partialPath = Path()
measure.getSegment(0.0f, length, partialPath, true)
partialPath.rLineTo(0.0f, 0.0f)
canvas!!.drawPath(partialPath, paint)
}
you can do this with DashPathEffect
DashPathEffect dashPathEffect = new DashPathEffect(new float[]{1000.0f,9999999},0);
Paint.setPathEffect(dashPathEffect);
change 1000 to your length ("on" parts in Dash)
and set 99999999 to your max ("off" parts in Dash)
play with this parameters and read this article please
Here's how I made it, as #mohandes explained:
private var path = Path()
private var paint = Paint()
private val dashes = floatArrayOf(125f, 125f)
init {
paint = Paint().apply {
isAntiAlias = true
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 10.0f
pathEffect = CornerPathEffect(8f)
}
path = Path().apply {
moveTo(312f, 475f)
lineTo(312f, 375f)
}
val lineAnim = ValueAnimator.ofFloat(100f, 0f)
lineAnim.interpolator = LinearInterpolator()
lineAnim.addUpdateListener {
paint.pathEffect = ComposePathEffect(DashPathEffect(dashes, lineAnim.animatedValue as Float), CornerPathEffect(8f))
invalidate()
}
lineAnim.duration = 1000
lineAnim.start()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas!!.drawPath(path, paint)
}