一、前言

巩固自定义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. 关于SaaS的图

  2. k8s入门_RC、RS、Deployment

    RC 什么是RC: Replication Controller(副本控制器),RC能够保证pod在任意时间运行的副本数量,能够保证pod总是可用的. RC控制的pod的多个副本,每个副本都有独立的i ...

  3. 第14章 Windows管理规范

    第14章 Windows管理规范 我们一直期望但是又害怕写这一章.Windows管理规范(Windows Management Instrumentation,WMI)可能是微软提供给管理员使用最优秀 ...

  4. Python学习笔记调试之取得反向跟踪的字符串

    随笔记录方便自己和同路人查阅. #------------------------------------------------我是可耻的分割线--------------------------- ...

  5. javaSE-验证码生成

    一.使用Math类的radom() 方法 //生成验证码 String verifcationCode = ""; for (int i = 0; i <= 5; i++) ...

  6. Alfred 好用工具分享

    好用的mac工具分享 1.带历史记录的剪切板 根据快捷键切换选择保存的数据 2.创建热键工作流程 将常用的工具变为热键,快捷切换 如何创建: 设置热键 单击右键 创建actions 打开app 然后将 ...

  7. 使用easypoi 最原始的代码进行导出Excel

    首先,产品有需求,我们苦逼的程序员就得把需求实现.那么今天咱就把产品提的导出Excel的需求给他搞定.他的需求是这样的,很简单的Excel导出.样式如图所示:. 其实我们项目中的ExcelUtils工 ...

  8. RDD编程

    一.词频统计 1.读文本文件生成RDD lines 2.将一行一行的文本分割成单词 words flatmap() 3.全部转换为小写 lower() 4.去掉长度小于3的单词 filter() 5. ...

  9. if __name__ == '__main__':中的语句无法执行

    在pycarm中我们用了pytest或unittest框架写测试用例,我们如果我们在最后加上if name == 'main':,如以下代码所示.最后我们右键点击运行的时候是不会执行**if name ...

  10. Flask----常用路由系统及自定义路由系统

    @app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<f ...