diff --git a/README.md b/README.md index 78ec390..525bfb4 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ ##### `Node Line Chart` -[![](https://jitpack.io/v/oky2abbas/reactor.svg)](https://jitpack.io/#dfmAbbas/reactor) -[![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/oky2abbas/reactor) -[![API](https://img.shields.io/badge/API-14%2B-blue.svg?style=flat)](https://github.com/oky2abbas/reactor) +[![](https://jitpack.io/v/naqdi/reactor.svg)](https://jitpack.io/#dfmAbbas/reactor) +[![License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/naqdi/reactor) +[![API](https://img.shields.io/badge/API-14%2B-blue.svg?style=flat)](https://github.com/naqdi/reactor) **Chain Chart View** is a -[![Donate](https://img.shields.io/badge/Donate-green)](https://idpay.ir/oky2abbas) +[![Donate](https://img.shields.io/badge/Donate-green)](https://idpay.ir/naqdi) **Bitcoin (BTC) Donate: `bc1qhgvnx2nfzr0qep5fnsevyyn59k32wpe7q0c7nh`** diff --git a/build.gradle b/build.gradle index 61abf78..c150856 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - minV = 14 + minV = 17 targetV = 30 vCode = 1 vName = '0.9.0' @@ -9,12 +9,13 @@ buildscript { gradleV = '4.1.0' kotlinV = '1.4.10' materialV = '1.2.1' + appcompatV = '1.2.0' } repositories { + maven { url 'https://maven.google.com' } google() jcenter() - mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:$gradleV" @@ -24,6 +25,7 @@ buildscript { allprojects { repositories { + maven { url 'https://maven.google.com' } google() jcenter() } diff --git a/library/build.gradle b/library/build.gradle index 4cf94a3..a412538 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -32,4 +32,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinV" + implementation "androidx.appcompat:appcompat:$appcompatV" } \ No newline at end of file diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index 2057551..49d7951 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.naqdi.chart"> \ No newline at end of file diff --git a/library/src/main/java/com/oky2abbas/library/view/ChainChartView.kt b/library/src/main/java/com/naqdi/chart/ChainChartView.kt similarity index 52% rename from library/src/main/java/com/oky2abbas/library/view/ChainChartView.kt rename to library/src/main/java/com/naqdi/chart/ChainChartView.kt index dba23b3..02564b1 100644 --- a/library/src/main/java/com/oky2abbas/library/view/ChainChartView.kt +++ b/library/src/main/java/com/naqdi/chart/ChainChartView.kt @@ -1,47 +1,34 @@ -package com.oky2abbas.library.view +package com.naqdi.chart import android.annotation.SuppressLint import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.DashPathEffect -import android.graphics.Paint +import android.graphics.* +import android.graphics.Paint.ANTI_ALIAS_FLAG import android.text.TextPaint import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import com.oky2abbas.library.ext.closestValue -import com.oky2abbas.library.ext.dpToPx -import com.oky2abbas.library.ext.getMaxGraph -import com.oky2abbas.library.ext.getSize -import com.oky2abbas.library.model.Graph -import com.oky2abbas.library.utils.FakeGenerator +import androidx.core.content.res.ResourcesCompat +import com.naqdi.chart.model.Line +import com.naqdi.chart.utils.* -/* - @author: abbas naqdi (naqdi) - - All the following code was written by @naqdi without any copying - - Use of this source code is not permitted without permission - - This source was sent to https://zelkaa.com - */ class ChainChartView @JvmOverloads constructor( context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0 ) : View( context, attrs, defStyleAttr ) { - - private var graphList = listOf() - private var nameList = listOf() + private var lineList = listOf() + private var titleList = listOf() private var rangeList = listOf() private val splitList = arrayListOf() + private val circleRadius = 9f private var selectedX = 0f private val minLeftMargin = 10f private var maxLeftMargin = minLeftMargin * 2 - private val topMargin = 30f - private val bottomMargin = 30f - private val textSize = 11f + private val topBottomMargin = 30f private var maxGraphTitleSize = 50f private var maxNode = 0f @@ -49,67 +36,142 @@ class ChainChartView @JvmOverloads constructor( private var widthVal = 0f private var heightVal = 0f - private val circlePaint = Paint().apply { - style = Paint.Style.FILL + + private val nodePaint = Paint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true + style = Paint.Style.FILL_AND_STROKE + strokeCap = Paint.Cap.ROUND } - private val linePaint = Paint().apply { - style = Paint.Style.FILL + private val linePaint = Paint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true + style = Paint.Style.FILL strokeCap = Paint.Cap.ROUND - strokeWidth = 3f } - private val badgePaint = Paint().apply { - style = Paint.Style.FILL + private val badgePaint = Paint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true + style = Paint.Style.FILL_AND_STROKE strokeCap = Paint.Cap.ROUND - strokeWidth = 5f + strokeWidth = context.dpToPx(1f) } - private val liteLinePaint = Paint().apply { - style = Paint.Style.FILL + private val liteLinePaint = Paint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true + style = Paint.Style.FILL strokeCap = Paint.Cap.ROUND - strokeWidth = 0.09f + color = 0x10000000 } - private val strokeLinePaint = Paint().apply { - style = Paint.Style.STROKE + private val strokeLinePaint = Paint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true - strokeWidth = 4f + style = Paint.Style.FILL pathEffect = DashPathEffect(floatArrayOf(10f, 12f), 0f) - strokeCap = Paint.Cap.BUTT - color = 0x20000000 + strokeCap = Paint.Cap.ROUND + color = 0x40000000 } - private val textPaint = TextPaint().apply { + private val textPaint = TextPaint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true - textSize = getContext().dpToPx(textSize) - color = Color.BLACK + style = Paint.Style.FILL + strokeCap = Paint.Cap.ROUND } - private val textCenterPaint = TextPaint().apply { + private val textCenterPaint = TextPaint(ANTI_ALIAS_FLAG).apply { isAntiAlias = true - textSize = getContext().dpToPx(textSize) - color = Color.BLACK + style = Paint.Style.FILL + strokeCap = Paint.Cap.ROUND textAlign = Paint.Align.CENTER } - /* - The client can use this method to set data in graphs - Note: The source instance of using this method is available - in the `FakeGenerator` object. - */ + init { + val typeArray = context.theme.obtainStyledAttributes( + attrs, R.styleable.cc_line, + 0, 0 + ) + + try { + typeArray.getDimension( + R.styleable.cc_line_cc_text_size, + context.dpToPx(11f) + ).let { + textCenterPaint.textSize = it + textPaint.textSize = it + } + + typeArray.getDimension( + R.styleable.cc_line_cc_line_size, + context.dpToPx(1f) + ).let { + linePaint.strokeWidth = it + } + + typeArray.getDimension( + R.styleable.cc_line_cc_node_size, + context.dpToPx(1f) + ).let { + nodePaint.strokeWidth = it + } + + typeArray.getColor( + R.styleable.cc_line_cc_text_color, + Color.BLACK + ).let { + textCenterPaint.color = it + textPaint.color = it + } + + typeArray.getResourceId( + R.styleable.cc_line_cc_font_family, + 0 + ).let { + if (it == 0) return@let + val font = ResourcesCompat.getFont(context, it) + textCenterPaint.typeface = font + textPaint.typeface = font + } + + } finally { + typeArray.recycle() + } + } + + fun setFontFamily(font: Typeface) { + textCenterPaint.typeface = font + textPaint.typeface = font + } + + fun setTextSize(size: Float) { + textCenterPaint.textSize = context.dpToPx(size) + textPaint.textSize = context.dpToPx(size) + } + + fun setLineSize(size: Float) { + linePaint.strokeWidth = context.dpToPx(size) + } + + fun setNodeSize(size: Float) { + nodePaint.strokeWidth = context.dpToPx(size) + } + + fun setTextColor(color: Int) { + textCenterPaint.color = color + textPaint.color = color + } + +/* + The client can use this method to set data in graphs + Note: The source instance of using this method is available + in the `FakeGenerator` object. + */ fun setData( - graphList: List, - nameList: List, + lineList: List, + intervalList: List, rangeList: List ) { - this.graphList = graphList - this.nameList = nameList + this.lineList = lineList + this.titleList = intervalList this.rangeList = rangeList invalidate() @@ -125,8 +187,8 @@ class ChainChartView @JvmOverloads constructor( override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) - // - if (graphList.isEmpty()) { + //work only in editable mode + if (isInEditMode && lineList.isEmpty()) { setData( FakeGenerator.generate(), FakeGenerator.nameList, @@ -140,7 +202,7 @@ class ChainChartView @JvmOverloads constructor( This code finds the largest graph based on the number of nodes, then divides it based on this graph. */ - graphList.getMaxGraph()?.let { + lineList.getMaxGraph()?.let { for (index in it.nodeList.indices) { splitList.add(it.getGraphX(index)) } @@ -151,7 +213,7 @@ class ChainChartView @JvmOverloads constructor( then converts this range to float size and is used for padding */ - rangeList.map { it.length }.maxOrNull()?.let { + rangeList.map { it.length }.max()?.let { maxLeftMargin = minLeftMargin + (it + 110).toFloat() } @@ -159,20 +221,20 @@ class ChainChartView @JvmOverloads constructor( This code finds the largest title in the graph and converts this title to a float size */ - graphList.map { it.title.getSize() }.maxOrNull()?.let { + lineList.map { it.title.getSize() }.max()?.let { maxGraphTitleSize = it } //This code finds the largest node among all graphs - graphList.flatMap { it.nodeList }.max()?.let { + lineList.flatMap { it.nodeList }.max()?.let { maxNode = it } - graphList.forEachIndexed { index, graph -> + lineList.forEachIndexed { index, graph -> //Draw title badge canvas?.drawCircle( - maxLeftMargin + (index * maxGraphTitleSize), topMargin, + maxLeftMargin + (index * maxGraphTitleSize), topBottomMargin, 10f, badgePaint.apply { color = graph.color } @@ -182,7 +244,7 @@ class ChainChartView @JvmOverloads constructor( canvas?.drawText( graph.title, minLeftMargin + maxLeftMargin + (maxGraphTitleSize * index), - topMargin * 2, textPaint + topBottomMargin * 2, textPaint ) rangeList.reversed().forEachIndexed { index, value -> @@ -210,29 +272,29 @@ class ChainChartView @JvmOverloads constructor( } @SuppressLint("ClickableViewAccessibility") - private fun generateGraph(canvas: Canvas?, graph: Graph) { - for (index in (graph.nodeList.indices)) { + private fun generateGraph(canvas: Canvas?, line: Line) { + for (index in (line.nodeList.indices)) { - val currentX = graph.getGraphX(index) - val currentY = graph.getGraphY(index) + val currentX = line.getGraphX(index) + val currentY = line.getGraphY(index) - val name = nameList[index] + val name = titleList[index] //Draw node text canvas?.drawText( - name, currentX, heightVal - topMargin, + name, currentX, heightVal - topBottomMargin, textCenterPaint ) - if (index < graph.nodeList.size - 1) { - val nextX = graph.getGraphX(index + 1) - val nextY = graph.getGraphY(index + 1) + if (index < line.nodeList.size - 1) { + val nextX = line.getGraphX(index + 1) + val nextY = line.getGraphY(index + 1) //Draw node line canvas?.drawLine( currentX, currentY, nextX, nextY, linePaint.apply { - color = graph.color + color = line.color } ) } @@ -241,14 +303,14 @@ class ChainChartView @JvmOverloads constructor( //Draw selected circle canvas?.drawCircle(currentX, currentY, circleRadius, - circlePaint.apply { - color = graph.color + nodePaint.apply { + color = line.color }) //Draw selected vertical line canvas?.drawLine( - currentX, topMargin * 3, currentX, - heightVal - topMargin * 3, + currentX, topBottomMargin * 3, currentX, + heightVal - topBottomMargin * 3, strokeLinePaint ) } @@ -267,28 +329,28 @@ class ChainChartView @JvmOverloads constructor( } //Calculate the width for each node - private fun Graph.getGraphX(index: Int): Float { + private fun Line.getGraphX(index: Int): Float { val w = (widthVal / nodeList.size) return (index * w) + (maxLeftMargin) } //Calculate the height for each node - private fun Graph.getGraphY(index: Int): Float { + private fun Line.getGraphY(index: Int): Float { val value = nodeList[index] val h = (heightVal - nodeList[index]) - return h - (topMargin * 4) + return h - (topBottomMargin * 4) } - /* - Adjust nodes based on height - If a node is larger than height, all nodes are rendered in height - */ +/* + Adjust nodes based on height + If a node is larger than height, all nodes are rendered in height + */ - private fun Graph.round(): Graph { + private fun Line.round(): Line { if (maxNode < heightVal) return this - val nav = (maxNode - (heightVal - topMargin * 7)) + val nav = (maxNode - (heightVal - topBottomMargin * 7)) val nav2 = (maxNode / nav) nodeList = nodeList.map { it - (it / nav2) } @@ -297,7 +359,7 @@ class ChainChartView @JvmOverloads constructor( //This method finds the distance between the range elements private fun getRangeYVal(index: Int): Float { - val h = (heightVal / rangeList.size) - (topMargin) - return (h * index) + (topMargin * 3.5f) + val h = (heightVal / rangeList.size) - (topBottomMargin) + return (h * index) + (topBottomMargin * 3.5f) } } \ No newline at end of file diff --git a/library/src/main/java/com/naqdi/chart/model/Line.kt b/library/src/main/java/com/naqdi/chart/model/Line.kt new file mode 100644 index 0000000..1defd01 --- /dev/null +++ b/library/src/main/java/com/naqdi/chart/model/Line.kt @@ -0,0 +1,10 @@ +package com.naqdi.chart.model + +import androidx.annotation.ColorInt + + +data class Line( + val title: String, + @ColorInt val color: Int, + var nodeList: List +) \ No newline at end of file diff --git a/library/src/main/java/com/oky2abbas/library/ext/ConversionsExt.kt b/library/src/main/java/com/naqdi/chart/utils/ConversionsExt.kt similarity index 57% rename from library/src/main/java/com/oky2abbas/library/ext/ConversionsExt.kt rename to library/src/main/java/com/naqdi/chart/utils/ConversionsExt.kt index 284bcd4..ceeb920 100644 --- a/library/src/main/java/com/oky2abbas/library/ext/ConversionsExt.kt +++ b/library/src/main/java/com/naqdi/chart/utils/ConversionsExt.kt @@ -1,8 +1,9 @@ -package com.oky2abbas.library.ext +package com.naqdi.chart.utils import android.content.Context +import android.content.res.Resources import android.util.TypedValue -import com.oky2abbas.library.model.Graph +import com.naqdi.chart.model.Line internal fun Context.dpToPx(dp: Float) = TypedValue.applyDimension( @@ -14,9 +15,9 @@ internal fun String.getSize(): Float { } internal fun List.closestValue(value: Float): Float? { - return minBy { kotlin.math.abs(value - it) } + return minByOrNull { kotlin.math.abs(value - it) } } -internal fun List.getMaxGraph(): Graph? { - return maxBy { it.nodeList.size } +internal fun List.getMaxGraph(): Line? { + return maxByOrNull { it.nodeList.size } } \ No newline at end of file diff --git a/library/src/main/java/com/oky2abbas/library/utils/FakeGenerator.kt b/library/src/main/java/com/naqdi/chart/utils/FakeGenerator.kt similarity index 73% rename from library/src/main/java/com/oky2abbas/library/utils/FakeGenerator.kt rename to library/src/main/java/com/naqdi/chart/utils/FakeGenerator.kt index 1a25b67..b392c4a 100644 --- a/library/src/main/java/com/oky2abbas/library/utils/FakeGenerator.kt +++ b/library/src/main/java/com/naqdi/chart/utils/FakeGenerator.kt @@ -1,14 +1,14 @@ -package com.oky2abbas.library.utils +package com.naqdi.chart.utils import android.graphics.Color -import com.oky2abbas.library.model.Graph +import com.naqdi.chart.model.Line object FakeGenerator { val nameList = listOf("Jul", "Aug", "Sep", "Oct", "Nov", "Dec") - val rangeList = listOf("0", "100", "200", "500") + val rangeList = listOf("0-1K", "100K", "200K", "500K") - fun generate(): List { - val graphList = arrayListOf() + fun generate(): List { + val graphList = arrayListOf() for (i in (1..(1..3).random())) { val nodeList = arrayListOf() @@ -18,7 +18,7 @@ object FakeGenerator { } graphList.add( - Graph( + Line( "Node ${(1..9).random()}", getColorValue(i), nodeList ) ) diff --git a/library/src/main/java/com/oky2abbas/library/model/Graph.kt b/library/src/main/java/com/oky2abbas/library/model/Graph.kt deleted file mode 100644 index fea002d..0000000 --- a/library/src/main/java/com/oky2abbas/library/model/Graph.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.oky2abbas.library.model - - -data class Graph( - val title: String, - val color: Int, - var nodeList: List -) diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml new file mode 100644 index 0000000..de45e96 --- /dev/null +++ b/library/src/main/res/values/attrs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/java/com/naqdi/sample/MainActivity.kt b/sample/src/main/java/com/naqdi/sample/MainActivity.kt index d5172eb..db26c5f 100644 --- a/sample/src/main/java/com/naqdi/sample/MainActivity.kt +++ b/sample/src/main/java/com/naqdi/sample/MainActivity.kt @@ -2,7 +2,7 @@ package com.naqdi.sample import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.oky2abbas.library.utils.FakeGenerator +import com.naqdi.chart.utils.FakeGenerator import kotlinx.android.synthetic.main.main_activity.* class MainActivity : AppCompatActivity() { @@ -11,10 +11,22 @@ class MainActivity : AppCompatActivity() { setContentView(R.layout.main_activity) btnGenerate.setOnClickListener { - grfView1.setData( - FakeGenerator.generate(), - FakeGenerator.nameList, FakeGenerator.rangeList - ) + chainChartView.apply { + + //example + +// setLineSize(3f) +// setTextSize(13f) +// setTextColor(Color.GRAY) +// setNodeSize(8F) +// setFontFamily(Typeface.DEFAULT_BOLD) + + + setData( + FakeGenerator.generate(), + FakeGenerator.nameList, FakeGenerator.rangeList + ) + } } } } \ No newline at end of file diff --git a/sample/src/main/res/layout/main_activity.xml b/sample/src/main/res/layout/main_activity.xml index 2dd60e5..4268cc0 100644 --- a/sample/src/main/res/layout/main_activity.xml +++ b/sample/src/main/res/layout/main_activity.xml @@ -1,27 +1,29 @@ - - + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical"> + + - + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 3dff4d5..d5a8eb6 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - Chart - Generate Fake Graph + Chart + Generate Fake Random Chart \ No newline at end of file