Android自定义控件之拖动条

本文介绍如何在Android中创建一个自定义控件,该控件表现为一条带有可拖动小圆的进度条。通过重写View的onMeasure()、onDraw()方法以及添加触摸事件监听,实现拖动改变进度的功能。详细步骤包括设置控件属性,绘制控件,以及处理触摸事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先奉上控件的截图,楼主的手机是小米5,。


下面说一下思路:

这个控件的主体就是一条细长的线,线的两端为圆形,中间有一个彩色的小圆指示当前进度,随着下面的seekbar的拖动或者手指的触摸,小圆的位置和控件的颜色会发生变化。

弄清楚了思路,就知道接下来该怎么办了,绘制这个控件还是很简单的,大概分为以下几步:

1、设置自定义控件的相关属性;

2、编写自定义控件类,继承android.view.View类,然后分别重写onMeasure()、onDraw()方法;

3、为控件添加触摸事件,这里需要在重写的onTouchEvent()方法中计算手指触摸的坐标,根据坐标的x、y值来确定是否触摸了控件;然后定义一个内部监听器,用于监听手指触摸事件,从而得到自定义控件的当前进度值。

代码如下:

1、attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="progressBar">
        <attr name="backgroundColor" format="color" />
        <attr name="startFillColor" format="color" />
        <attr name="middleFillColor" format="color" />
        <attr name="endFillColor" format="color" />
        <attr name="progressPointColor" format="color" />
        <attr name="lineWidth" format="dimension" />
    </declare-styleable>

</resources>

2、MySeekBar.java

package com.ctgu.horizontalprogressbarwithnotext;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

/**
 * 水平渐变色拖动条
 * 
 */
public class MySeekBar extends View
{
	/**
	 * 进度条填充起始色
	 */
	protected int startFillColor;

	/**
	 * 进度条填充中间色
	 */
	protected int middleFillColor;

	/**
	 * 进度条填充结束颜色
	 */
	protected int endFillColor;

	/**
	 * 指示圆点颜色
	 */
	private int progressPointColor;

	/**
	 * 进度条未填充颜色
	 */
	protected int backgroundColor;

	/**
	 * 进度条长度
	 */
	protected float progressWidth;

	/**
	 * 进度条的高度,包括进度条的高度和上下的间隔
	 */
	private float progressHeight;

	/**
	 * 进度条的高度,不包括上下的间隔
	 */
	private float lineWidth;

	/**
	 * 进度条左侧间隔
	 */
	private float widthSpace;

	/**
	 * 进度条中心与上方的间隔
	 */
	private float heightSpace;

	/**
	 * 內圆的半径
	 */
	private float radius;

	/**
	 * 按比例计算各部分的值
	 */
	private float unit;

	/**
	 * 整个控件宽度
	 */
	protected int viewWidth;

	/**
	 * 整个控件高度
	 */
	protected int viewHeight;

	/**
	 * 渐变色数组
	 */
	private int colorArray[];

	/**
	 * 画笔
	 */
	protected Paint paint;

	/**
	 * 控件当前的进度
	 */
	private float progress = 0;

	/**
	 * 处理颜色渐变
	 */
	private LinearGradient shader;

	/**
	 * 自定义的监听器,负责监听控件的触摸事件
	 */
	private ProgressChangedListener listener;

	/**
	 * @param context
	 *            上下文
	 */
	public MySeekBar(Context context)
	{
		this(context, null);
	}

	/**
	 * @param context上下文
	 * @param attrs
	 *            自定义的属性
	 */
	public MySeekBar(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);
	}

	/**
	 * @param context
	 *            上下文
	 * @param attrs
	 *            自定义的属性
	 * @param defStyleAttr
	 *            自定义的风格
	 */
	public MySeekBar(Context context, AttributeSet attrs, int defStyleAttr)
	{
		super(context, attrs, defStyleAttr);

		colorArray = new int[3];
		paint = new Paint();

		TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.progressBar);
		int n = ta.getIndexCount();
		for (int i = 0; i < n; i++)
		{
			int attr = ta.getIndex(i);
			switch (attr)
			{
				case R.styleable.progressBar_startFillColor:
					colorArray[0] = ta.getColor(R.styleable.progressBar_startFillColor, 0xffff0000);
					break;
				case R.styleable.progressBar_middleFillColor:
					colorArray[1] = ta.getColor(R.styleable.progressBar_middleFillColor, 0xffff0000);
					break;
				case R.styleable.progressBar_endFillColor:
					colorArray[2] = ta.getColor(R.styleable.progressBar_endFillColor, 0xffff0000);
					break;
				case R.styleable.progressBar_backgroundColor:
					backgroundColor = ta.getColor(R.styleable.progressBar_backgroundColor, Color.GRAY);
					break;
				case R.styleable.progressBar_progressPointColor:
					progressPointColor = ta.getColor(R.styleable.progressBar_progressPointColor, Color.WHITE);
					break;
				case R.styleable.progressBar_lineWidth:
					lineWidth = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
							TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
					break;
			}
		}
		ta.recycle();
	}

	/**
	 * 测量组件高度和宽度
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		int width = 0;
		int height = 0;

		// 设置宽度
		// MeasureSpec.getMode()会得到三个int类型的值分别为:MeasureSpec.EXACTLY
		// MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED。
		// MeasureSpec.UNSPECIFIED 未指定,所以可以设置任意大小。
		// MeasureSpec.AT_MOST MeasureExampleView可以为任意大小,但是有一个上限。
		int specMode = MeasureSpec.getMode(widthMeasureSpec);
		int specSize = MeasureSpec.getSize(widthMeasureSpec); // 会解析MeasureSpec值得到父容器width或者height。
		switch (specMode)
		{
			case MeasureSpec.EXACTLY: // 明确指定了
				width = specSize;
				break;
			case MeasureSpec.AT_MOST: // 一般为wrap_content
				width = getPaddingLeft() + getPaddingRight();
				break;
		}

		// 设置高度
		specMode = MeasureSpec.getMode(heightMeasureSpec);
		specSize = MeasureSpec.getSize(heightMeasureSpec);
		switch (specMode)
		{
			case MeasureSpec.EXACTLY:
				height = specSize;
				break;
			case MeasureSpec.AT_MOST:
				height = width / 10;
				break;
		}
		// Logger.d("onMeasure():width=" + width + ",height=" + height);
		setMeasuredDimension(width, height);

		viewWidth = getMeasuredWidth(); // 获得视图的宽度
		viewHeight = getMeasuredHeight(); // 获得视图的高度
		unit = Math.min((float) viewWidth / 300, (float) viewHeight / 30); // 按比例计算各部分的值

		progressWidth = 280 * unit; // 进度条的长度
		progressHeight = 30 * unit; // 进度条的高度,包括上下间隔

		widthSpace = 8 * unit; // 进度条左侧间隔
		heightSpace = progressHeight / 2; // float heightSpace 进度条中心与上方的间隔

		radius = lineWidth / 2; // 內圆的半径
	}

	@Override
	public void onDraw(Canvas canvas)
	{
		float percent = ((float) progress) / 100; // 当前进度的百分比
		if (percent > 1)
		{
			percent = 1;
		}

		// 画灰色的底线
		paint.setAntiAlias(true);
		paint.setDither(true);
		paint.setColor(backgroundColor); // 设置颜色为灰色
		paint.setStrokeWidth(lineWidth); // 设置线的宽度
		paint.setStrokeCap(Paint.Cap.ROUND); // 设置线为圆角
		canvas.drawLine(widthSpace, heightSpace, widthSpace + progressWidth, heightSpace, paint); // 画灰色的底线

		// 画渐变色横线
		shader = new LinearGradient(0, 0, widthSpace + progressWidth, 0, colorArray, null,
				Shader.TileMode.CLAMP);
		paint.setShader(shader);// 设置渐变色
		canvas.drawLine(widthSpace, heightSpace, widthSpace + percent * progressWidth, heightSpace, paint); // 画渐变色进度线条

		// 画圆
		paint.setShader(null);
		paint.setColor(progressPointColor);
		paint.setStrokeWidth(2);
		paint.setStyle(Paint.Style.FILL); // 设置绘画方式为填充
		canvas.drawCircle(widthSpace + percent * progressWidth, heightSpace, radius, paint); // 绘制圆
	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		int action = event.getAction();
		switch (action)
		{
			case MotionEvent.ACTION_DOWN:
			case MotionEvent.ACTION_MOVE:
				float x = event.getX();
				float y = event.getY();

				if ((x >= (0 - radius)) && (x <= (progressWidth + widthSpace + radius)))
				{
					if (y >= 0 && y <= progressHeight)
					{
						this.progress = x * 100.0f / (progressWidth + widthSpace); // 这里要重新计算进度值
						invalidate(); // 重绘界面
						if (listener != null)
						{
							listener.onProgressChanged(progress); // 在处理触摸事件时传入重新计算过的进度参数,
						}
					}
				}
				return true;
			case MotionEvent.ACTION_UP:
				invalidate(); // 手指抬起时刷新一下页面
				return true;
		}
		return super.onTouchEvent(event);
	}

	public void setOnProgressChangeListener(ProgressChangedListener listener2)
	{
		this.listener = listener2;
	}

	/**
	 * 设置进度条的进度值
	 * 
	 * @param pro
	 *            传入的进度值
	 */
	public void setProgress(float pro)
	{
		if (pro < 0)
		{
			this.progress = 0;
		}
		else if (pro > 100)
		{
			this.progress = 100;
		}
		else
		{
			this.progress = pro;
		}
		invalidate();
	}

	/**
	 * 设置渐变色数组
	 * 
	 * @param colorArray
	 */
	public void setColorArray(int[] colorArray)
	{
		this.colorArray = colorArray;
	}

	public interface ProgressChangedListener
	{
		public void onProgressChanged(float touchX);
	}
}

3、activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:lh2="http://schemas.android.com/apk/res/com.ctgu.horizontalprogressbarwithnotext"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.ctgu.horizontalprogressbarwithnotext.MySeekBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="50dp"
        lh2:progressPointColor="#EEC591"
        lh2:lineWidth="16dp"
        lh2:backgroundColor="#D8D8D8"
        lh2:endFillColor="#e03060"
        lh2:middleFillColor="#B03060"
        lh2:startFillColor="#505050" />

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/progressBar"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:layout_marginTop="30dp"
        android:padding="20dp" />

</RelativeLayout>

4、MainActivity.java

package com.ctgu.horizontalprogressbarwithnotext;

import android.app.Activity;
import android.os.Bundle;
import android.widget.SeekBar;

import com.ctgu.horizontalprogressbarwithnotext.MySeekBar.ProgressChangedListener;

/**
 * 测试界面
 */
public class MainActivity extends Activity
{
	private MySeekBar bar;
	private SeekBar seekbar;

	// private int colors[] = { Color.RED, Color.GREEN };

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bar = (MySeekBar) findViewById(R.id.progressBar);
		// bar.setColorArray(colors); // 传入一个渐变色数组,更改默认的拖动条样式,是可选项
		bar.setOnProgressChangeListener(new ProgressChangedListener()
		{
			@Override
			public void onProgressChanged(float progress)
			{
				bar.setProgress(progress);
			}
		});

		seekbar = (SeekBar) findViewById(R.id.seekbar);
		seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
		{
			@Override
			public void onStopTrackingTouch(SeekBar seekBar)
			{

			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar)
			{

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
			{
				bar.setProgress(progress);
			}
		});
	}
}

整个项目的代码如下:

demo地址











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值