diff --git a/build.gradle b/build.gradle index 4cbcc3b..9dc7195 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.30' + ext.kotlin_version = '1.4.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } 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 d89380a..64fc27b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Feb 04 15:28:31 IRST 2018 + #Fri Oct 30 16:00:58 IRST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/library/build.gradle b/library/build.gradle index 3968b0f..668f32f 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,18 +1,19 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' apply plugin: 'com.github.dcendents.android-maven' group = 'com.github.beigirad' android { - compileSdkVersion 28 + compileSdkVersion 30 defaultConfig { minSdkVersion 14 - targetSdkVersion 28 + targetSdkVersion 30 def majorVersion = 1 - def minorVersion = 0 - def patchVersion = 7 + def minorVersion = 1 + def patchVersion = 0 versionCode majorVersion * 10000 + minorVersion * 100 + patchVersion versionName "$majorVersion.$minorVersion.$patchVersion" @@ -29,5 +30,6 @@ android { } dependencies { - implementation 'com.android.support:appcompat-v7:28.0.0' -} + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation "androidx.core:core-ktx:1.3.2" +} \ No newline at end of file diff --git a/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.java b/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.java deleted file mode 100644 index 58b0ac8..0000000 --- a/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.java +++ /dev/null @@ -1,222 +0,0 @@ -package ir.beigirad.zigzagview; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Path; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; -import android.os.Build; -import android.renderscript.Allocation; -import android.renderscript.Element; -import android.renderscript.RenderScript; -import android.renderscript.ScriptIntrinsicBlur; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import static android.graphics.Bitmap.Config.ALPHA_8; -import static android.graphics.Color.BLACK; -import static android.graphics.Color.TRANSPARENT; -import static android.graphics.PorterDuff.Mode.SRC_IN; - -public class ZigzagView extends FrameLayout { - private static final int ZIGZAG_TOP = 1; - private static final int ZIGZAG_BOTTOM = 2; // default to be backward compatible.Like google ;) - - private int zigzagHeight; - private int zigzagElevation; - private int zigzagPaddingContent; - private int zigzagBackgroundColor; - private int zigzagPadding; - private int zigzagPaddingLeft; - private int zigzagPaddingRight; - private int zigzagPaddingTop; - private int zigzagPaddingBottom; - private int zigzagSides; - private float zigzagShadowAlpha; - - private Path pathZigzag = new Path(); - private Paint paintZigzag; - private Paint paintShadow; - - private Bitmap shadow; - - - Rect rectMain = new Rect(); - Rect rectZigzag = new Rect(); - Rect rectContent = new Rect(); - - public ZigzagView(Context context) { - super(context); - init(context, null, 0, 0); - } - - public ZigzagView(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0, 0); - } - - public ZigzagView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context, attrs, defStyleAttr, 0); - } - - @RequiresApi(api = 21) - public ZigzagView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(context, attrs, defStyleAttr, defStyleRes); - } - - - private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZigzagView, defStyleAttr, defStyleRes); - zigzagElevation = (int) a.getDimension(R.styleable.ZigzagView_zigzagElevation, 0.0f); - zigzagHeight = (int) a.getDimension(R.styleable.ZigzagView_zigzagHeight, 0.0f); - zigzagPaddingContent = (int) a.getDimension(R.styleable.ZigzagView_zigzagPaddingContent, 0.0f); - zigzagBackgroundColor = a.getColor(R.styleable.ZigzagView_zigzagBackgroundColor, Color.WHITE); - zigzagPadding = (int) a.getDimension(R.styleable.ZigzagView_zigzagPadding, zigzagElevation); - zigzagPaddingLeft = (int) a.getDimension(R.styleable.ZigzagView_zigzagPaddingLeft, zigzagPadding); - zigzagPaddingRight = (int) a.getDimension(R.styleable.ZigzagView_zigzagPaddingRight, zigzagPadding); - zigzagPaddingTop = (int) a.getDimension(R.styleable.ZigzagView_zigzagPaddingTop, zigzagPadding); - zigzagPaddingBottom = (int) a.getDimension(R.styleable.ZigzagView_zigzagPaddingBottom, zigzagPadding); - zigzagSides = a.getInt(R.styleable.ZigzagView_zigzagSides, ZIGZAG_BOTTOM); - zigzagShadowAlpha = a.getFloat(R.styleable.ZigzagView_zigzagShadowAlpha, 0.5f); - a.recycle(); - - zigzagElevation = Math.min(zigzagElevation, 25); - zigzagShadowAlpha = Math.min(zigzagShadowAlpha, 100); - - paintZigzag = new Paint(); - paintZigzag.setColor(zigzagBackgroundColor); - paintZigzag.setStyle(Style.FILL); - - paintShadow = new Paint(Paint.ANTI_ALIAS_FLAG); - paintShadow.setColorFilter(new PorterDuffColorFilter(BLACK, SRC_IN)); - paintShadow.setAlpha((int) (zigzagShadowAlpha * 100)); - - - setWillNotDraw(false); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - rectMain.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); - rectZigzag.set(rectMain.left + zigzagPaddingLeft, rectMain.top + zigzagPaddingTop, rectMain.right - zigzagPaddingRight, rectMain.bottom - zigzagPaddingBottom); - rectContent.set(rectZigzag.left + zigzagPaddingContent, - rectZigzag.top + zigzagPaddingContent + (containsSide(zigzagSides, ZIGZAG_TOP) ? zigzagHeight : 0), - rectZigzag.right - zigzagPaddingContent, - rectZigzag.bottom - zigzagPaddingContent - (containsSide(zigzagSides, ZIGZAG_BOTTOM) ? zigzagHeight : 0)); - - super.setPadding(rectContent.left, rectContent.top, rectMain.right - rectContent.right, rectMain.bottom - rectContent.bottom); - } - - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - drawZigzag(); - - if (zigzagElevation > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !isInEditMode()) { - drawShadow(); - canvas.drawBitmap(shadow, 0, 0, null); - } - - canvas.drawPath(pathZigzag, paintZigzag); - } - - private void drawZigzag() { - float left = rectZigzag.left; - float right = rectZigzag.right; - float top = rectZigzag.top; - float bottom = rectZigzag.bottom; - - pathZigzag.moveTo(right, bottom); - pathZigzag.lineTo(right, top); - - if (containsSide(zigzagSides, ZIGZAG_TOP)) - drawHorizontalSide(pathZigzag, left, top, right, true); - else - pathZigzag.lineTo(left, top); - - pathZigzag.lineTo(left, bottom); - - if (containsSide(zigzagSides, ZIGZAG_BOTTOM)) - drawHorizontalSide(pathZigzag, left, bottom, right, false); - else - pathZigzag.lineTo(right, bottom); - } - - @RequiresApi(api = Build.VERSION_CODES.KITKAT) - private void drawShadow() { - shadow = Bitmap.createBitmap(getWidth(), getHeight(), ALPHA_8); - shadow.eraseColor(TRANSPARENT); - Canvas c = new Canvas(shadow); - c.drawPath(pathZigzag, paintShadow); - - RenderScript rs = RenderScript.create(getContext()); - ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8(rs)); - Allocation input = Allocation.createFromBitmap(rs, shadow); - Allocation output = Allocation.createTyped(rs, input.getType()); - blur.setRadius(zigzagElevation); - blur.setInput(input); - blur.forEach(output); - output.copyTo(shadow); - input.destroy(); - output.destroy(); - - } - - private void drawHorizontalSide(Path path, float left, float y, float right, boolean isTop) { - int h = zigzagHeight; - int seed = 2 * h; - int width = (int) (right - left); - int count = width / seed; - int diff = width - (seed * count); - int sideDiff = diff / 2; - - float halfSeed = (float) (seed / 2); - float innerHeight = isTop ? y + h : y - h; - - if (isTop) { - for (int i = count; i > 0; i--) { - int startSeed = (i * seed) + sideDiff + (int) left; - int endSeed = startSeed - seed; - - if (i == 1) { - endSeed = endSeed - sideDiff; - } - - path.lineTo(startSeed - halfSeed, innerHeight); - path.lineTo(endSeed, y); - } - } else { - for (int i = 0; i < count; i++) { - int startSeed = (i * seed) + sideDiff + (int) left; - int endSeed = startSeed + seed; - - if (i == 0) { - startSeed = (int) left + sideDiff; - } else if (i == count - 1) { - endSeed = endSeed + sideDiff; - } - - path.lineTo(startSeed + halfSeed, innerHeight); - path.lineTo(endSeed, y); - } - } - - - } - - private boolean containsSide(int flagSet, int flag) { - return (flagSet | flag) == flagSet; - } -} \ No newline at end of file diff --git a/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.kt b/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.kt new file mode 100644 index 0000000..46bc4c5 --- /dev/null +++ b/library/src/main/java/ir/beigirad/zigzagview/ZigzagView.kt @@ -0,0 +1,195 @@ +package ir.beigirad.zigzagview + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.graphics.Rect +import android.graphics.RectF +import android.os.Build +import android.renderscript.Allocation +import android.renderscript.Element +import android.renderscript.RenderScript +import android.renderscript.ScriptIntrinsicBlur +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.annotation.ColorInt +import androidx.annotation.RequiresApi + +class ZigzagView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : FrameLayout(context, attrs, defStyleAttr) { + private var zigzagHeight = 0f + private var zigzagElevation = 0f + private var zigzagPaddingContent = 0f + var zigzagBackgroundColor = Color.WHITE + set(@ColorInt value) { + field = value + paintZigzag.color = value + invalidate() + } + private var zigzagPadding = 0f + private var zigzagPaddingLeft = 0f + private var zigzagPaddingRight = 0f + private var zigzagPaddingTop = 0f + private var zigzagPaddingBottom = 0f + private var zigzagSides = 0 + private var zigzagShadowAlpha = 0f + private val pathZigzag = Path() + private val paintZigzag by lazy { + Paint().apply { + color = Color.BLACK + style = Paint.Style.FILL + } + } + private val paintShadow by lazy { + Paint().apply { + isAntiAlias = true + colorFilter = PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN) + } + } + private var shadow: Bitmap? = null + private var rectMain = Rect() + private var rectZigzag = RectF() + private var rectContent = RectF() + + init { + context.obtainStyledAttributes(attrs, R.styleable.ZigzagView).run { + zigzagElevation = getDimension(R.styleable.ZigzagView_zigzagElevation, 0.0f) + zigzagHeight = getDimension(R.styleable.ZigzagView_zigzagHeight, 0.0f) + zigzagPaddingContent = getDimension(R.styleable.ZigzagView_zigzagPaddingContent, 0.0f) + zigzagBackgroundColor = getColor(R.styleable.ZigzagView_zigzagBackgroundColor, zigzagBackgroundColor) + zigzagPadding = getDimension(R.styleable.ZigzagView_zigzagPadding, zigzagElevation) + zigzagPaddingLeft = getDimension(R.styleable.ZigzagView_zigzagPaddingLeft, zigzagPadding) + zigzagPaddingRight = getDimension(R.styleable.ZigzagView_zigzagPaddingRight, zigzagPadding) + zigzagPaddingTop = getDimension(R.styleable.ZigzagView_zigzagPaddingTop, zigzagPadding) + zigzagPaddingBottom = getDimension(R.styleable.ZigzagView_zigzagPaddingBottom, zigzagPadding) + zigzagSides = getInt(R.styleable.ZigzagView_zigzagSides, ZIGZAG_BOTTOM) + zigzagShadowAlpha = getFloat(R.styleable.ZigzagView_zigzagShadowAlpha, 0.5f) + recycle() + } + + zigzagElevation = zigzagElevation.coerceIn(0f, 25f) + zigzagShadowAlpha = zigzagShadowAlpha.coerceIn(0f, 1f) + paintShadow.alpha = (zigzagShadowAlpha * 100).toInt() + setWillNotDraw(false) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + rectMain.set(0, 0, measuredWidth, measuredHeight) + rectZigzag.set( + rectMain.left + zigzagPaddingLeft, + rectMain.top + zigzagPaddingTop, + rectMain.right - zigzagPaddingRight, + rectMain.bottom - zigzagPaddingBottom + ) + rectContent.set( + rectZigzag.left + zigzagPaddingContent, + rectZigzag.top + zigzagPaddingContent + (if (containsSide(zigzagSides, ZIGZAG_TOP)) zigzagHeight else 0f), + rectZigzag.right - zigzagPaddingContent, + rectZigzag.bottom - zigzagPaddingContent - if (containsSide(zigzagSides, ZIGZAG_BOTTOM)) zigzagHeight else 0f + ) + super.setPadding( + rectContent.left.toInt(), + rectContent.top.toInt(), + (rectMain.right - rectContent.right).toInt(), + (rectMain.bottom - rectContent.bottom).toInt() + ) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + drawZigzag() + if (zigzagElevation > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !isInEditMode) { + drawShadow() + canvas.drawBitmap(shadow!!, 0f, 0f, null) + } + canvas.drawPath(pathZigzag, paintZigzag) + } + + private fun drawZigzag() { + val left = rectZigzag.left + val right = rectZigzag.right + val top = rectZigzag.top + val bottom = rectZigzag.bottom + pathZigzag.moveTo(right, bottom) + pathZigzag.lineTo(right, top) + if (containsSide(zigzagSides, ZIGZAG_TOP) && zigzagHeight > 0) + drawHorizontalSide(pathZigzag, left, top, right, isTop = true) + else + pathZigzag.lineTo(left, top) + pathZigzag.lineTo(left, bottom) + if (containsSide(zigzagSides, ZIGZAG_BOTTOM) && zigzagHeight > 0) + drawHorizontalSide(pathZigzag, left, bottom, right, isTop = false) + else + pathZigzag.lineTo(right, bottom) + } + + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + private fun drawShadow() { + shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8) + shadow!!.eraseColor(Color.TRANSPARENT) + val c = Canvas(shadow!!) + c.drawPath(pathZigzag, paintShadow) + val rs = RenderScript.create(context) + val blur = ScriptIntrinsicBlur.create(rs, Element.U8(rs)) + val input = Allocation.createFromBitmap(rs, shadow) + val output = Allocation.createTyped(rs, input.type) + blur.setRadius(zigzagElevation) + blur.setInput(input) + blur.forEach(output) + output.copyTo(shadow) + input.destroy() + output.destroy() + } + + private fun drawHorizontalSide(path: Path, left: Float, y: Float, right: Float, isTop: Boolean) { + val h = zigzagHeight + val seed = 2 * h + val width = right - left + val count: Int = (width / seed).toInt() + val diff = width - seed * count + val sideDiff = diff / 2 + val halfSeed = seed / 2 + val innerHeight = if (isTop) y + h else y - h + if (isTop) { + for (i in count downTo 1) { + val startSeed = i * seed + sideDiff + left.toInt() + var endSeed = startSeed - seed + if (i == 1) { + endSeed -= sideDiff + } + path.lineTo(startSeed - halfSeed, innerHeight) + path.lineTo(endSeed, y) + } + } else { + for (i in 0 until count) { + var startSeed = i * seed + sideDiff + left.toInt() + var endSeed = startSeed + seed + if (i == 0) { + startSeed = left.toInt() + sideDiff + } else if (i == count - 1) { + endSeed += sideDiff + } + path.lineTo(startSeed + halfSeed, innerHeight) + path.lineTo(endSeed, y) + } + } + } + + private fun containsSide(flagSet: Int, flag: Int): Boolean { + return flagSet or flag == flagSet + } + + companion object { + private const val ZIGZAG_TOP = 1 + private const val ZIGZAG_BOTTOM = 2 // default to be backward compatible.Like google ;) + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index f25bdb2..5987c7f 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -5,14 +5,14 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 28 + compileSdkVersion 30 defaultConfig { applicationId "ir.beigirad.zigzagview.sample" minSdkVersion 15 - targetSdkVersion 28 + targetSdkVersion 30 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -24,7 +24,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib" + implementation 'androidx.appcompat:appcompat:1.2.0' implementation project(':library') } diff --git a/sample/src/main/java/ir/beigirad/zigzagview/sample/MainActivity.kt b/sample/src/main/java/ir/beigirad/zigzagview/sample/MainActivity.kt index e111bf6..8f0adf7 100644 --- a/sample/src/main/java/ir/beigirad/zigzagview/sample/MainActivity.kt +++ b/sample/src/main/java/ir/beigirad/zigzagview/sample/MainActivity.kt @@ -1,12 +1,20 @@ package ir.beigirad.zigzagview.sample -import android.support.v7.app.AppCompatActivity +import android.graphics.Color import android.os.Bundle -import ir.beigirad.zigzagview.sample.R +import androidx.appcompat.app.AppCompatActivity +import kotlinx.android.synthetic.main.activity_main.* +import kotlin.random.Random + class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + btn_randomize.setOnClickListener { + val randomColor = Color.argb(255, Random.nextInt(256), Random.nextInt(256), Random.nextInt(256)) + zigzag2.zigzagBackgroundColor = randomColor + } } } diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index ad5da05..aebb46c 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,35 +1,33 @@ + android:gravity="center_horizontal"> + - - - - - - + android:layout_height="match_parent" + android:gravity="center" + android:text="ZigzagView" + android:textColor="#000000" + android:textSize="23sp" /> + - - - +