Android自定义控件NodeProgressBar进度条

在我们的日常开发中,有时候会需要一些特别的控件,这些控件是android自带的控件所不能满足的,所以需要我们自定义实现。
本文就是通过自定义控件来实现可点击设置金额的进度条。
可通过自定义控件获取节点值和金额值。
效果图如下:
Android自定义控件NodeProgressBar进度条_第1张图片
Android自定义控件NodeProgressBar进度条_第2张图片
Android自定义控件NodeProgressBar进度条_第3张图片
自定义控件

package com.example.customnodeprogressbar;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 自定义节点进度条控件,可点击进度条上的节点来触发进度条进度,设置和获得相应数值。
 * 进度条左右两头没有节点
 */
public class NodeProgressBar extends View {
    Paint mPaint;
    RectF mRectF;
    /**
     * 圆点的直径
     */
    float CycleWidth;
    /**
     * 距离左边界的距离
     */
    float border;
    float paintSize = 25;
    float node_progress_top;
    onNodeProgressBarListener onNodeProgressBarListener;

    public NodeProgressBar.onNodeProgressBarListener getOnNodeProgressBarListener() {
        return onNodeProgressBarListener;
    }

    public void setOnNodeProgressBarListener(NodeProgressBar.onNodeProgressBarListener onNodeProgressBarListener) {
        this.onNodeProgressBarListener = onNodeProgressBarListener;
    }

    public NodeProgressBar(Context context) {
        super(context);
        InitPaint(context, null);
    }

    public NodeProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        InitPaint(context, attrs);
    }

    /**
     * 初始化数据
     */
    private void InitPaint(Context context, AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mRectF = new RectF();

        try {
            CycleWidth = getResources().getDimension(R.dimen.node_progress_cycle_width);
        } catch (Exception e) {
            CycleWidth = 15;
        }

        try {
            border = getResources().getDimension(R.dimen.node_progress_border_width);
        } catch (Exception e) {
            border = 25;
        }

        try {
            paintSize = getResources().getDimension(R.dimen.node_progress_textsize);
        } catch (Exception e) {
            paintSize = 16;
        }

        try {
            node_progress_top = getResources().getDimension(R.dimen.node_progress_top);
        } catch (Exception e) {
            node_progress_top = 6;
        }

        mPaint.setTextSize(paintSize);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        DrawBackgroundLine(canvas, 0xFF4dd0c8, 0xFFdddddd);
        DrawProgress(canvas, 0xFF4dd0c8, 0xFFdddddd);
        DrawText(canvas, 0xFF4dd0c8, 0xFFdddddd); //ff8b02
    }

    private void DrawText(Canvas canvas, int currentColor, int color) {

    }


    /**
     * 画进度条的线(进度条分为背景线和进度线)
     *
     * @param currentColor 进度条进度的颜色
     * @param color        进度条背景的颜色
     */
    private void DrawBackgroundLine(Canvas canvas, int currentColor, int color) {
        mPaint.setColor(color);//设置画笔颜色
        mPaint.setStyle(Paint.Style.FILL);//设置画笔为实心
        mPaint.setStrokeWidth(CycleWidth);//设置画笔的空心线宽(实心无效)

        float left = CycleWidth + border;
        mRectF.left = left;
        mRectF.top = CycleWidth / 3 + node_progress_top;
        mRectF.right = left + (getWidth() - left * 2) / (max - 1) * (current - 1);
        mRectF.bottom = CycleWidth / 3 * 2 + node_progress_top;

        mPaint.setColor(currentColor);
        //canvas.drawRect(mRectF, mPaint);
        canvas.drawRoundRect(mRectF, 10, 10, mPaint);//画进度条左侧的进度线(圆角的长方形)

        mRectF.left = mRectF.right;
        mRectF.right = getWidth() - left;
        mPaint.setColor(color);

        //canvas.drawRect(mRectF, mPaint);
        canvas.drawRoundRect(mRectF, 10, 10, mPaint);//画进度条右侧的背景线
    }

    /**
     * 画进度条上的圆和下面的文字
     *
     * @param currentColor 进度条进度的颜色
     * @param color        进度条背景的颜色
     */
    private void DrawProgress(Canvas canvas, int currentColor, int color) {
        float current_border = CycleWidth + border;
        mPaint.setTextAlign(Paint.Align.CENTER);
        for (int i = 1; i < max - 1; i++) {//第一个和最后一个圆点、文字不画
            if (i < current) {
                mPaint.setColor(currentColor);
            } else {
                mPaint.setColor(color);
            }
            float left = current_border + (getWidth() - current_border * 2) / (max - 1) * i;
            canvas.drawCircle(left, CycleWidth / 2 + node_progress_top, CycleWidth / 2, mPaint);//画进度条上的圆
            if (i < current) {
                mPaint.setColor(currentColor);
            } else {
                mPaint.setColor(0xFF999999);
            }
            if (moneys != null) {
                canvas.drawText(moneys[i] + "元", left, CycleWidth * 2 + node_progress_top, mPaint);//画进度条上的字
            } else {
//                canvas.drawText(i * 5 + "元", left, CycleWidth * 2/* + paintSize */ + node_progress_top, mPaint);
            }
        }
    }


    /**
     * 金额数组
     */
    int[] moneys = {0, 50, 200, 400, 600, 800, 1000, 0};
    int max = moneys.length;//数组的长短

    int current = 2;//进度条现在的位置,画图用的
    int nodeNum = 1;//默认节点
    int money = 50;//默认金额

    private int getCurrent() {
        return current;
    }

    /**
     * 设置进度条现在位置
     */
    private void setCurrent(int current) {
        if (current <= 1) {
            current = 2;
            nodeNum = 1;
        } else if (current >= max - 1) {//当点击最后一个点时,把进度条进度画满
            current = max;
            nodeNum = max - 2;
        } else {
            this.nodeNum = current - 1;
        }
        this.money = moneys[this.nodeNum];
        this.current = current;
        this.postInvalidate();
    }

    int currentX = 0;

    private int getNodeNum() {
        return nodeNum;
    }

    /**
     * 设置当前节点
     *
     * @param nodeNum
     */
    public void setNodeNum(int nodeNum) {
        this.nodeNum = nodeNum;
        if (nodeNum <= 1) {
            current = 2;
        } else if (nodeNum >= max - 2) {
            current = max;
        } else {
            current = nodeNum + 1;
        }
        setCurrent(current);
    }

    /**
     * 触屏时会触发点击事件
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                int current_border = (int) (CycleWidth + border);
                //
                currentX = (int) (event.getX() - current_border);
                int itemWidth = (getWidth() - current_border * 2) / ((max - 1));
                int select = currentX / itemWidth + ((currentX % itemWidth) / (itemWidth / 2) > 0 ? 1 : 0) + 1;
                setCurrent(select);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (onNodeProgressBarListener != null) {
                    onNodeProgressBarListener.getCurrentNodeAndMoney(nodeNum, money);
                }
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 获取当前  金额
     *
     * @return
     */
    public int getCurrentMoney() {
        if (this.moneys != null) {
            return this.moneys[nodeNum];
        }
        return (current) * 5;
    }

    /**
     * 设置当前金额
     *
     * @param money 设置金额至进度条(只能是金额数组里存在的金额才有效)
     */
    public void setCurrentMoney(int money) {
        for (int i = 1; i < moneys.length - 1; i++) {
            if (moneys[i] == money) {
                this.nodeNum = i;
                this.money = money;
                break;
            }
        }
        setNodeNum(nodeNum);
    }

    /**
     * 设置金额数组
     *
     * @param moneys 设置金额数组,例:{0, 50, 200, 400, 600, 800, 1000, 0};
     */
    public void setMoneys(int[] moneys) {
        this.moneys = moneys;
        this.max = moneys.length;
        postInvalidate();
    }

    public interface onNodeProgressBarListener {
        void getCurrentNodeAndMoney(int currentNode, int currentMoney);
    }
}

自定义控件的使用,在xml中直接使用。


<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.customnodeprogressbar.NodeProgressBar
        android:id="@+id/porBar_main"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:layout_marginTop="53dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintBottom_toTopOf="@+id/tv_main_show"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_main_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="447dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/porBar_main" />


androidx.constraintlayout.widget.ConstraintLayout>

获取进度条的节点值和金额值并显示。

package com.example.customnodeprogressbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private NodeProgressBar porBarMoney;
    private TextView tvShow;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        porBarMoney = (NodeProgressBar) findViewById(R.id.porBar_main);
        tvShow = (TextView) findViewById(R.id.tv_main_show);
        porBarMoney.setOnNodeProgressBarListener(new NodeProgressBar.onNodeProgressBarListener() {
            @Override
            public void getCurrentNodeAndMoney(int currentNode, int currentMoney) {
                tvShow.setText("currentNode"+currentNode+"---currentMoney"+currentMoney);
            }
        });
    }
}

源码地址:
https://gitee.com/hjqjl/WhDemo

你可能感兴趣的:(android控件)