文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android怎么新建水平节点进度条

2023-07-02 13:27

关注

本文小编为大家详细介绍“Android怎么新建水平节点进度条”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android怎么新建水平节点进度条”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

效果图

前几天在网上没有找到合适的横向节点进度条,自己动手写了一个,先来看看效果图

Android怎么新建水平节点进度条

圆圈和文字状态

我们看到小圆圈和文字有几种状态呢?

文字居中

我们写一个类继承自AppCompatTextView,通过onMeasure方法得到控件的宽高,通过Paint的getTextBounds()也可以知道文字的宽高,我们看到有5个节点需要处理,我们把屏幕划分成5个等份,每个等份都相等,这里用itemWidth 表示每个相同的等份。文字居中的写法很简单,

itemWidth / 2 - textWidth / 2

代码

package cn.wwj.customview.widgetimport android.content.Contextimport android.graphics.Canvasimport android.graphics.Colorimport android.graphics.Paintimport android.graphics.Rectimport android.text.TextPaintimport android.util.AttributeSetimport android.util.Logimport androidx.annotation.Nullableimport androidx.appcompat.widget.AppCompatTextViewimport androidx.core.content.ContextCompatimport androidx.core.view.marginTopimport cn.wwj.customview.Rimport cn.wwj.customview.dp2pximport cn.wwj.customview.sp2pxclass NodePointProcessBar : AppCompatTextView {        private lateinit var mTextPaint: TextPaint        private lateinit var mCirclePaint: Paint    private var isDebug = false        private var mCompleteTextColor: Int = ContextCompat.getColor(context, android.R.color.black)        private var mProcessTextColor: Int = ContextCompat.getColor(context, R.color.purple)        private var mWaitProcessTextColor: Int = ContextCompat.getColor(context, R.color.gray_text)        private var mCircleCount = 0    private var TAG = "NodePointProcessBar"        private var mCircleRadius = 5f.dp2px()        private var mCircleBorder = 1f.dp2px()        private var mLineWidth = 1f.dp2px()        private var mLineMargin = 4f.dp2px()        var mTextCircleMargin = 7f.dp2px()        private var mTextLeftRightMargin = 8f.dp2px()        private var mContentHeight = 0f    private var mContentWidth = 0f        private var mTextList: List<String> = mutableListOf()        private var mProcessIndexSet: Set<Int> = mutableSetOf()        private var mTextBoundList: MutableList<Rect> = mutableListOf()        private val mRect = Rect()    constructor(context: Context) : this(context, null)    constructor(context: Context, @Nullable attrs: AttributeSet?) : this(context, attrs, 0)    constructor(        context: Context,        @Nullable attrs: AttributeSet?,        defStyleAttr: Int    ) : super(context, attrs, defStyleAttr) {        val appearance = context.obtainStyledAttributes(attrs, R.styleable.NodePointProcessBar)        mCompleteTextColor = appearance.getColor(            R.styleable.NodePointProcessBar_completedTextColor, mCompleteTextColor        )        mWaitProcessTextColor = appearance.getColor(            R.styleable.NodePointProcessBar_processTextColor, mWaitProcessTextColor        )        mProcessTextColor =            appearance.getColor(                R.styleable.NodePointProcessBar_waitProcessTextColor,                mProcessTextColor            )        isDebug = appearance.getBoolean(            R.styleable.NodePointProcessBar_isDebug, isDebug        )        mTextCircleMargin = appearance.getDimension(            R.styleable.NodePointProcessBar_textCircleMargin, mTextCircleMargin        )        mTextLeftRightMargin = appearance.getDimension(            R.styleable.NodePointProcessBar_textLeftRightMargin, mTextLeftRightMargin        )        mCircleRadius = appearance.getDimension(            R.styleable.NodePointProcessBar_npbCircleRadius, mCircleRadius        )        mCircleBorder = appearance.getDimension(            R.styleable.NodePointProcessBar_circleBorder, mCircleBorder        )        mLineWidth = appearance.getDimension(            R.styleable.NodePointProcessBar_lineWidth, mLineWidth        )        mLineMargin = appearance.getDimension(            R.styleable.NodePointProcessBar_lineMargin, mLineMargin        )        initPaint()        show(mTextList, mProcessIndexSet)    }        private fun initPaint() {        // 设置文字画笔        mTextPaint = TextPaint()        mTextPaint.isAntiAlias = true        mTextPaint.textSize = textSize        mTextPaint.color = mWaitProcessTextColor        // 设置圆圈画笔        mCirclePaint = Paint()        mCirclePaint.isAntiAlias = true        mCirclePaint.color = mProcessTextColor        mCirclePaint.style = Paint.Style.STROKE        mCirclePaint.strokeWidth = mCircleBorder    }    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec)        val widthMode = MeasureSpec.getMode(widthMeasureSpec)        val heightMode = MeasureSpec.getMode(heightMeasureSpec)        Log.d(TAG, "---------------onMeasure()")        measureText()        val widthSize = if (MeasureSpec.EXACTLY == widthMode) {            MeasureSpec.getSize(widthMeasureSpec)        } else {            mContentWidth.toInt()        }        val heightSize = if (MeasureSpec.EXACTLY == heightMode) {            MeasureSpec.getSize(heightMeasureSpec)        } else {            mContentHeight.toInt()        }                setMeasuredDimension(widthSize, heightSize)        calcContentWidthHeight()    }        private fun measureText() {        Log.d(TAG, "---------------measureText()")        mTextBoundList.clear()        for (name in mTextList) {            mRect.setEmpty()            mTextPaint.getTextBounds(name, 0, name.length, mRect)            mTextBoundList.add(mRect)        }    }        private fun calcContentWidthHeight() {        // 一开始没有传递文字的        mContentHeight = if (mTextBoundList.isNotEmpty()) {            mCircleRadius * 2 + mTextCircleMargin + mRect.height() + getBaseline(mTextPaint)        } else {            mTextPaint.getTextBounds("中", 0, 1, mRect)            mCircleRadius * 2 + mTextCircleMargin + mRect.height() + getBaseline(mTextPaint)        }        if (measuredWidth == 0 || mTextBoundList.isEmpty()) {            return        }        mContentWidth = 0f        for (rect in mTextBoundList) {            mContentWidth += rect.width()        }        Log.d(TAG, "---------------measuredWidth=$measuredWidth,mContentWidth=$mContentWidth")        // 如果控件的宽度小于内容的宽度加文本的边距,意味着一行放不下了,文字的大小减小1sp,重新测量文字的宽高后,设置控件得高度        // 如果控件的宽度大于内容的宽度加文本的边距,意味着一行放得下,设置控件得高度        if (measuredWidth - mContentWidth < (mTextLeftRightMargin * (mTextList.size - 1))) {            mTextPaint.textSize = mTextPaint.textSize - 1f.sp2px()            measureText()            calcContentWidthHeight()            return        }        setMeasuredDimension(measuredWidth, mContentHeight.toInt())    }    override fun onDraw(canvas: Canvas) {        //若未设置节点或者选中项的列表,则取消绘制        if (mTextList.isEmpty() || mTextBoundList.isEmpty()) {            return        }        //画灰色圆圈的个数        mCircleCount = mTextList.size        // 每一段文字的Y坐标        val textY = getBaseline(mTextPaint) + height / 2 + marginTop / 2        mCirclePaint.strokeWidth = mCircleBorder        //绘制文字和圆形        for (i in 0 until mCircleCount) {            if (mProcessIndexSet.contains(i)) {                // 正在处理中                if (mProcessIndexSet.size == i + 1) {                    mCirclePaint.style = Paint.Style.FILL                    // 正在处理中的文字颜色                    mTextPaint.color = mProcessTextColor                    mCirclePaint.color = mProcessTextColor                } else {                    //处理完成圆圈空心                    mCirclePaint.style = Paint.Style.STROKE                    //处理完成文字颜色                    mTextPaint.color = mCompleteTextColor                    mCirclePaint.color = mProcessTextColor                }            } else {                //待处理                mCirclePaint.color = mWaitProcessTextColor                mCirclePaint.style = Paint.Style.FILL                mTextPaint.color = mWaitProcessTextColor            }            //每一段文字宽度            val textWidth = mTextBoundList[i].width()            // 每一段宽度            val itemWidth = width * 1f / mCircleCount            // 每一段文字居中            // |----text----|----text----|            //    一段文字       一段文字            //每一段文字起始的X坐标            val textX = itemWidth / 2f - textWidth / 2f + i * itemWidth            canvas.drawText(mTextList[i], textX, textY, mTextPaint)            //每一个圆圈的Y坐标            val circleY = height / 2f - mCircleRadius - mTextCircleMargin / 2            //每一个圆圈的X坐标            val circleX = itemWidth / 2 + i * itemWidth            canvas.drawCircle(                circleX,                circleY,                mCircleRadius,                mCirclePaint            )            // 画线,两个圆圈之间一条线段            mCirclePaint.strokeWidth = mLineWidth            if (i < mCircleCount - 1) {                //已经处理过的线颜色                if (mProcessIndexSet.contains(i + 1)) {                    mCirclePaint.color = mProcessTextColor                } else {                    // 待处理的线段颜色                    mCirclePaint.color = mWaitProcessTextColor                }                // 线段起始 x 坐标                val lineStartX = itemWidth * i + itemWidth / 2f + mCircleRadius + mLineMargin                // 线段结束 x 坐标                val lineEndX =                    itemWidth * i + itemWidth + itemWidth / 2f - mCircleRadius - mLineMargin                canvas.drawLine(                    lineStartX,                    circleY,                    lineEndX,                    circleY,                    mCirclePaint                )            }            Log.d("tag", "--------itemWidth=$itemWidth")        }        if (isDebug) {            mCirclePaint.color = Color.RED            canvas.drawLine(                0f,                height / 2f - 1f.dp2px() / 2,                width * 1F,                height / 2f + 1f.dp2px() / 2,                mCirclePaint            )        }    }        fun setNodeData(titles: List<String>, progressIndexSet: Set<Int>) {        mTextList = titles        mProcessIndexSet = progressIndexSet        measureText()        calcContentWidthHeight()        invalidate()    }        private fun getBaseline(p: Paint): Float {        val fontMetrics: Paint.FontMetrics = p.fontMetrics        return (fontMetrics.bottom - fontMetrics.top) - fontMetrics.descent    }}

这里的show()方法用于展示内容,第一个参数要展示的内容列表,第二个参数代表节点选中项集合,紧接着测量文字的宽高,调用这个方法calcContentWidthHeight()获取文字的高度,然后设置文字的宽高,代码中的注释写的很详细,我们就不再细说了

声明下style

Android怎么新建水平节点进度条

attrs.xml

 <declare-styleable name="NodePointProcessBar">        <attr name="completedTextColor" format="color" />        <attr name="processTextColor" format="color" />        <attr name="waitProcessTextColor" format="color" />        <attr name="textCircleMargin" format="dimension" />        <attr name="textLeftRightMargin" format="dimension" />        <attr name="npbCircleRadius" format="dimension" />        <attr name="circleBorder" format="dimension" />        <attr name="lineWidth" format="dimension" />        <attr name="lineMargin" format="dimension" />        <attr name="isDebug" format="boolean" />    </declare-styleable>

新建一个ExtendUtil.kt文件

fun Int.sp2px(): Int {    val displayMetrics = Resources.getSystem().displayMetrics    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), displayMetrics)        .toInt()}fun Float.sp2px(): Float {    val displayMetrics = Resources.getSystem().displayMetrics    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this, displayMetrics)}fun Int.dp2px(): Int {    val displayMetrics = Resources.getSystem().displayMetrics    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), displayMetrics)        .toInt()}fun Float.dp2px(): Float {    val displayMetrics = Resources.getSystem().displayMetrics    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, displayMetrics)}

接着创建布局文件

activity_node_progress_bar.xml

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent">    <cn.wwj.customview.widget.NodePointProcessBar        android:id="@+id/nodePointPb"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:textSize="18sp"        android:layout_marginHorizontal="10dp"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:isDebug="false"        app:lineWidth="1dp"        app:lineMargin="5dp"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

再Activity中使用它

package cn.wwj.customviewimport android.os.Bundleimport android.os.Handlerimport android.os.Looperimport androidx.appcompat.app.AppCompatActivityimport cn.wwj.customview.widget.NodePointProcessBarclass NodeProgressBarActivity : AppCompatActivity() {        private val mTextList: List<String> = mutableListOf("提交申请", "商家处理", "寄回商品", "商家退款", "退款成功")        private var mProgressIndexSet: Set<Int> = mutableSetOf(0, 1,2,3,4,6)    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_node_progress_bar)        val nodePointPb: NodePointProcessBar = findViewById(R.id.nodePointPb)        Handler(Looper.getMainLooper()).postDelayed({            nodePointPb.setNodeData(mTextList, mProgressIndexSet)        }, 1000)    }}

mTextList数据集合

mProgressIndexSet正在处理的节点索引结合,创建Handler对象模拟调用网络接口,1秒后返回数据

Android怎么新建水平节点进度条

读到这里,这篇“Android怎么新建水平节点进度条”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯