一、前言

巩固自定义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. 根据pid定时监控CPU使用率和内存使用率并输出到文件 (windows和linux跨平台可用)

    有时服务器运维中,某些程序员的应用发布后完全不管CPU和内存的使用率,只觉得代码能运行就行了,这样给我们运维人员经常造成困扰: 比如我在zabbix平台中就经常监测到凌晨1~3~5点时候突然CPU飙升 ...

  2. CANas分析软件,DBC文件解析,CAN报文分析,仿CANoe曲线显示

    2023.01.01:增加对Kvaser的支持参考了CANoe写了下面的软件,主要用途是对报文的回放及曲线的分析. 1.CAN连接,支持周立功CAN.CANFD及PCAN 2.DBC解析与生成文件 打 ...

  3. 7款WordPress图片分离对象存储插件 含国内主流云服务存储商

    如果我们有用WordPress搭建网站的朋友应该会发现网站文件和数据库其实没有多大,在网站运营几年后数据最大的就是图片.而且图片占用服务器的空间会搬家比较麻烦,而且占用服务求的带宽.我们看到很多的Wo ...

  4. h5项目

    h5项目,用vue3,用vite搭建就好,是一个新的项目. 接口还在开发,可以用mock模拟. 现有信息:接口url,ui-url,原型url(各部分的交互关系)

  5. SpringCloud-Hoxton.SR1-config整合

    1.前一篇讲到了整合eureka和gateway,实现了服务的发现与注册以及网关的统一入口,这一篇在此基础上整合分布式配置中心config,首先新建一个子项目config-services作为服务端, ...

  6. Springboot实现文件上传下载功能

    一.文件上传 1. controller层 @RestController @RequestMapping(value = "/excel") public class Uploa ...

  7. TypeScript 元组

    TypeScript 元组 我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组. 元组中允许存储不同类型的元素,元组可以作为参数传 ...

  8. Android studio学习第一期

    下载工具 Android studio 参考博客https://blog.csdn.net/weixin_45406151/article/details/114531103 汉化完成 并创建了虚拟手 ...

  9. Linux-centos7 下开放某个端口

    方式一 1.开启防火墙 systemctl start firewalld  2.开放指定端口 firewall-cmd --zone=public --add-port=1935/tcp --per ...

  10. Taro3 扫描不同二维码参数不同,但是热启动之后参数不变 根据环境不同更换域名

    热启动:先执行缓存的静态数据,然后再执行页面代码.比如右上角退出或者按home键错误用法:用的Taro3 react function函数,之前用的  Taro.getLaunchOptionsSyn ...