一、前言

巩固自定义view基础用,本次尝试构建一个拖动条组件。代码参考于 https://github.com/woxingxiao/BubbleSeekBar ,精简其中高度可重用的部分,仅保留基本的拖拽功能,由于代码很巧妙,以后可以再深入探究学习。

本文在前面自定义view的基础上,增加了测量(onMeasure) 以及 触碰屏幕事件(onTouchEvent)。相信可以一步步踏实巩固,学会自定义view的知识。由于本拖动条仍是一个view,不需要涉及到布局(onLayout) 。以后学习自定义viewGroup时再另行探究。老规矩第一章先放效果图和全部代码。

二、效果图

三、代码

values/attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MySeekBar"> <attr name="progress_max" format="float|reference"/> <!-- 进度条最大值 -->
<attr name="progress_min" format="float|reference"/> <!-- 进度条最小值 -->
<attr name="progress_default" format="float|reference"/> <!-- 进度条默认值 --> <attr name="track_left_height" format="dimension|reference"/> <!-- 进度条左边高度 -->
<attr name="track_right_height" format="dimension|reference"/> <!-- 进度条右边高度 --> <attr name="track_left_color" format="color|reference"/> <!-- 进度条左边颜色 -->
<attr name="track_right_color" format="color|reference"/> <!-- 进度条右边颜色 --> <attr name="thumb_color_default" format="color|reference"/> <!-- 拖动滑块默认颜色 -->
<attr name="thumb_radius_default" format="dimension|reference"/> <!-- 拖动滑块半径 --> <attr name="thumb_color_on_dragging" format="color|reference"/> <!-- 拖动滑块拖动中颜色 -->
<attr name="thumb_radius_on_dragging" format="dimension|reference"/> <!-- 拖动滑块拖动中半径 --> </declare-styleable>
</resources>


MySeekBar.java

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator; import com.kms.myseekbar.util.DimensionUtil; import androidx.annotation.Nullable; public class MySeekBar extends View { /********************** 参数 **********************/ private float progress_max; // 进度条最大值
private float progress_min; // 进度条最小值
private float progress_default; // 进度条默认值 private int track_left_height; // 进度条左边高度
private int track_right_height; // 进度条右边高度 private int track_left_color; // 进度条左边颜色
private int track_right_color; // 进度条右边颜色 private int thumb_color_default; // 拖动滑块默认颜色
private int thumb_radius_default; // 拖动滑块半径 private int thumb_color_on_dragging; // 拖动滑块拖动中颜色
private int thumb_radius_on_dragging; // 拖动滑块拖动中半径 /********************** 绘制相关 **********************/ private Paint paint; // 画笔
private int xLeft; // 实际的绘图区域按距离父布局左边 padding 算起
private int xRight; // 到距离父布局右边的的 padding 结束
private int yCenter; // 确定绘制进度条Y轴意义上的中点 private int thumb_radius; // 滑动滑块半径 /********************** 构造函数 **********************/ public MySeekBar(Context context) {
this(context, null);
} public MySeekBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public MySeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySeekBar, defStyleAttr, 0); this.progress_max = typedArray.getFloat(R.styleable.MySeekBar_progress_max, 100);
this.progress_min = typedArray.getFloat(R.styleable.MySeekBar_progress_min, 0);
this.progress_default = typedArray.getFloat(R.styleable.MySeekBar_progress_default, progress_min); this.track_left_height = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_track_left_height, DimensionUtil.dp2px(8));
this.track_right_height = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_track_right_height, track_left_height - DimensionUtil.dp2px(2)); this.track_left_color = typedArray.getColor(R.styleable.MySeekBar_track_left_color, Color.BLUE);
this.track_right_color = typedArray.getColor(R.styleable.MySeekBar_track_right_color, Color.LTGRAY); this.thumb_color_default = typedArray.getColor(R.styleable.MySeekBar_thumb_color_default, track_left_color);
this.thumb_radius_default = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_thumb_radius_default, track_left_height + DimensionUtil.dp2px(2)); this.thumb_color_on_dragging = typedArray.getColor(R.styleable.MySeekBar_thumb_color_on_dragging, thumb_color_default);
this.thumb_radius_on_dragging = typedArray.getDimensionPixelSize(R.styleable.MySeekBar_thumb_radius_on_dragging, thumb_radius_default + DimensionUtil.dp2px(2)); typedArray.recycle(); thumb_radius = thumb_radius_default; initPaint(); // 初始化画笔
} /********************** Getter and Setter **********************/ public float getProgressMax() {
return progress_max;
} public MySeekBar setProgressMax(float progressMax) {
this.progress_max = progressMax;
return this;
} public float getProgressMin() {
return progress_min;
} public MySeekBar setProgressMin(float progressMin) {
this.progress_min = progressMin;
return this;
} public float getProgressDefault() {
return progress_default;
} public MySeekBar setProgressDefault(float progressDefault) {
this.progress_default = progressDefault;
return this;
} public int getTrackLeftHeight() {
return track_left_height;
} public MySeekBar setTrackLeftHeight(int trackLeftHeight) {
this.track_left_height = trackLeftHeight;
return this;
} public int getTrackRightHeight() {
return track_right_height;
} public MySeekBar setTrackRightHeight(int trackRightHeight) {
this.track_right_height = trackRightHeight;
return this;
} public int getTrackLeftColor() {
return track_left_color;
} public MySeekBar setTrackLeftColor(int trackLeftColor) {
this.track_left_color = trackLeftColor;
return this;
} public int getTrackRightColor() {
return track_right_color;
} public MySeekBar setTrackRightColor(int trackRightColor) {
this.track_right_color = trackRightColor;
return this;
} public int getThumbColorDefault() {
return thumb_color_default;
} public MySeekBar setThumbColorDefault(int thumbColorDefault) {
this.thumb_color_default = thumbColorDefault;
return this;
} public int getThumbRadiusDefault() {
return thumb_radius_default;
} public MySeekBar setThumbRadiusDefault(int thumbRadiusDefault) {
this.thumb_radius_default = thumbRadiusDefault;
return this;
} public int getThumbColorOnDragging() {
return thumb_color_on_dragging;
} public MySeekBar setThumbColorOnDragging(int thumbColorOnDragging) {
this.thumb_color_on_dragging = thumbColorOnDragging;
return this;
} public int getThumbRadiusOnDragging() {
return thumb_radius_on_dragging;
} public MySeekBar setThumbRadiusOnDragging(int thumbRadiusOnDragging) {
this.thumb_radius_on_dragging = thumbRadiusOnDragging;
return this;
} /********************** 绘制相关 **********************/ private void initPaint(){
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 仅当 android_layout_width = wrap_content 或未指定时生效,若测出来的size大于你所指定的size (譬如这里是180dp),则使用所指定的size
int width = resolveSize(DimensionUtil.dp2px(180), widthMeasureSpec);
int height = thumb_radius_on_dragging * 2; // 控件高度按拖动时的滑块直径
setMeasuredDimension(width, height); // 强制指定控件大小 xLeft = getPaddingLeft() + thumb_radius_on_dragging; // 实际的绘图区域按距离父布局左边 padding 算起
xRight = getMeasuredWidth() - getPaddingRight() - thumb_radius_on_dragging; // 到距离父布局右边的的 padding 结束
yCenter = getPaddingTop() + thumb_radius_on_dragging; // 确定绘制进度条Y轴意义上的中点
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int currentProgress = (int) (progress_default / progress_max * (xRight-xLeft));
drawRightTrack(canvas, currentProgress);
drawLeftTrack(canvas, currentProgress);
drawThumb(canvas, currentProgress);
} /**
* 绘制进度条左边
*/
private void drawLeftTrack(Canvas canvas, int currentProgress){
paint.setColor(track_left_color);
paint.setStrokeWidth(track_left_height);
canvas.drawLine(xLeft, yCenter, xLeft + currentProgress, yCenter, paint);
} /**
* 绘制拖动滑块
*/
private void drawThumb(Canvas canvas, int currentProgress){
paint.setColor(thumb_color_default);
canvas.drawCircle(xLeft + currentProgress, yCenter, thumb_radius, paint);
} /**
* 绘制进度条右边
*/
private void drawRightTrack(Canvas canvas, int currentProgress){
paint.setColor(track_right_color);
paint.setStrokeWidth(track_right_height);
canvas.drawLine(xLeft + currentProgress, yCenter, xRight, yCenter, paint);
} public void startAnim(){
final ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setDuration(100*500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
progress_default = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
} @Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
performClick(); // 若 SeekBar设置了 OnClickListener,可以在此处唤醒监听器
getParent().requestDisallowInterceptTouchEvent(true); // 不允许父组件拦截触摸事件
thumb_radius = thumb_radius_on_dragging;
progress_default = calculateDraggingX(event.getX());
break;
default:
thumb_radius = thumb_radius_default;
}
invalidate();
return true;
} /**
* 计算拖动值
*
* @param x 屏幕上的event.getX()
* @return 经转换后对应拖动条的进度值
*/
private float calculateDraggingX(float x){
if(x < xLeft){
return progress_min;
}
if(x > xRight){
return progress_max;
}
return x / getMeasuredWidth() * progress_max;
}
}

util/DimensionUtil.java

import android.content.res.Resources;
import android.util.TypedValue; public class DimensionUtil { public static int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
Resources.getSystem().getDisplayMetrics());
} public static int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
Resources.getSystem().getDisplayMetrics());
} }

layout/activity_main.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:padding="10dp"> <com.kms.myseekbar.MySeekBar
android:id="@+id/my_seek_bar"
android:layout_width="match_parent"
android:layout_height="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

import android.graphics.Color;
import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { MySeekBar mySeekBar; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mySeekBar = findViewById(R.id.my_seek_bar);
mySeekBar.setThumbColorDefault(Color.BLUE).setProgressDefault(50);
// mySeekBar.startAnim();
}
}

完!下一节将讲解下基本思路

Android 自定义SeekBar (一)的更多相关文章

  1. Android自定义Seekbar拖动条式样

    SeekBar拖动条可以由用户控制,进行拖动操作.比如,应用程序中用户需要对音量进行控制,就可以使用拖动条来实现. 1.SeekBar控件的使用 1.1SeekBar常用属性 SeekBar的常用属性 ...

  2. Android 自定义seekbar中,thumb被覆盖掉一部分问题

    (图一)  (图二)    (图三) 做一个自定义的seekbar,更改其背景图片: <com.android.Progress android:id="@+id/focus_seek ...

  3. Android自定义Seekbar滑动条,Pop提示跟随滑动按钮一起滑动

    由于项目需要做出此效果,自定义写了一个. 效果图 思路: 原始的seekbar只有滑动条并没有下方的提示文字,所以我们必须要继承Seekbar重写这个控件. 代码: 在values文件夹下新建attr ...

  4. android自定义seekBar

    Android原生控件只有横向进度条一种,而且没法变换样式,比如原生rom的样子 很丑是吧,当伟大的产品设计要求更换前背景,甚至纵向,甚至圆弧状的,咋办,比如 ok,我们开始吧: 一)变换前背景 先来 ...

  5. Android 自定义带刻度的seekbar

    自定义带刻度的seekbar 1.布局 <span style="font-family:SimHei;font-size:18px;"><com.imibaby ...

  6. Android 开发之网易云音乐(或QQ音乐)的播放界面转盘和自定义SeekBar的实现

    这个东西我在eoeAndroid上首发的,但没有详细的实现说明:http://www.eoeandroid.com/thread-317901-1-1.html 在csdn上进行详细的说明吧.(同时上 ...

  7. Android简易实战教程--第三十四话《 自定义SeekBar以及里面的一些小知识》

    转载本专栏文章,请注明出处尊重原创:博客地址http://blog.csdn.net/qq_32059827/article/details/52849676:小杨的博客 许多应用可能需要加入进度,例 ...

  8. 我的Android进阶之旅------>Android如何通过自定义SeekBar来实现视频播放进度条

    首先来看一下效果图,如下所示: 其中进度条如下: 接下来说一说我的思路,上面的进度拖动条有自定义的Thumb,在Thumb正上方有一个PopupWindow窗口,窗口里面显示当前的播放时间.在Seek ...

  9. 自定义SeekBar的使用

    一.seekbar是进度条,可以使用系统的,也可以自己定义,下面我们将自己定义一个seekbar. 1.自定义滑条,包括对背景,第一进度,第二进度的设置,通过一个xml来实现,在drawable下创建 ...

  10. Android使用SeekBar时动态显示进度且随SeekBar一起移动

    最近有做一个android项目,里面有使用到在播放视频时可以跳播,同时动态显示播放时间.类似于下图 的效果,我只是抽取其中的一部分做展示,刚接到这个事时也是在网上一通找,最后没找到!而且还碰到有些朋友 ...

随机推荐

  1. zookeeper 选举流程源码解析

    在开始之前,我们先了解一下zookeeper集群角色,zookeeper中存在leader,follower, observer这么几个角色, leader, follower, 就类似与mysql ...

  2. STP协议-基础

    生成树协议 一 .技术背景一个缺乏冗余性设计的网络:任何一个网络节点出现故障,会造成单链路故障.单设备故障,使整个网络瘫痪. 引入冗余性的同时也引入了二层环路:网络的冗余性增强了,但是却出现了二层环路 ...

  3. Linux下hadoop和spark的基础环境配置准备

    我使用了4台虚拟机centos7来搭建环境, 2个主结点(一般是一个,但为了体验zookeeper,万一其中一个master挂掉呢,另外一个会自动启动接管), 2个从结点 注意:下面的配置主要以主节点 ...

  4. 3阶(次)贝塞尔曲线的JavaScript(JS)实现

    php中文网数学符号的显示太差了,推荐看这里 贝塞尔曲线简介:贝塞尔曲线,是贝塞尔老爷子在使用电子计算机设计汽车零件的时候 进行曲面设计而采用的一种参数化的样条曲线. 一般参数方程: B(t) = \ ...

  5. ideavimrc 示例

    我自己的idea vim配置,用熟悉了以后还真的挺方便的 比较常用的有 ManageRecentProjects,快速切换多个project,经常会遇到同时打开多个project,来回切换方便多了 H ...

  6. win7安装AutoCAD2019

    1.Win7专业版64位先安装SP1补丁 2.根证书下载 MicrosoftRootCertificateAuthority2011.cer 链接:http://go.microsoft.com/fw ...

  7. Demo of canvas, canvas optimization and svg

    It used the canvas to draw the curves in the old project, and the client felt that it was vague, so ...

  8. win10系统下mysql安装

    1.官网下载压缩包 2.添加环境变量到PATH 3.在bin的同级目录下,新增my.ini,内容如下(严重注意,是斜杠,千万别写成反斜杠!!!): [mysqld] basedir = D://sof ...

  9. sshpass免密登录源码剖析

    源码下载地址:https://sourceforge.net/projects/sshpass/ 免密登陆程序sshpass源码解析,短小精悍的程序,非常值得学习!

  10. password_encryption_type 和 pg_hba.conf 不匹配导致用户连不上

    # 问题概述xxx客户新上一套opengauss数据库,在测试中用户输入正确的密码,提示用户密码错误,导致用户被锁# 问题原因password_encryption_type 和 pg_hba.con ...