{"id":1507,"date":"2021-09-22T16:13:09","date_gmt":"2021-09-22T08:13:09","guid":{"rendered":"https:\/\/badgameshow.com\/fly\/?p=1507"},"modified":"2021-09-23T13:33:52","modified_gmt":"2021-09-23T05:33:52","slug":"android-captcha-%e9%98%b2%e6%ad%a2%e6%a9%9f%e5%99%a8%e4%ba%ba-%e7%94%9f%e6%88%90%e9%9a%a8%e6%a9%9f%e9%a9%97%e8%ad%89%e7%a2%bc","status":"publish","type":"post","link":"https:\/\/badgameshow.com\/fly\/android-captcha-%e9%98%b2%e6%ad%a2%e6%a9%9f%e5%99%a8%e4%ba%ba-%e7%94%9f%e6%88%90%e9%9a%a8%e6%a9%9f%e9%a9%97%e8%ad%89%e7%a2%bc\/","title":{"rendered":"Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8\u6a5f\u9a57\u8b49\u78bc"},"content":{"rendered":"<h1>Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8\u6a5f\u9a57\u8b49\u78bc<\/h1>\n<h5>Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8\u6a5f\u9a57\u8b49\u78bc\uff0c\u9019\u500b\u96a8\u6a5f\u9a57\u8b49\u78bc\u53ef\u4ee5\u8f15\u6613\u7684\u64cb\u6389\u90a3\u4e9b\u6a5f\u5668\u4eba\u4f86\u505a\u4e00\u4e9b\u4e8b\u60c5\uff0c\u6b64\u65b9\u6cd5\u662f\u4f7f\u75280~9\u548cA~Z\u7684\u6240\u6709\u7d44\u5408\uff0c\u5979\u53ef\u4ee5\u6709\u6548\u7684\u9632\u6b62\u6a5f\u5668\u4eba\u653b\u64ca\u4f60\u7684\u4f3a\u670d\u5668\uff0c\u96a8\u6a5f\u9a57\u8b49\u78bc\u662f\u4f60\u7684\u597d\u5e6b\u624b\u4f86\u963b\u6b62\u4f3a\u670d\u5668\u88ab\u6a5f\u5668\u4eba\u653b\u7834\u3002<\/h5>\n<hr \/>\n<h4>\u6587\u7ae0\u76ee\u9304<\/h4>\n<ol>\n<li><a href=\"#a\">\u5275\u5efa\u9a57\u8b49\u78bc\u985e\u5225<\/a><\/li>\n<li><a href=\"#b\">\u756b\u9762\u5e03\u5c40<\/a><\/li>\n<li><a href=\"#c\">\u4f7f\u7528\u9a57\u8b49\u78bc<\/a><\/li>\n<li><a href=\"#d\">\u7a0b\u5f0f\u7bc4\u4f8b<\/a><\/li>\n<li><a href=\"#e\">\u6548\u679c\u5c55\u793a<\/a><\/li>\n<li><a href=\"#f\">GitHub<\/a><\/li>\n<\/ol>\n<hr \/>\n<p><a id=\"a\"><\/a><\/p>\n<h4>1.\u5275\u5efa\u9a57\u8b49\u78bc<\/h4>\n<pre><code class=\"language-Kotlin line-numbers\">package com.example.test\n\nimport android.graphics.*\nimport java.lang.StringBuilder\nimport java.util.*\n\n\/**\n * Author: Wade\n * E-mail: tony91097@gmail.com\n * Date: 2021\/9\/22\n *\/\n\nclass Captcha private constructor() {\n    companion object {\n        val CHARS_NUMBER = arrayListOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')\n        val CHARS_LETTER = arrayListOf(\n            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',\n            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\n            'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',\n            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\n            'X', 'Y', 'Z'\n        )\n        val CHARS_ALL = arrayListOf(\n            '0', '1', '2', '3', '4', '5', '6',\n            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',\n            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\n            'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',\n            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\n            'X', 'Y', 'Z'\n        )\n\n        val instance: Captcha by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Captcha() }\n    }\n\n    \/**\n     * \u9a57\u8b49\u78bc\u5bec\u5ea6\n     *\/\n    private var mWidth = 0\n\n    \/**\n     * \u9a57\u8b49\u78bc\u9ad8\u5ea6\n     *\/\n    private var mHeight = 0\n\n    \/**\n     * \u9a57\u8b49\u78bc\u80cc\u666f\u984f\u8272\n     *\/\n    private var mBackgroundColor = 0\n\n    \/**\n     * \u9a57\u8b49\u78bc\u9577\u5ea6\n     *\/\n    private var mLength = 0\n\n    \/**\n     * \u5e72\u64fe\u7dda\u7684\u6578\u91cf\n     *\/\n    private var mLineNumber = 0\n\n    \/**\n     * \u9a57\u8b49\u78bc\u6587\u5b57\u5927\u5c0f\n     *\/\n    private var mFontSize = 0\n\n    \/**\n     * \u5b57\u9ad4\u5de6\u908a\u8ddd\n     *\/\n    private var mFontPaddingLeft = 0\n\n    \/**\n     * \u5b57\u9ad4\u5de6\u908a\u8ddd\u7bc4\u570d\u503c\n     *\/\n    private var mFontPaddingLeftRange = 0\n\n    \/**\n     * \u5b57\u9ad4\u4e0a\u908a\u8ddd\n     *\/\n    private var mFontPaddingTop = 0\n\n    \/**\n     * \u5b57\u9ad4\u4e0a\u908a\u8ddd\u7bc4\u570d\u503c\n     *\/\n    private var mFontPaddingTopRange = 0\n\n    \/**\n     * \u5716\u7247\u500d\u7387\n     *\/\n    private var mScale = 0\n\n    private var mType: TYPE\n    private var mRandom: Random\n    private lateinit var mCode: String\n\n    sealed class TYPE {\n        object NUMBER : TYPE()\n        object LETTER : TYPE()\n        object CHARS : TYPE()\n    }\n\n    init {\n        mType = TYPE.CHARS\n        mScale = 1\n        mWidth = 200\n        mHeight = 100\n        mBackgroundColor = Color.WHITE\n        mLength = 4\n        mLineNumber = 5\n        mFontSize = 50\n        mFontPaddingLeft = 25\n        mFontPaddingLeftRange = 20\n        mFontPaddingTop = 60\n        mFontPaddingTopRange = 20\n        mRandom = Random()\n    }\n\n    fun setType(type: TYPE): Captcha {\n        mType = type\n        return instance\n    }\n\n    fun setScale(scale: Int): Captcha {\n        mScale = scale\n        return instance\n    }\n\n    fun setSize(width: Int, height: Int): Captcha {\n        mWidth = width * mScale\n        mHeight = height * mScale\n        return instance\n    }\n\n    fun setBackgroundColor(color: Int): Captcha {\n        mBackgroundColor = color\n        return instance\n    }\n\n    fun setLength(length: Int): Captcha {\n        mLength = length\n        return instance\n    }\n\n    fun setLineNumber(number: Int): Captcha {\n        mLineNumber = number\n        return instance\n    }\n\n    fun setFontSize(size: Int): Captcha {\n        mFontSize = size * mScale\n        return instance\n    }\n\n    fun setFontPadding(paddingLeft: Int, paddingTop: Int): Captcha {\n        mFontPaddingLeft = paddingLeft * mScale\n        mFontPaddingTop = paddingTop * mScale\n        return instance\n    }\n\n    fun setFontPadding(\n        paddingLeft: Int,\n        paddingLeftRange: Int,\n        paddingTop: Int,\n        paddingRange: Int\n    ): Captcha {\n        mFontPaddingLeft = paddingLeft * mScale\n        mFontPaddingLeftRange = paddingLeftRange * mScale\n        mFontPaddingTop = paddingTop * mScale\n        mFontPaddingTopRange = paddingRange * mScale\n        return instance\n    }\n\n    \/**\n     * \u7372\u53d6\u9a57\u8b49\u78bc\n     *\/\n    fun getCode() = mCode\n\n    fun create(): Bitmap {\n        mCode = createCode()\n\n        val bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888)\n        val canvas = Canvas(bitmap)\n        canvas.drawColor(mBackgroundColor)\n        val paint = Paint(Paint.ANTI_ALIAS_FLAG)\n        paint.textSize = mFontSize.toFloat()\n\n        var fontPaddingLeft = 0\n        for (code in mCode) {\n            getRandomTextStyle(paint)\n            fontPaddingLeft += getRandomFontPaddingLeft()\n            canvas.drawText(\n                code.toString(),\n                fontPaddingLeft.toFloat(),\n                getRandomFontPaddingTop().toFloat(),\n                paint\n            )\n        }\n\n        for (i in 0 until mLineNumber) {\n            drawLine(canvas, paint)\n        }\n\n        canvas.save()\n        canvas.restore()\n        return bitmap\n    }\n\n    private fun drawLine(canvas: Canvas, paint: Paint) {\n        val color = getRandomColor()\n        val startX = mRandom.nextInt(mWidth)\n        val startY = mRandom.nextInt(mHeight)\n        val stopX = mRandom.nextInt(mWidth)\n        val stopY = mRandom.nextInt(mHeight)\n        paint.strokeWidth = 1f\n        paint.color = color\n        canvas.drawLine(startX.toFloat(), startY.toFloat(), stopX.toFloat(), stopY.toFloat(), paint)\n    }\n\n    private fun getRandomFontPaddingLeft() =\n        mFontPaddingLeft + mRandom.nextInt(mFontPaddingLeftRange)\n\n    private fun getRandomFontPaddingTop() = mFontPaddingTop + mRandom.nextInt(mFontPaddingTopRange)\n\n    private fun getRandomTextStyle(paint: Paint) {\n        val color = getRandomColor()\n        paint.color = color\n        paint.isFakeBoldText = mRandom.nextBoolean()\n        var skewX = mRandom.nextInt(11) \/ 10\n        skewX = if (mRandom.nextBoolean()) skewX else -skewX\n        paint.textSkewX = skewX.toFloat()\n    }\n\n    private fun getRandomColor(): Int {\n        val red = mRandom.nextInt(256)\n        val green = mRandom.nextInt(256)\n        val blue = mRandom.nextInt(256)\n        return Color.rgb(red, green, blue)\n    }\n\n    private fun createCode(): String {\n        val buffer = StringBuilder()\n        when (mType) {\n            TYPE.NUMBER -&gt; {\n                for (i in 0 until mLength) {\n                    buffer.append(CHARS_NUMBER[mRandom.nextInt(CHARS_NUMBER.size)])\n                }\n            }\n            TYPE.LETTER -&gt; {\n                for (i in 0 until mLength) {\n                    buffer.append(CHARS_LETTER[mRandom.nextInt(CHARS_LETTER.size)])\n                }\n            }\n            TYPE.CHARS -&gt; {\n                for (i in 0 until mLength) {\n                    buffer.append(CHARS_ALL[mRandom.nextInt(CHARS_ALL.size)])\n                }\n            }\n        }\n        return buffer.toString()\n    }\n}\n<\/code><\/pre>\n<p><a id=\"b\"><\/a><\/p>\n<h4>2.\u756b\u9762\u5e03\u5c40<\/h4>\n<pre><code class=\"language-XML line-numbers\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\n    xmlns:app=\"http:\/\/schemas.android.com\/apk\/res-auto\"\n    xmlns:tools=\"http:\/\/schemas.android.com\/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\"&gt;\n\n    &lt;androidx.appcompat.widget.AppCompatImageView\n        android:id=\"@+id\/imageView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        app:layout_constraintStart_toStartOf=\"@+id\/code\"\n        app:layout_constraintTop_toBottomOf=\"@+id\/code\"\n        tools:srcCompat=\"@tools:sample\/avatars\" \/&gt;\n\n    &lt;androidx.appcompat.widget.AppCompatEditText\n        android:hint=\"\u8acb\u8f38\u5165\u9a57\u8b49\u78bc\"\n        android:maxLength=\"4\"\n        android:id=\"@+id\/code\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"36dp\"\n        android:ems=\"10\"\n        android:inputType=\"textPersonName\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" \/&gt;\n\n    &lt;androidx.appcompat.widget.AppCompatButton\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"\u9a57\u8b49\"\n        android:onClick=\"validate\"\n        app:layout_constraintBottom_toBottomOf=\"@+id\/code\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@+id\/code\"\n        app:layout_constraintTop_toTopOf=\"@+id\/code\" \/&gt;\n\n    &lt;androidx.appcompat.widget.AppCompatTextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"8dp\"\n        android:text=\"\u770b\u4e0d\u6e05\u695a\u53ef\u9ede\u64ca\u5716\u7247\u66f4\u63db\"\n        android:textColor=\"@color\/black\"\n        android:textSize=\"16sp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toEndOf=\"@+id\/imageView\"\n        app:layout_constraintTop_toTopOf=\"@+id\/imageView\" \/&gt;\n&lt;\/androidx.constraintlayout.widget.ConstraintLayout&gt;\n<\/code><\/pre>\n<p><a id=\"c\"><\/a><\/p>\n<h4>3.\u4f7f\u7528\u9a57\u8b49\u78bc<\/h4>\n<pre><code class=\"language-Kotlin line-numbers\">val mCaptcha = Captcha.instance\n    .setType(Captcha.TYPE.CHARS)\n    .setScale(2)\n    .setSize(200, 100)\n    .setBackgroundColor(Color.WHITE)\n    .setLength(4)\n    .setLineNumber(5)\n    .setFontSize(50)\n    .setFontPadding(25, 20, 60, 20)\n<\/code><\/pre>\n<p><a id=\"d\"><\/a><\/p>\n<h4>4.\u7a0b\u5f0f\u7bc4\u4f8b<\/h4>\n<pre><code class=\"language-Kotlin line-numbers\">package com.example.test\n\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.AppCompatEditText\nimport androidx.appcompat.widget.AppCompatImageView\n\nclass MainActivity : AppCompatActivity() {\n\n    private lateinit var captcha: AppCompatImageView\n    private lateinit var code: AppCompatEditText\n    private lateinit var mCaptcha: Captcha\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        captcha = findViewById(R.id.imageView)\n        code = findViewById(R.id.code)\n\n        createCaptcha()\n    }\n\n    private fun createCaptcha() {\n        mCaptcha = Captcha.instance\n            .setType(Captcha.TYPE.CHARS)\n            .setScale(2)\n            .setSize(200, 100)\n            .setBackgroundColor(Color.WHITE)\n            .setLength(4)\n            .setLineNumber(5)\n            .setFontSize(50)\n            .setFontPadding(25, 20, 60, 20)\n\n        captcha.setImageBitmap(mCaptcha.create())\n\n        captcha.setOnClickListener {\n            captcha.setImageBitmap(mCaptcha.create())\n        }\n    }\n\n    fun validate(view: View) {\n        if (mCaptcha.getCode().equals(code.text.toString(), true)) {\n            Toast.makeText(this, \"\u9a57\u8b49\u6210\u529f\", Toast.LENGTH_SHORT).show()\n        } else {\n            Toast.makeText(this, \"\u9a57\u8b49\u5931\u6557\", Toast.LENGTH_SHORT).show()\n        }\n    }\n}\n<\/code><\/pre>\n<p><a id=\"e\"><\/a><\/p>\n<h4>5.\u6548\u679c\u5c55\u793a<\/h4>\n<p><a href=\"https:\/\/badgameshow.com\/fly\/wp-content\/uploads\/2021\/09\/20210921_160746.gif\"><img decoding=\"async\" src=\"https:\/\/badgameshow.com\/fly\/wp-content\/uploads\/2021\/09\/20210921_160746.gif\" width=\"30%\"\/><\/a><\/p>\n<p><a id=\"f\"><\/a><\/p>\n<h4>6.Github<\/h4>\n<p><a class=\"wp-editor-md-post-content-link\" href=\"https:\/\/github.com\/MuHongWeiWei\/CaptchaDemo\" title=\"Github\" target=\"_blank\" rel=\"noopener\">Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8\u6a5f\u9a57\u8b49\u78bc Github<\/a><\/p>\n\n<div style=\"font-size: 0px; height: 0px; line-height: 0px; margin: 0; padding: 0; clear: both;\"><\/div>","protected":false},"excerpt":{"rendered":"<p>Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8\u6a5f\u9a57\u8b49\u78bc Android Captcha \u9632\u6b62\u6a5f\u5668\u4eba \u751f\u6210\u96a8 &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"pgc_sgb_lightbox_settings":"","footnotes":""},"categories":[5],"tags":[13,87,15],"class_list":["post-1507","post","type-post","status-publish","format-standard","hentry","category-android","tag-android","tag-captcha","tag-kotlin"],"_links":{"self":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1507","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/comments?post=1507"}],"version-history":[{"count":1,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1507\/revisions"}],"predecessor-version":[{"id":1509,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/posts\/1507\/revisions\/1509"}],"wp:attachment":[{"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/media?parent=1507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/categories?post=1507"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badgameshow.com\/fly\/wp-json\/wp\/v2\/tags?post=1507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}