iis服务器助手广告
返回顶部
首页 > 资讯 > 精选 >Android如何实现九宫格手势密码
  • 262
分享到

Android如何实现九宫格手势密码

2023-07-02 13:07:05 262人浏览 薄情痞子
摘要

这篇“Android如何实现九宫格手势密码”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android如何实现九宫格手势密码

这篇“Android如何实现九宫格手势密码”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android如何实现九宫格手势密码”文章吧。

先见图

Android如何实现九宫格手势密码

思路:首先是9个格子,接着是格子连线;那么我们的步骤就有了。

手势监听,进行连线
2.格子的状态未连接(初始状态)、已连接的(没有结果前)、错误状态(有结果后)。(先这三个,可扩展,比如按下状态)
3.自定义viewgroup作为九宫格的容器,里面包含9个view(小格子)

一、先从简单的说起吧,9个小格子以及状态

为了扩展性,不自定义view,将三个状态和有关属性提取

提取属性,代码如下: 

class NineChildInf {                var index = 0                var isLight = false                var centerX = 0.toFloat()        var centerY = 0.toFloat()         fun setContent(index: Int, centerX: Float, centerY: Float) {            this.index = index            this.centerX = centerX            this.centerY = centerY        }         constructor()         fun updateCenterPoint(x: Float, y: Float) {            this.centerX = x            this.centerY = y        }         fun reset() {            this.index = 0            this.centerX = 0f            this.centerY = 0f            this.isLight = false        }         override fun toString(): String {            return "NineChildInf(index=$index, isLight=$isLight, centerX=$centerX, centerY=$centerY)"        }    }

三个状态,代码如下

abstract class NineChildParent<T : View>(var view: T) {    protected open var context = view.context.applicationContext    val NINE_CHILD_INF = NineChildInf()        abstract fun setErrorStatue()         abstract fun setLightStatue()         abstract fun setDefaultStatue() }

二、自定义九宫格容器,NineViewGroup。

既然是九宫格,那自然少不了这些属性,水平间隔、垂直间隔、最小有效连接数、当前状态、密码是否设置完成等。还需要将开启viewgroup的onDraw()方法。具体代码如下:

class NineViewGroup @JVMOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) {        private var paddingH = 60        private var paddingV = 60        var minEffectiveSize = 4        private var childSlide: Int = 30    private val ERROR_STATUE = 2    private val LINKING_STATUE = 1    private val DEFAULT_STATUE = 0        private var nowStatue = DEFAULT_STATUE        private var complete = false        private var lineWidth = 5    private var lineColor = Color.parseColor("#33b5e5")    private var errorLineColor = Color.RED    private var childViews = ArrayList<NineChildParent<*>>(9)    init {        //使能调用onDraw()方法        setWillNotDraw(false)        var array = context.obtainStyledAttributes(attrs, R.styleable.NineViewGroup, defStyleAttr, 0)        (0..array.indexCount).forEach {            var index = array.getIndex(it)            when (index) {                R.styleable.NineViewGroup_nine_child_size -> childSlide = array.getDimensionPixelSize(index, childSlide)                R.styleable.NineViewGroup_nine_line_color -> lineColor = array.getColor(index, lineColor)                R.styleable.NineViewGroup_nine_error_line_color -> errorLineColor = array.getColor(index, errorLineColor)                R.styleable.NineViewGroup_nine_effective_size -> minEffectiveSize = array.getInt(index, minEffectiveSize)                R.styleable.NineViewGroup_nine_padding_h -> paddingH = array.getDimensionPixelSize(index, paddingH)                R.styleable.NineViewGroup_nine_padding_v -> paddingV = array.getDimensionPixelSize(index, paddingV)                R.styleable.NineViewGroup_nine_line_width -> lineWidth = array.getDimensionPixelSize(index, lineWidth)            }        }        array.recycle()          }    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {        var width = childSlide * 3 + paddingLeft + paddingRight + paddingH * 2        var height = childSlide * 3 + paddingTop + paddingBottom + paddingV * 2        setMeasuredDimension(width, height)        //又忘了计算子view的大小了。。。        measureChildren(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))    }    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {        var childView: View        var top: Int = paddingTop        var left: Int = paddingLeft        var right: Int        var bottom: Int        if (childCount > 0) {            (0 until childCount).forEach {                childView = getChildAt(it)                right = left + childView.measuredWidth                bottom = top + childView.measuredHeight                //Log.e("TAG", "onLayout: $left $top $right $bottom")                var nineChildInf = (childViews[it]).NINE_CHILD_INF                nineChildInf.setContent(it + 1, (left + right) / 2f, (top + bottom) / 2f)                //Log.e("TAG", "onLayout: child=$nineChildInf")                childView.layout(left, top, right, bottom)                if ((it + 1) % 3 == 0) {                    left = paddingLeft                    top = bottom + paddingV                } else {                    left = right + paddingH                }            }        }    }}

三、手势监听、连线

手势监听,重写onTouchEvent()方法,必要需要时重写onInterceptTouchEvent()方法进行拦截(跟情况而定,这里就不多说了)。简单的三个手势状态按下、移动、抬起。在各个状态下,记录坐标,并且更新子view(小格子)的ui,还有线条。代码片段如下:

override fun onTouchEvent(event: MotionEvent): Boolean {        if (childCount == 0 || complete) {            return super.onTouchEvent(event)        }        when (event.action) {            MotionEvent.ACTION_DOWN -> {                //记录落点                lastX = event.x                lastY = event.y                downUpdateChild(lastX, lastY)            }            MotionEvent.ACTION_MOVE -> {                lastX = event.x                lastY = event.y                moveUpdateChild(lastX, lastY)            }            MotionEvent.ACTION_UP -> {                complete = true                //统计                upUpdateChild()            }        }        return true    }

连线,在容器的onDraw()方法,进行画线操作,代码片段如下:

override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)        if (!showLine) {            return        }        paint.color = when (nowStatue) {            ERROR_STATUE -> errorLineColor            else -> lineColor        }        if (points.size > 1) {            (1 until points.size).forEach {                var pointXYStart = points[it - 1].NINE_CHILD_INF                var pointXYEnd = points[it].NINE_CHILD_INF                canvas.drawLine(pointXYStart.centerX, pointXYStart.centerY, pointXYEnd.centerX, pointXYEnd.centerY, paint)            }        }        if (lastX > 0 && points.size > 0) {            var pointXY = points[points.size - 1].NINE_CHILD_INF            canvas.drawLine(pointXY.centerX, pointXY.centerY, lastX, lastY, paint)        }    }

四、进行到这一步,大致的步骤就是这了。

但是还有一些细节:比如连线中需要判断中间是否含有小格子、判断触点是否在小格子上、连接完成后的回调、错误状态显示、恢复初始状态等。粘出部分代码片段(这些只是能实现效果,还可以优化,交给大家了):

判断触点是否在小格子上

private fun childContains(x: Float, y: Float): Boolean {        (0 until childCount).forEach {            var childAt = getChildAt(it)            //这一句,循环判断,是否属于其范围            if (x >= childAt.left && x < childAt.right && y >= childAt.top && y < childAt.bottom) {                return if (!childViews[it].NINE_CHILD_INF.isLight) {                    if (points.size > 0) {                        checkMiddleChild(points[points.size - 1], childViews[it])?.run {                            if (!NINE_CHILD_INF.isLight) {                                buffer.append(NINE_CHILD_INF.index)                                changeLightStatue(this)                            }                        }                    }                    buffer.append(it + 1)                    //TODO 改变子view的UI状态                    changeLightStatue(childViews[it])                    true                } else {                    false                }            }        }        return false    }

判断中间是否含有小格子

private fun checkMiddleChild(nineChildParent: NineChildParent<*>, nineChildParent1: NineChildParent<*>): NineChildParent<*>? {        var index = nineChildParent.NINE_CHILD_INF.index        var index1 = nineChildParent1.NINE_CHILD_INF.index        var sum = index + index1        if (sum == 10) {            return childViews[4]        } else if (index % 2 != 0 && index1 % 2 != 0) {            if ((sum == 4 || sum == 16) || (sum == 8 && (index == 1 || index1 == 1))||(sum == 12 && (index == 3 || index1 == 3)))                return childViews[sum / 2 - 1]        }        return null    }

五、如有bug欢迎留言指出,下面粘出九宫格容器的全部代码。

class NineViewGroup @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) {        private var paddingH = 60        private var paddingV = 60        private var firstSelect = true    private val ERROR_STATUE = 2    private val LINKING_STATUE = 1    private val DEFAULT_STATUE = 0        var showLine = false        var minEffectiveSize = 4        private var nowStatue = DEFAULT_STATUE        private var complete = false        private var lineWidth = 5    private var lastX: Float = 0f    private var lastY: Float = 0f    private var buffer = StringBuilder()    private var points = ArrayList<NineChildParent<*>>(9)    private var childViews = ArrayList<NineChildParent<*>>(9)        private var childSlide: Int = 30    private var lineColor = Color.parseColor("#33b5e5")    private var errorLineColor = Color.RED    var onNineViewGroupListener: OnNineViewGroupListener? = null        set(value) {            field = value            value?.let {                setChildMode(it)            }        }    private val paint = Paint().apply {        isAntiAlias = true        isDither = true    }     init {        //使能调用onDraw()方法        setWillNotDraw(false)        var array = context.obtainStyledAttributes(attrs, R.styleable.NineViewGroup, defStyleAttr, 0)        (0..array.indexCount).forEach {            var index = array.getIndex(it)            when (index) {                R.styleable.NineViewGroup_nine_child_size -> childSlide = array.getDimensionPixelSize(index, childSlide)                R.styleable.NineViewGroup_nine_line_color -> lineColor = array.getColor(index, lineColor)                R.styleable.NineViewGroup_nine_error_line_color -> errorLineColor = array.getColor(index, errorLineColor)                R.styleable.NineViewGroup_nine_effective_size -> minEffectiveSize = array.getInt(index, minEffectiveSize)                R.styleable.NineViewGroup_nine_padding_h -> paddingH = array.getDimensionPixelSize(index, paddingH)                R.styleable.NineViewGroup_nine_padding_v -> paddingV = array.getDimensionPixelSize(index, paddingV)                R.styleable.NineViewGroup_nine_show_line -> showLine = array.getBoolean(index, showLine)                R.styleable.NineViewGroup_nine_line_width -> lineWidth = array.getDimensionPixelSize(index, lineWidth)            }        }        array.recycle()        paint.strokeWidth = lineWidth.toFloat()    }     private fun setChildMode(onNineViewGroupListener: OnNineViewGroupListener) {        removeAllViews()        childViews.clear()        (0..8).forEach {            var mode = onNineViewGroupListener.getChildMode()            mode.NINE_CHILD_INF.index = it + 1            mode.setDefaultStatue()            addView(mode.view, getLp())            childViews.add(mode)        }    }     private fun getLp(): LayoutParams {        return LayoutParams(childSlide, childSlide)    }     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {        var width = childSlide * 3 + paddingLeft + paddingRight + paddingH * 2        var height = childSlide * 3 + paddingTop + paddingBottom + paddingV * 2        setMeasuredDimension(width, height)        //又忘了计算子view的大小了。。。        measureChildren(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))    }     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {        var childView: View        var top: Int = paddingTop        var left: Int = paddingLeft        var right: Int        var bottom: Int        if (childCount > 0) {            (0 until childCount).forEach {                childView = getChildAt(it)                right = left + childView.measuredWidth                bottom = top + childView.measuredHeight                //Log.e("TAG", "onLayout: $left $top $right $bottom")                var nineChildInf = (childViews[it]).NINE_CHILD_INF                nineChildInf.setContent(it + 1, (left + right) / 2f, (top + bottom) / 2f)                //Log.e("TAG", "onLayout: child=$nineChildInf")                childView.layout(left, top, right, bottom)                if ((it + 1) % 3 == 0) {                    left = paddingLeft                    top = bottom + paddingV                } else {                    left = right + paddingH                }            }        }    }     override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {        return true    }    override fun onTouchEvent(event: MotionEvent): Boolean {        if (childCount == 0 || complete) {            return super.onTouchEvent(event)        }        when (event.action) {            MotionEvent.ACTION_DOWN -> {                //记录落点                lastX = event.x                lastY = event.y                downUpdateChild(lastX, lastY)            }            MotionEvent.ACTION_MOVE -> {                lastX = event.x                lastY = event.y                moveUpdateChild(lastX, lastY)            }            MotionEvent.ACTION_UP -> {                complete = true                //统计                upUpdateChild()            }        }        return true    }     private fun downUpdateChild(x: Float, y: Float) {        firstSelect = childContains(x, y)    }     private fun moveUpdateChild(x: Float, y: Float) {        if (firstSelect) {            moveUpdateLineAndChildView(x, y)        } else {            downUpdateChild(x, y)        }    }     private fun moveUpdateLineAndChildView(x: Float, y: Float) {        if (points.size != childCount)            childContains(x, y)        invalidate()    }     private fun upUpdateChild() {        var effective = points.size >= minEffectiveSize        onNineViewGroupListener?.complete(effective, buffer.toString())    }         fun showErrorStatue() {        nowStatue = ERROR_STATUE        points.forEach {            it.setErrorStatue()        }        invalidate()        resetStatueDelayed(500)    }         private fun resetStatue() {        points.clear()        firstSelect = false        lastX = 0f        lastY = 0f        buffer.clear()        nowStatue = DEFAULT_STATUE        (0 until childCount).forEach {            var nineChildParent = childViews[it]            nineChildParent.setDefaultStatue()            nineChildParent.NINE_CHILD_INF.isLight = false        }        invalidate()        complete = false    }     fun resetStatueDelayed(time: Int) {        postDelayed({ resetStatue() }, time.toLong())    }     private fun childContains(x: Float, y: Float): Boolean {        (0 until childCount).forEach {            var childAt = getChildAt(it)            if (x >= childAt.left && x < childAt.right && y >= childAt.top && y < childAt.bottom) {                return if (!childViews[it].NINE_CHILD_INF.isLight) {                    if (points.size > 0) {                        checkMiddleChild(points[points.size - 1], childViews[it])?.run {                            if (!NINE_CHILD_INF.isLight) {                                buffer.append(NINE_CHILD_INF.index)                                changeLightStatue(this)                            }                        }                    }                    buffer.append(it + 1)                    //TODO 改变子view的UI状态                    changeLightStatue(childViews[it])                    true                } else {                    false                }            }        }        return false    }     private fun changeLightStatue(childParent: NineChildParent<*>) {        childParent.NINE_CHILD_INF.isLight = true        childParent.setLightStatue()        points.add(childParent)//记录    }     private fun checkMiddleChild(nineChildParent: NineChildParent<*>, nineChildParent1: NineChildParent<*>): NineChildParent<*>? {        var index = nineChildParent.NINE_CHILD_INF.index        var index1 = nineChildParent1.NINE_CHILD_INF.index        var sum = index + index1        if (sum == 10) {            return childViews[4]        } else if (index % 2 != 0 && index1 % 2 != 0) {            if ((sum == 4 || sum == 16) || (sum == 8 && (index == 1 || index1 == 1))||(sum == 12 && (index == 3 || index1 == 3)))                return childViews[sum / 2 - 1]        }        return null    }     override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)        if (!showLine) {            return        }        paint.color = when (nowStatue) {            ERROR_STATUE -> errorLineColor            else -> lineColor        }        if (points.size > 1) {            (1 until points.size).forEach {                var pointXYStart = points[it - 1].NINE_CHILD_INF                var pointXYEnd = points[it].NINE_CHILD_INF                canvas.drawLine(pointXYStart.centerX, pointXYStart.centerY, pointXYEnd.centerX, pointXYEnd.centerY, paint)            }        }        if (lastX > 0 && points.size > 0) {            var pointXY = points[points.size - 1].NINE_CHILD_INF            canvas.drawLine(pointXY.centerX, pointXY.centerY, lastX, lastY, paint)        }    }     interface OnNineViewGroupListener {                fun getChildMode(): NineChildParent<*>                 fun complete(effective: Boolean, passWord: String)    }}

以上就是关于“Android如何实现九宫格手势密码”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网精选频道。

--结束END--

本文标题: Android如何实现九宫格手势密码

本文链接: https://www.lsjlt.com/news/342146.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android如何实现九宫格手势密码
    这篇“Android如何实现九宫格手势密码”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android如何实现九宫格手势密码...
    99+
    2023-07-02
  • Android实现九宫格手势密码
    本文实例为大家分享了Android实现九宫格手势密码的具体代码,供大家参考,具体内容如下 介绍下自己编写的九宫格手势密码。先见图 思路:首先是9个格子,接着是格子连线;那么我们的步...
    99+
    2024-04-02
  • Android开发中中怎么实现投放九宫格手势密码功能
    这篇文章将为大家详细讲解有关Android开发中中怎么实现投放九宫格手势密码功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。要实现九宫格密码要解决的问题有:给九宫格密码界面布局九个点,即确...
    99+
    2023-05-31
    android roi
  • Android实现九宫格抽奖
    本文实例为大家分享了Android实现九宫格抽奖的具体代码,供大家参考,具体内容如下 package cq.cake.luckdraw; import android.graph...
    99+
    2024-04-02
  • Android实现图片九宫格
    本文实例为大家分享了Android实现图片九宫格的具体代码,供大家参考,具体内容如下 九宫格分三类 实现的效果 具体实现 activity_main <xml v...
    99+
    2024-04-02
  • 怎么在Android 应用中实现一个九宫格手势锁
    怎么在Android 应用中实现一个九宫格手势锁?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。主要的方法是重写View.onTouchEvent( Motion...
    99+
    2023-05-31
    android roi
  • Android RecyclerView实现九宫格效果
    RecyclerView更加优化的复用机制和方便实现UI效果,几乎替代Listview和GridView的使用。但是分割线的实现,需要自己继承ItemDecoration来绘制。 效...
    99+
    2024-04-02
  • Android如何实现数字九宫格软键盘
    这篇文章主要介绍了Android如何实现数字九宫格软键盘,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言一开始大概是这种需求组长说 要不搞一个自定义软键盘吧 数字搞大点 方...
    99+
    2023-06-15
  • Android怎么实现九宫格解锁
    这篇文章主要介绍Android怎么实现九宫格解锁,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!首先理清一下逻辑,我们要做NxN的九宫格 下图是3x3的简单图例// -(--)-(--)-(--)-// -(--)-(...
    99+
    2023-05-30
    android
  • Android实现九宫格图案解锁
    本文实例为大家分享了Android实现九宫格图案解锁的具体代码,供大家参考,具体内容如下 前言:自定义了一个九宫格的VIew来绘制九宫格图案,实现了绘制图案解锁的功能。 效果图如下:...
    99+
    2024-04-02
  • Android 实现九宫格抽奖功能
    目录效果展示实现步骤 1.生成抽奖矩形:2.添加奖品图片:3.实现抽奖动画:4.实现动态设置参数:5.添加抽奖结果回调效果展示 实现步骤 1.生成抽奖矩形: 其中每个矩形的宽高相...
    99+
    2024-04-02
  • Android 实现数字九宫格软键盘
    目录前言需求实现效果GIF实现代码使用方法前言 一开始大概是这种 需求 组长说 要不搞一个自定义软键盘吧 数字搞大点 方便外卖员输入数字 我设置了输入EditText的输入格式为...
    99+
    2024-04-02
  • jquery如何实现九宫格抽奖
    这篇文章主要介绍jquery如何实现九宫格抽奖,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!jquery实现九宫格抽奖的方法:1、创建好前端展示的代码;2、通过jquery代码“$("#lottery a&...
    99+
    2023-06-21
  • Android编程简单实现九宫格示例
    本文实例讲述了Android编程简单实现九宫格。分享给大家供大家参考,具体如下:实现的步骤 一个整体的容器部分。就是上图中包括整个图片项个各个部分,这里我们使用gridView(表格布局)来实现整个界面里需要注意的是 “重复的部分”,就是 ...
    99+
    2023-05-31
    android 九宫格 roi
  • Android怎么实现九宫格图案解锁
    今天小编给大家分享一下Android怎么实现九宫格图案解锁的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。效果图如下: 第一步...
    99+
    2023-07-02
  • 如何利用CSS实现九宫格布局
    今天就跟大家聊聊有关如何利用CSS实现九宫格布局,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。最近几天刷面经常看见一道题,“九宫格布局”。自己尝试用...
    99+
    2024-04-02
  • Android如何自定义ViewGroup实现朋友圈九宫格控件
    本篇内容介绍了“Android如何自定义ViewGroup实现朋友圈九宫格控件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录一、简介1、...
    99+
    2023-06-20
  • JS如何实现九宫格拼图游戏
    这篇文章主要介绍了JS如何实现九宫格拼图游戏的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JS如何实现九宫格拼图游戏文章都会有所收获,下面我们一起来看看吧。具体代码如下<!doctype htm...
    99+
    2023-07-02
  • Android自定义控件实现九宫格解锁
    关于九宫格解锁,我看了不少博客,但是都感觉很复杂,可能我的功夫还不到,所以很多东西我不了解,但是我还是打算写一个自己的九宫格。我相信我的九宫格大家都能很快的理解,当然如果需要实现更复...
    99+
    2024-04-02
  • JavaScript如何实现九宫格拖拽效果
    这篇文章主要介绍“JavaScript如何实现九宫格拖拽效果”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JavaScript如何实现九宫格拖拽效果”文章能帮助大家解决问题。代码如下:<!DO...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作