diff --git a/app/build.gradle b/app/build.gradle index e925a11..36faca2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'kotlin-android-extensions' ext { PUBLISH_GROUP_ID = 'com.poovam' PUBLISH_ARTIFACT_ID = 'pin-edittext-field' - PUBLISH_VERSION = '1.0.9' + PUBLISH_VERSION = '1.1.0' } android { diff --git a/app/src/main/java/com/poovam/pinedittextfield/CirclePinField.kt b/app/src/main/java/com/poovam/pinedittextfield/CirclePinField.kt index 8094378..ed7a5ff 100644 --- a/app/src/main/java/com/poovam/pinedittextfield/CirclePinField.kt +++ b/app/src/main/java/com/poovam/pinedittextfield/CirclePinField.kt @@ -5,6 +5,8 @@ import android.graphics.Canvas import android.graphics.Paint import android.support.v4.content.ContextCompat import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup /** * Created by poovam-5255 on 5/23/2018. @@ -14,7 +16,7 @@ import android.util.AttributeSet */ class CirclePinField: PinField{ - var fillerColor = ContextCompat.getColor(context,R.color.accent) + var fillerColor = ContextCompat.getColor(context,R.color.pinFieldLibraryAccent) set(value){ field = value fillerPaint.color = fillerColor @@ -46,6 +48,7 @@ class CirclePinField: PinField{ try { circleRadiusDp = a.getDimension(R.styleable.CirclePinField_circleRadius, circleRadiusDp) fillerColor = a.getColor(R.styleable.CirclePinField_fillerColor, fillerColor) + if(distanceInBetween == DEFAULT_DISTANCE_IN_BETWEEN) distanceInBetween = Util.dpToPx(30f) } finally { a.recycle() } @@ -55,36 +58,80 @@ class CirclePinField: PinField{ fillerPaint.strokeWidth = circleRadiusDp } - override fun getDefaultDistanceInBetween(): Float{ - return (singleFieldWidth - (circleRadiusDp*2))*2 + override fun getViewWidth(desiredWidth:Int, widthMeasureSpec: Int): Int { + + val widthMode = View.MeasureSpec.getMode(widthMeasureSpec) + val widthSize = View.MeasureSpec.getSize(widthMeasureSpec) + val viewWidth = getCircleDiameterWithPadding() * numberOfFields + //Measure Width + return when (widthMode) { + MeasureSpec.EXACTLY -> widthSize + MeasureSpec.AT_MOST -> Math.min(viewWidth, widthSize) + MeasureSpec.UNSPECIFIED -> viewWidth + else -> viewWidth + } + } + + private fun getCircleDiameterWithPadding():Int{ + val diameter = circleRadiusDp*2 + return Math.round(diameter + getThickness() + getPadding()) + } + + /** + * This will return thickness based on the chosen mode + * there will be a small padding even if distanceInBetween = 0dp if there is a highlight + * this is to make sure when highlighting there is no overlap + */ + private fun getThickness(): Int{ + return Math.round(if(isHighlightEnabled)highLightThickness else lineThickness) + } + + private fun getPadding(): Float{ + return if (distanceInBetween!= DEFAULT_DISTANCE_IN_BETWEEN) distanceInBetween else getDefaultDistanceInBetween()/2 } override fun onDraw(canvas: Canvas?) { for (i in 0 until numberOfFields){ + var startingX = (width - (getCircleDiameterWithPadding()*numberOfFields))/2 - val padding = (if (distanceInBetween!= defDistanceInBetweenValue) distanceInBetween else getDefaultDistanceInBetween())/2 + startingX = if(startingX > 0) startingX else 0 - val x1 = (padding+(circleRadiusDp*2))*i + val x1 = (getCircleDiameterWithPadding().toFloat() * i) + getCircleDiameterWithPadding()/2 + startingX val character:Char? = text?.getOrNull(i) + val y1 = getViewHeight(height) + if(isHighlightEnabled && !highlightSingleFieldMode && hasFocus()){ - canvas?.drawCircle(x1+(singleFieldWidth/2).toFloat(),(height/2).toFloat(),circleRadiusDp, highlightPaint) + canvas?.drawCircle(x1,y1,circleRadiusDp, highlightPaint) }else{ - canvas?.drawCircle(x1+(singleFieldWidth/2).toFloat(),(height/2).toFloat(),circleRadiusDp, fieldPaint) + canvas?.drawCircle(x1,y1,circleRadiusDp, fieldPaint) } if(character!=null) { - canvas?.drawCircle(x1+(singleFieldWidth/2).toFloat(),(height/2).toFloat(),(circleRadiusDp/2)-highLightThickness, fillerPaint) + canvas?.drawCircle(x1,y1,(circleRadiusDp/2)-highLightThickness, fillerPaint) } if(hasFocus() && i == text?.length ?: 0){ if(isHighlightEnabled && highlightSingleFieldMode){ - canvas?.drawCircle(x1+(singleFieldWidth/2).toFloat(),(height/2).toFloat(),circleRadiusDp, highlightPaint) + canvas?.drawCircle(x1,y1,circleRadiusDp, highlightPaint) } } } } + /** + * This is a dirty hack since the height provided by Android framework seems same + * but drawing it on canvas seems to draw out of frame + * Need to look into this more + */ + private fun getViewHeight(height: Int): Float{ + if(!isCustomBackground && this.layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT + && getPadding() < circleRadiusDp){ + return (height - getPadding()*1.5).toFloat() + } + return height/2.toFloat() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/poovam/pinedittextfield/LinePinField.kt b/app/src/main/java/com/poovam/pinedittextfield/LinePinField.kt index 8505d5d..76a04c8 100644 --- a/app/src/main/java/com/poovam/pinedittextfield/LinePinField.kt +++ b/app/src/main/java/com/poovam/pinedittextfield/LinePinField.kt @@ -15,24 +15,40 @@ class LinePinField : PinField { private val cursorBottomPadding = Util.dpToPx(2f) + var bottomTextPaddingDp = 0f + + constructor(context: Context): super(context) - constructor(context: Context, attr: AttributeSet) : super(context,attr) + constructor(context: Context, attr: AttributeSet) : super(context,attr){ + initParams(attr) + } - constructor(context: Context, attr: AttributeSet, defStyle: Int) : super(context,attr,defStyle) + constructor(context: Context,attr: AttributeSet,defStyle: Int) : super(context,attr,defStyle){ + initParams(attr) + } + + private fun initParams(attr: AttributeSet){ + val a = context.theme.obtainStyledAttributes(attr, R.styleable.LinePinField, 0,0) + try { + bottomTextPaddingDp = a.getDimension(R.styleable.LinePinField_bottomTextPaddingDp,bottomTextPaddingDp) + } finally { + a.recycle() + } + } override fun onDraw(canvas: Canvas?) { for (i in 0 until numberOfFields){ val x1 = (i*singleFieldWidth) - val padding = (if (distanceInBetween!= defDistanceInBetweenValue) distanceInBetween else getDefaultDistanceInBetween())/2 + val padding = (if (distanceInBetween!= DEFAULT_DISTANCE_IN_BETWEEN) distanceInBetween else getDefaultDistanceInBetween())/2 val paddedX1 = (x1 + padding) val paddedX2 = ((x1+singleFieldWidth)-padding) val paddedY1 = height - yPadding val textX = ((paddedX2-paddedX1)/2)+paddedX1 - val textY = (paddedY1- lineThickness)-(textPaint.textSize/4) - val character:Char? = text?.getOrNull(i) + val textY = (paddedY1- lineThickness)-(textPaint.textSize/4)-bottomTextPaddingDp + val character:Char? = transformationMethod?.getTransformation(text,this)?.getOrNull(i) ?: text.getOrNull(i) if(isHighlightEnabled && !highlightSingleFieldMode && hasFocus()){ canvas?.drawLine(paddedX1,paddedY1,paddedX2,paddedY1, highlightPaint) @@ -46,7 +62,7 @@ class LinePinField : PinField { if(hasFocus() && i == text?.length ?: 0){ if(isCursorEnabled){ - val cursorY1 = paddedY1 - cursorBottomPadding - highLightThickness + val cursorY1 = paddedY1 - cursorBottomPadding - highLightThickness - bottomTextPaddingDp val cursorY2 = cursorTopPadding drawCursor(canvas,textX ,cursorY1,cursorY2,highlightPaint) } diff --git a/app/src/main/java/com/poovam/pinedittextfield/PinField.kt b/app/src/main/java/com/poovam/pinedittextfield/PinField.kt index 5241b57..0d384e1 100644 --- a/app/src/main/java/com/poovam/pinedittextfield/PinField.kt +++ b/app/src/main/java/com/poovam/pinedittextfield/PinField.kt @@ -20,9 +20,11 @@ open class PinField : AppCompatEditText { private val defaultWidth = Util.dpToPx(60f).toInt() - protected val defDistanceInBetweenValue = -1f + companion object { + val DEFAULT_DISTANCE_IN_BETWEEN = -1f + } - protected var distanceInBetween:Float = defDistanceInBetweenValue + protected var distanceInBetween:Float = DEFAULT_DISTANCE_IN_BETWEEN set(value) { field = value requestLayout() @@ -53,7 +55,7 @@ open class PinField : AppCompatEditText { invalidate() } - var highlightPaintColor = ContextCompat.getColor(context,R.color.accent) + var highlightPaintColor = ContextCompat.getColor(context,R.color.pinFieldLibraryAccent) set(value){ field = value highlightPaint.color = field @@ -87,7 +89,7 @@ open class PinField : AppCompatEditText { var isCustomBackground = false set(value) { if(!value){ - setBackgroundResource(R.color.transparent) + setBackgroundResource(R.color.pinFieldLibraryTransparent) } field = value } @@ -136,7 +138,7 @@ open class PinField : AppCompatEditText { try { numberOfFields = a.getInt(R.styleable.PinField_noOfFields, numberOfFields) lineThickness = a.getDimension(R.styleable.PinField_lineThickness, lineThickness) - distanceInBetween = a.getDimension(R.styleable.PinField_distanceInBetween, defDistanceInBetweenValue) + distanceInBetween = a.getDimension(R.styleable.PinField_distanceInBetween, DEFAULT_DISTANCE_IN_BETWEEN) fieldColor = a.getColor(R.styleable.PinField_fieldColor,fieldColor) highlightPaintColor = a.getColor(R.styleable.PinField_highlightColor,highlightPaintColor) isHighlightEnabled = a.getBoolean(R.styleable.PinField_highlightEnabled,isHighlightEnabled) @@ -149,36 +151,35 @@ open class PinField : AppCompatEditText { } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width = getViewWidth(defaultWidth * numberOfFields,widthMeasureSpec) + singleFieldWidth = width/numberOfFields + setMeasuredDimension(width, getViewHeight(singleFieldWidth,heightMeasureSpec)) + } - val desiredWidth = (defaultWidth * numberOfFields) + open protected fun getViewWidth(desiredWidth:Int, widthMeasureSpec: Int): Int{ val widthMode = View.MeasureSpec.getMode(widthMeasureSpec) val widthSize = View.MeasureSpec.getSize(widthMeasureSpec) - val width: Int //Measure Width - width = when (widthMode) { - View.MeasureSpec.EXACTLY -> widthSize - View.MeasureSpec.AT_MOST -> Math.min(desiredWidth, widthSize) - View.MeasureSpec.UNSPECIFIED -> desiredWidth + return when (widthMode) { + MeasureSpec.EXACTLY -> widthSize + MeasureSpec.AT_MOST -> Math.min(desiredWidth, widthSize) + MeasureSpec.UNSPECIFIED -> desiredWidth else -> desiredWidth } - singleFieldWidth = width/numberOfFields - + } - val desiredHeight = singleFieldWidth + open protected fun getViewHeight(desiredHeight: Int, heightMeasureSpec: Int): Int{ val heightMode = View.MeasureSpec.getMode(heightMeasureSpec) val heightSize = View.MeasureSpec.getSize(heightMeasureSpec) - val height: Int //Measure Height - height = when (heightMode) { + return when (heightMode) { View.MeasureSpec.EXACTLY -> heightSize View.MeasureSpec.AT_MOST -> Math.min(desiredHeight, heightSize) View.MeasureSpec.UNSPECIFIED -> desiredHeight else -> desiredHeight } - - setMeasuredDimension(width, height) } override fun onSelectionChanged(selStart: Int, selEnd: Int) { @@ -232,6 +233,9 @@ open class PinField : AppCompatEditText { } interface OnTextCompleteListener { + /** + * @return return true if keyboard should be closed after text is entered + */ fun onTextComplete(enteredText: String): Boolean } } \ No newline at end of file diff --git a/app/src/main/java/com/poovam/pinedittextfield/SquarePinField.kt b/app/src/main/java/com/poovam/pinedittextfield/SquarePinField.kt index a9025f4..95ce93b 100644 --- a/app/src/main/java/com/poovam/pinedittextfield/SquarePinField.kt +++ b/app/src/main/java/com/poovam/pinedittextfield/SquarePinField.kt @@ -2,6 +2,8 @@ package com.poovam.pinedittextfield import android.content.Context import android.graphics.Canvas +import android.graphics.Paint +import android.os.Build import android.util.AttributeSet /** @@ -12,21 +14,39 @@ import android.util.AttributeSet class SquarePinField : PinField{ + private var cornerRadius = 0f + set(value) { + field = value + invalidate() + } + private val cursorPadding = Util.dpToPx(5f) constructor(context: Context): super(context) - constructor(context: Context, attr: AttributeSet) : super(context,attr) + constructor(context: Context, attr: AttributeSet) : super(context,attr){ + initParams(attr) + } - constructor(context: Context, attr: AttributeSet, defStyle: Int) : super(context,attr,defStyle) + constructor(context: Context, attr: AttributeSet, defStyle: Int) : super(context,attr,defStyle){ + initParams(attr) + } + private fun initParams(attr: AttributeSet){ + val a = context.theme.obtainStyledAttributes(attr, R.styleable.SquarePinField, 0,0) + try { + cornerRadius = a.getDimension(R.styleable.SquarePinField_cornerRadius, cornerRadius) + } finally { + a.recycle() + } + } override fun onDraw(canvas: Canvas?) { for (i in 0 until numberOfFields){ val x1 = (i*singleFieldWidth) - val padding = (if (distanceInBetween!= defDistanceInBetweenValue) distanceInBetween else getDefaultDistanceInBetween())/2 + val padding = (if (distanceInBetween!= DEFAULT_DISTANCE_IN_BETWEEN) distanceInBetween else getDefaultDistanceInBetween())/2 val paddedX1 = (x1 + padding) val paddedX2 = ((x1+singleFieldWidth)-padding) val squareHeight = paddedX2-paddedX1 @@ -34,12 +54,12 @@ class SquarePinField : PinField{ val paddedY2 = (height/2)+(squareHeight/2) val textX = ((paddedX2-paddedX1)/2)+paddedX1 val textY = ((paddedY2-paddedY1)/2+paddedY1)+ lineThickness +(textPaint.textSize/4) - val character:Char? = text?.getOrNull(i) + val character:Char? = transformationMethod?.getTransformation(text,this)?.getOrNull(i) ?: text.getOrNull(i) if(isHighlightEnabled && !highlightSingleFieldMode && hasFocus()){ - canvas?.drawRect(paddedX1,paddedY1,paddedX2,paddedY2, highlightPaint) + drawRect(canvas,paddedX1,paddedY1,paddedX2,paddedY2, highlightPaint) }else{ - canvas?.drawRect(paddedX1,paddedY1,paddedX2,paddedY2, fieldPaint) + drawRect(canvas,paddedX1,paddedY1,paddedX2,paddedY2, fieldPaint) } if(character!=null) { @@ -54,9 +74,17 @@ class SquarePinField : PinField{ drawCursor(canvas,textX,cursorY1,cursorY2,highlightPaint) } if(isHighlightEnabled && highlightSingleFieldMode){ - canvas?.drawRect(paddedX1,paddedY1,paddedX2,paddedY2, highlightPaint) + drawRect(canvas,paddedX1,paddedY1,paddedX2,paddedY2, highlightPaint) } } } } + + private fun drawRect(canvas: Canvas?,paddedX1:Float,paddedY1:Float,paddedX2:Float,paddedY2:Float,paint: Paint){ + if(cornerRadius>0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + canvas?.drawRoundRect(paddedX1,paddedY1,paddedX2,paddedY2,cornerRadius,cornerRadius, paint) + }else{ + canvas?.drawRect(paddedX1,paddedY1,paddedX2,paddedY2, paint) + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 61f06a6..3808b3b 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -12,6 +12,14 @@ + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 02f0ac6..1a56559 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,6 @@ - #FF4081 + #FF4081 #808080 - #00000000 + #00000000 diff --git a/sample/build.gradle b/sample/build.gradle index 339d606..9404fff 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -34,5 +34,6 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - compile 'com.poovam:pin-edittext-field:1.0.9' + compile 'com.poovam:pin-edittext-field:1.1.0' + //compile project(':app') //for library development purpose please ignore this line } diff --git a/sample/src/main/res/layout/activity_sample.xml b/sample/src/main/res/layout/activity_sample.xml index 1f357e9..25b05d3 100644 --- a/sample/src/main/res/layout/activity_sample.xml +++ b/sample/src/main/res/layout/activity_sample.xml @@ -25,6 +25,7 @@ app:isCursorEnabled="true" app:highlightSingleFieldMode="true" android:id="@+id/squareField" + app:cornerRadius="10dp" android:layout_marginTop="15dp"/>