最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android新建水平節(jié)點(diǎn)進(jìn)度條示例

 更新時(shí)間:2022年06月25日 16:02:16   作者:as_pixar  
這篇文章主要為大家介紹了Android新建水平節(jié)點(diǎn)進(jìn)度條示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

效果圖

前幾天在網(wǎng)上沒(méi)有找到合適的橫向節(jié)點(diǎn)進(jìn)度條,自己動(dòng)手寫了一個(gè),先來(lái)看看效果圖

圓圈和文字狀態(tài)

我們看到小圓圈和文字有幾種狀態(tài)呢?

  • 第一個(gè)空心的小圓圈是處理完成的狀態(tài)
  • 第二個(gè)實(shí)心的小圓圈是處理中的狀態(tài)
  • 第三個(gè)實(shí)心的小圓圈是待處理的狀態(tài)
    沒(méi)錯(cuò),我們看到了小圓圈和文字有三種處理狀態(tài)

文字居中

我們寫一個(gè)類繼承自AppCompatTextView,通過(guò)onMeasure方法得到控件的寬高,通過(guò)Paint的getTextBounds()也可以知道文字的寬高,我們看到有5個(gè)節(jié)點(diǎn)需要處理,我們把屏幕劃分成5個(gè)等份,每個(gè)等份都相等,這里用itemWidth 表示每個(gè)相同的等份。文字居中的寫法很簡(jiǎn)單,

itemWidth / 2 - textWidth / 2

代碼

package cn.wwj.customview.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
import androidx.annotation.Nullable
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.core.view.marginTop
import cn.wwj.customview.R
import cn.wwj.customview.dp2px
import cn.wwj.customview.sp2px
/**
 * 節(jié)點(diǎn)進(jìn)度條
 */
class 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)
    /**
     * 繪制的節(jié)點(diǎn)個(gè)數(shù),由底部節(jié)點(diǎn)標(biāo)題數(shù)量控制
     */
    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()
    /**
     * 計(jì)算內(nèi)容的高度 和 寬度
     */
    private var mContentHeight = 0f
    private var mContentWidth = 0f
    /**
     * 節(jié)點(diǎn)底部的文字列表
     */
    private var mTextList: List<String> = mutableListOf()
    /**
     * 選中項(xiàng)集合
     */
    private var mProcessIndexSet: Set<Int> = mutableSetOf()
    /**
     * 文字同寬高的矩形,用來(lái)測(cè)量文字
     */
    private var mTextBoundList: MutableList<Rect> = mutableListOf()
    /**
     * 計(jì)算文字寬高的矩形
     */
    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() {
        // 設(shè)置文字畫筆
        mTextPaint = TextPaint()
        mTextPaint.isAntiAlias = true
        mTextPaint.textSize = textSize
        mTextPaint.color = mWaitProcessTextColor
        // 設(shè)置圓圈畫筆
        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()
        }
        /**
         * 設(shè)置控件的寬高
         */
        setMeasuredDimension(widthSize, heightSize)
        calcContentWidthHeight()
    }
    /**
     * 測(cè)量文字的長(zhǎng)寬,將文字視為rect矩形
     */
    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)
        }
    }
    /**
     * 獲取內(nèi)容的高度,如果控件的寬度小于內(nèi)容的寬度,意味著一行放不下了,文字的大小減小1sp,重新測(cè)量文字的寬高,重新
     */
    private fun calcContentWidthHeight() {
        // 一開(kāi)始沒(méi)有傳遞文字的
        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")
        // 如果控件的寬度小于內(nèi)容的寬度加文本的邊距,意味著一行放不下了,文字的大小減小1sp,重新測(cè)量文字的寬高后,設(shè)置控件得高度
        // 如果控件的寬度大于內(nèi)容的寬度加文本的邊距,意味著一行放得下,設(shè)置控件得高度
        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) {
        //若未設(shè)置節(jié)點(diǎn)標(biāo)題或者選中項(xiàng)的列表,則取消繪制
        if (mTextList.isEmpty() || mTextBoundList.isEmpty()) {
            return
        }
        //畫灰色圓圈的個(gè)數(shù)
        mCircleCount = mTextList.size
        // 每一段文字的Y坐標(biāo)
        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坐標(biāo)
            val textX = itemWidth / 2f - textWidth / 2f + i * itemWidth
            canvas.drawText(mTextList[i], textX, textY, mTextPaint)
            //每一個(gè)圓圈的Y坐標(biāo)
            val circleY = height / 2f - mCircleRadius - mTextCircleMargin / 2
            //每一個(gè)圓圈的X坐標(biāo)
            val circleX = itemWidth / 2 + i * itemWidth
            canvas.drawCircle(
                circleX,
                circleY,
                mCircleRadius,
                mCirclePaint
            )
            // 畫線,兩個(gè)圓圈之間一條線段
            mCirclePaint.strokeWidth = mLineWidth
            if (i < mCircleCount - 1) {
                //已經(jīng)處理過(guò)的線顏色
                if (mProcessIndexSet.contains(i + 1)) {
                    mCirclePaint.color = mProcessTextColor
                } else {
                    // 待處理的線段顏色
                    mCirclePaint.color = mWaitProcessTextColor
                }
                // 線段起始 x 坐標(biāo)
                val lineStartX = itemWidth * i + itemWidth / 2f + mCircleRadius + mLineMargin
                // 線段結(jié)束 x 坐標(biāo)
                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
            )
        }
    }
    /**
     * 供外部調(diào)用,展示內(nèi)容
     * @param titles 要展示的內(nèi)容列表
     * @param progressIndexSet 節(jié)點(diǎn)選中項(xiàng)集合
     */
    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()方法用于展示內(nèi)容,第一個(gè)參數(shù)要展示的內(nèi)容列表,第二個(gè)參數(shù)代表節(jié)點(diǎn)選中項(xiàng)集合,緊接著測(cè)量文字的寬高,調(diào)用這個(gè)方法calcContentWidthHeight()獲取文字的高度,然后設(shè)置文字的寬高,代碼中的注釋寫的很詳細(xì),我們就不再細(xì)說(shuō)了

聲明下style

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>

新建一個(gè)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)
}

接著創(chuàng)建布局文件

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.customview
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import cn.wwj.customview.widget.NodePointProcessBar
/**
 * 節(jié)點(diǎn)進(jìn)度Activity
 */
class NodeProgressBarActivity : AppCompatActivity() {
    /**
     * 數(shù)據(jù)結(jié)合
     */
    private val mTextList: List<String> = mutableListOf("提交申請(qǐng)", "商家處理", "寄回商品", "商家退款", "退款成功")
    /**
     * 正在處理的節(jié)點(diǎn)索引結(jié)合
     */
    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數(shù)據(jù)集合

mProgressIndexSet正在處理的節(jié)點(diǎn)索引結(jié)合,創(chuàng)建Handler對(duì)象模擬調(diào)用網(wǎng)絡(luò)接口,1秒后返回?cái)?shù)據(jù)

節(jié)點(diǎn)全部處理完成.png

項(xiàng)目地址,在customview這模塊下

https://github.com/githubwwj/MyAndroid

以上就是Android新建水平節(jié)點(diǎn)進(jìn)度條示例的詳細(xì)內(nèi)容,更多關(guān)于Android水平節(jié)點(diǎn)進(jìn)度條的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android中方法數(shù)超限問(wèn)題與啟動(dòng)優(yōu)化詳解

    Android中方法數(shù)超限問(wèn)題與啟動(dòng)優(yōu)化詳解

    這篇文章主要給大家介紹了Android中方法數(shù)超限問(wèn)題與啟動(dòng)優(yōu)化的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Android自定義View旋轉(zhuǎn)圓形圖片

    Android自定義View旋轉(zhuǎn)圓形圖片

    這篇文章主要為大家詳細(xì)介紹了Android自定義View旋轉(zhuǎn)圓形圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Android實(shí)現(xiàn)一個(gè)簡(jiǎn)單帶動(dòng)畫的展開(kāi)收起功能

    Android實(shí)現(xiàn)一個(gè)簡(jiǎn)單帶動(dòng)畫的展開(kāi)收起功能

    今天給大家?guī)?lái)一個(gè)展開(kāi)和收起的簡(jiǎn)單效果,如果只是代碼中簡(jiǎn)單設(shè)置顯示或隱藏,熟悉安卓系統(tǒng)的朋友都知道,那一定是閃現(xiàn),所以筆者結(jié)合了動(dòng)畫,使得體驗(yàn)效果瞬間提升一個(gè)檔次,感興趣的小伙伴可以自己動(dòng)手試一試
    2023-08-08
  • Android 圖片特效如何實(shí)現(xiàn)及總結(jié)

    Android 圖片特效如何實(shí)現(xiàn)及總結(jié)

    這篇文章主要介紹了Android 圖形特效如何實(shí)現(xiàn)及總結(jié)的相關(guān)資料,這里對(duì)Android圖像特效的實(shí)現(xiàn)比如:旋轉(zhuǎn),放大,縮小,傾斜等,需要的朋友可以參考下
    2016-12-12
  • Android ViewPager實(shí)現(xiàn)輪播圖效果

    Android ViewPager實(shí)現(xiàn)輪播圖效果

    這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)輪播圖效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Android中ViewPager實(shí)現(xiàn)滑動(dòng)指示條及與Fragment的配合

    Android中ViewPager實(shí)現(xiàn)滑動(dòng)指示條及與Fragment的配合

    這篇文章主要介紹了Android中ViewPager實(shí)現(xiàn)滑動(dòng)指示條及與Fragment的配合,使用Fragment實(shí)現(xiàn)ViewPager的滑動(dòng)是一種比較推薦的做法,需要的朋友可以參考下
    2016-03-03
  • Android Listview滑動(dòng)時(shí)不加載數(shù)據(jù) 停止時(shí)加載數(shù)據(jù)

    Android Listview滑動(dòng)時(shí)不加載數(shù)據(jù) 停止時(shí)加載數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了Android Listview滑動(dòng)時(shí)不加載數(shù)據(jù),停止時(shí)加載數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • flutter?Bloc?實(shí)現(xiàn)原理示例解析

    flutter?Bloc?實(shí)現(xiàn)原理示例解析

    這篇文章主要為大家介紹了flutter?Bloc實(shí)現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Android編程之SharedPreferences文件存儲(chǔ)操作實(shí)例分析

    Android編程之SharedPreferences文件存儲(chǔ)操作實(shí)例分析

    這篇文章主要介紹了Android編程之SharedPreferences文件存儲(chǔ)操作方法,實(shí)例分析了SharedPreferences文件操作的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • Android 單雙擊實(shí)現(xiàn)的方法步驟

    Android 單雙擊實(shí)現(xiàn)的方法步驟

    這篇文章主要介紹了Android 單雙擊實(shí)現(xiàn)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01

最新評(píng)論

阿克陶县| 石河子市| 清丰县| 南漳县| 莲花县| 长白| 南岸区| 固始县| 芷江| 中宁县| 天台县| 甘肃省| 长子县| 五原县| 沈丘县| 永寿县| 江山市| 平舆县| 郑州市| 邹平县| 射洪县| 九龙坡区| 修文县| 时尚| 清河县| 迁西县| 维西| 中牟县| 涟水县| 伊春市| 博罗县| 中超| 桂平市| 沙湾县| 开远市| 高州市| 犍为县| 汨罗市| 沙湾县| 共和县| 天全县|