在我们的日常开发中,有时候会需要一些特别的控件,这些控件是android自带的控件所不能满足的,所以需要我们自定义实现。
本文就是通过自定义控件来实现可点击设置金额的进度条。
可通过自定义控件获取节点值和金额值。
效果图如下:
自定义控件
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