From 27f81d154dbb0c3dd6a6ad422671d608188fca22 Mon Sep 17 00:00:00 2001 From: lingarajsankaravelu Date: Sun, 26 Jul 2020 09:35:27 +0530 Subject: [PATCH] "1. Allow multiple anchors through single attribute (app:tv_anchors=id1,id2,id3) > 2.added attribute to enable ordisable semiCircle on Top Ticketview (app:tv_enableSemiCircleOnTop) > 3. refactored dependencies to androidx , updated target and compile sdk version to 30 and readme.md" --- .gitignore | 1 + README.md | 8 +- app/build.gradle | 14 +- .../ExampleInstrumentedTest.java | 4 +- .../mreram/sampleticketview/MainActivity.kt | 2 +- app/src/main/res/layout/activity_main.xml | 18 +-- app/src/main/res/layout/activity_ticket.xml | 22 ++-- build.gradle | 6 +- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- ticketview/build.gradle | 12 +- .../ticketview/ExampleInstrumentedTest.java | 4 +- .../java/com/mreram/ticketview/TicketView.kt | 120 +++++++++--------- ticketview/src/main/res/values/attrs.xml | 5 +- 14 files changed, 112 insertions(+), 110 deletions(-) diff --git a/.gitignore b/.gitignore index 39fb081..166f30b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.iml +.idea/ .gradle /local.properties /.idea/workspace.xml diff --git a/README.md b/README.md index 1ed93db..2de9a80 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ - + app:tv_anchors="cl_head,cl_center" + app:tv_dashColor="@color/colorAccent" + app:tv_enableSemiCircleOnTop="true"> - - + - - + + android:padding="10dp"> - - + - + - - + - + diff --git a/build.gradle b/build.gradle index 8e533d9..72643df 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.2.21' + ext.kotlin_version = '1.3.72' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:4.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' // NOTE: Do not place your application dependencies here; they belong @@ -29,7 +29,7 @@ repositories { mavenCentral() } dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } compileKotlin { kotlinOptions { diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4179a32..06a4aad 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Mar 01 16:57:28 IRST 2018 +#Sat Jul 25 21:49:05 IST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/ticketview/build.gradle b/ticketview/build.gradle index ade1435..b6ea6d6 100644 --- a/ticketview/build.gradle +++ b/ticketview/build.gradle @@ -3,18 +3,18 @@ apply plugin: 'kotlin-android' apply plugin: 'com.github.dcendents.android-maven' group='com.github.mreram' android { - compileSdkVersion 26 + compileSdkVersion 30 defaultConfig { minSdkVersion 14 - targetSdkVersion 26 + targetSdkVersion 30 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -30,8 +30,8 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:0.5' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2' - compile 'org.jetbrains.kotlin:kotlin-stdlib:1.2.21' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' + implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72' } diff --git a/ticketview/src/androidTest/java/com/mreram/ticketview/ExampleInstrumentedTest.java b/ticketview/src/androidTest/java/com/mreram/ticketview/ExampleInstrumentedTest.java index b5fee84..92499b5 100644 --- a/ticketview/src/androidTest/java/com/mreram/ticketview/ExampleInstrumentedTest.java +++ b/ticketview/src/androidTest/java/com/mreram/ticketview/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package com.mreram.ticketview; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/ticketview/src/main/java/com/mreram/ticketview/TicketView.kt b/ticketview/src/main/java/com/mreram/ticketview/TicketView.kt index 54454ee..f4ac91c 100644 --- a/ticketview/src/main/java/com/mreram/ticketview/TicketView.kt +++ b/ticketview/src/main/java/com/mreram/ticketview/TicketView.kt @@ -14,44 +14,37 @@ import android.widget.RelativeLayout import android.graphics.PorterDuffXfermode - /** * Created by Mohammad Reza Eram on 2/23/18. */ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr) { - + private var mContext:Context = context; companion object { private val DEFAULT_RADIUS: Float = 9f - private val NO_VALUE = -1 } private val eraser = Paint(Paint.ANTI_ALIAS_FLAG) - - private var anchorViewId1: Int = 0 - private var anchorViewId2: Int = 0 - private var circlesPath = Path() - private var circlePosition1: Float = 0f - private var circlePosition2: Float = 0f private var circleRadius: Float = 0f private var circleSpace: Float = 0f - + private var enableSemiCircleOnTop:Boolean; + private var anchorIdsString:String?= null + private var circlePositions = mutableListOf() private var dashColor: Int = 0 private var dashSize: Float = 0f private val dashPath = Path() private val dashPaint = Paint(Paint.ANTI_ALIAS_FLAG) init { - setLayerType(View.LAYER_TYPE_HARDWARE, null) val a = context.obtainStyledAttributes(attrs, R.styleable.TicketView) try { + enableSemiCircleOnTop = a.getBoolean(R.styleable.TicketView_tv_enableSemiCircleOnTop,true) circleRadius = a.getDimension(R.styleable.TicketView_tv_circleRadius, getDp(DEFAULT_RADIUS).toFloat()) - anchorViewId1 = a.getResourceId(R.styleable.TicketView_tv_anchor1, NO_VALUE) - anchorViewId2 = a.getResourceId(R.styleable.TicketView_tv_anchor2, NO_VALUE) + anchorIdsString = a.getString(R.styleable.TicketView_tv_anchors) circleSpace = a.getDimension(R.styleable.TicketView_tv_circleSpace, getDp(15f).toFloat()) dashColor = a.getColor(R.styleable.TicketView_tv_dashColor, Color.parseColor("#0085be")) dashSize = a.getDimension(R.styleable.TicketView_tv_dashSize, getDp(1.5f).toFloat()) @@ -66,26 +59,37 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet dashPaint.strokeWidth = dashSize dashPaint.pathEffect = DashPathEffect(floatArrayOf(getDp(3f).toFloat(), getDp(3f).toFloat()), 0f) } + // parse views matching the names provided in the user applications under app:tv_anchors + private fun parseViews() { + val rect = Rect() + anchorIdsString!!.split(",").iterator().forEach { + val view = this.findViewById(getResources().getIdentifier(it, "id", this.mContext.packageName)) + addCirclePosition(view, rect) + + } + } + fun setRadius(radius: Float) { this.circleRadius = radius postInvalidate() } - fun setAnchor(view1: View?, view2: View?) { - - val rect = Rect() - view1?.getDrawingRect(rect) - offsetDescendantRectToMyCoords(view1, rect) - circlePosition1 = rect.bottom.toFloat() + fun setAnchor() { + parseViews() + postInvalidate() + } - if (view2 != null) { - view2.getDrawingRect(rect) - offsetDescendantRectToMyCoords(view2, rect) - circlePosition2 = rect.bottom.toFloat() + // adds circlePosition to a list. + private fun addCirclePosition(view: View?, rect: Rect) { + if (view == null) { + return } + view.getDrawingRect(rect) + offsetDescendantRectToMyCoords(view, rect) + circlePositions.add(rect.bottom.toFloat()); + - postInvalidate() } override fun drawChild(canvas: Canvas?, child: View?, drawingTime: Long): Boolean { @@ -102,9 +106,6 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet override fun onFinishInflate() { super.onFinishInflate() - if (anchorViewId1 != NO_VALUE || anchorViewId2 != NO_VALUE) { - val anchorView1 = findViewById(anchorViewId1) - val anchorView2 = findViewById(anchorViewId2) viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { override fun onGlobalLayout() { if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { @@ -112,10 +113,10 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet } else { viewTreeObserver.removeGlobalOnLayoutListener(this) } - setAnchor(anchorView1, anchorView2) + setAnchor() } }) - } + } override fun onDraw(canvas: Canvas) { @@ -124,7 +125,7 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet } private fun drawHoles(canvas: Canvas) { - circlesPath = Path() + this.circlesPath = Path() val w = width val radius = circleRadius val space = circleSpace @@ -143,42 +144,18 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet val sideOffset = offset / 2 val halfCircleSpace = circleSpace / 2 - for (i in 0 until count) { - var positionCircle = i * circleSpace + sideOffset + left.toFloat() - radius - if (i == 0) { - positionCircle = left + sideOffset - radius - } - this.circlesPath.addCircle(positionCircle + halfCircleSpace, -circleRadius / 4, radius, Path.Direction.CW) - } - // add holes on the ticketView by erasing them - with(circlesPath) { - //anchor1 - addCircle(-circleRadius / 4, circlePosition1, circleRadius, Path.Direction.CW) // bottom left hole - addCircle(w + circleRadius / 4, circlePosition1, circleRadius, Path.Direction.CW)// bottom right hole - - //anchor2 - when { - anchorViewId2 != NO_VALUE -> { - addCircle(-circleRadius / 4, circlePosition2, circleRadius, Path.Direction.CW) // bottom left hole - addCircle(w + circleRadius / 4, circlePosition2, circleRadius, Path.Direction.CW) // bottom right hole + if (enableSemiCircleOnTop){ + for (i in 0 until count) { + var positionCircle = i * circleSpace + sideOffset + left.toFloat() - radius + if (i == 0) { + positionCircle = left + sideOffset - radius } + this.circlesPath.addCircle(positionCircle + halfCircleSpace, -circleRadius / 4, radius, Path.Direction.CW) } - } - with(dashPath) { - //anchor1 - moveTo(circleRadius, circlePosition1) - quadTo(w - circleRadius, circlePosition1, w - circleRadius, circlePosition1) - - //anchor2 - when { - anchorViewId2 != NO_VALUE -> { - moveTo(circleRadius, circlePosition2) - quadTo(w - circleRadius, circlePosition2, w - circleRadius, circlePosition2) - } - } } + createCircleAndDashPath(w) with(canvas) { if (dashSize > 0) @@ -187,6 +164,27 @@ class TicketView @JvmOverloads constructor(context: Context, attrs: AttributeSet } } + private fun createCircleAndDashPath(width: Int) { + + circlePositions.iterator().forEach { + with(circlesPath) { + // add path for the holes on the ticketView by erasing them at an anchor + addCircle(-circleRadius / 4, it, circleRadius, Path.Direction.CW) // bottom left hole + addCircle(width + circleRadius / 4, it, circleRadius, Path.Direction.CW)// bottom right hole + } + + with(dashPath) { + //adds path for the dash connecting the holes on left and right of an anchor . + moveTo(circleRadius, it) + quadTo(width - circleRadius, it, width - circleRadius, it) + + } + + } + + } + + private fun getDp(value: Float): Int { return when (value) { diff --git a/ticketview/src/main/res/values/attrs.xml b/ticketview/src/main/res/values/attrs.xml index 3cfae89..38a3570 100644 --- a/ticketview/src/main/res/values/attrs.xml +++ b/ticketview/src/main/res/values/attrs.xml @@ -2,10 +2,9 @@ - - - + +