需求
- 实现垂直摆放
- 根据容器大小均匀等分刻度
- 实现长按监听
思路
- 将原本水平的 SeekBar 垂直摆放
- 添加长按接口
- 根据等分的份数计算每份的长度
- 使用 Paint 逐个画线
实现
- 因为有份数是变动的,我们需要自定义属性
- 自定义 View
1. 自定义属性
我们将份数定义为 tick_mark_count ,整形
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="tick_count">
<attr name="tick_mark_count" format="integer" />
</declare-styleable>
</resources>
2.支持长按的 VerticalSeekBar
原生的 SeekBar 有坑,即使调用 setOnLongClickListener 也无效,可以自己动手实验一下。So,我们需要自己定义长按接口
package com.feng.launcher.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
/**
* Created by Administrator on 2018/5/28.
*/
// 支持长按事件垂直摆放的 SeekBar
// VerticalSeekBar have longclick behavior
public class VerticalSeekBar extends SeekBar {
private Drawable mThumb;
private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
private LongClickListener longClickListener;
private long downTime;
private boolean hasLongTouch = false;//是否已经执行长按操作
private boolean possibleLongTouch = true;//可能是长按
private float lastY;
private float lastX;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1 && !hasLongTouch) {
hasLongTouch = true;
if (longClickListener != null) longClickListener.onLongClick();
}
}
};
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener l) {
mOnSeekBarChangeListener = l;
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
void onProgressRefresh(float scale, boolean fromUser) {
Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
invalidate();
}
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
}
}
private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
int available = w - getPaddingLeft() - getPaddingRight();
int thumbWidth = thumb.getIntrinsicWidth();
int thumbHeight = thumb.getIntrinsicHeight();
int thumbPos = (int) (scale * available + 0.5f);
int topBound, bottomBound;
if (gap == Integer.MIN_VALUE) {
Rect oldBounds = thumb.getBounds();
topBound = oldBounds.top;
bottomBound = oldBounds.bottom;
} else {
topBound = gap;
bottomBound = gap + thumbHeight;
}
thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
}
public void setThumb(Drawable thumb) {
mThumb = thumb;
super.setThumb(thumb);
}
void onStartTrackingTouch() {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
}
void onStopTrackingTouch() {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
}
private void attemptClaimDrag() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
float x = event.getX();
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setPressed(true);
onStartTrackingTouch();
hasLongTouch = false;// 是否已经执行过长按事件
possibleLongTouch = true;//是否可能为长按模式
lastY = y;
lastX = x;
downTime = System.currentTimeMillis();//记录按下的时间
if (!handler.hasMessages(1)) {//如果消息队列中已有消息 则不在重新发送
handler.sendEmptyMessageDelayed(1, 800);
}
break;
case MotionEvent.ACTION_MOVE:
attemptClaimDrag();
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onStopTrackingTouch();
//移动时如果x|y滑动了一段距离 则不可能为长按事件 即将 possibleLongTouch置为false
if (lastX != 0 && lastY != 0 && (Math.abs(y - lastY) > 5 || Math.abs(x - lastX) > 5)) {
possibleLongTouch = false;
handler.removeMessages(1);
}
lastY = y;
lastX = x;
break;
case MotionEvent.ACTION_UP:
onStopTrackingTouch();
setPressed(false);
lastX = 0;
lastY = 0;
handler.removeMessages(1);
if (System.currentTimeMillis() - downTime > 800 && possibleLongTouch) {
if (!hasLongTouch) {//如果已经执行过长按操作 则不需要再次执行
hasLongTouch = true;
if (longClickListener != null) {
longClickListener.onLongClick();
}
}
return true;
}
break;
case MotionEvent.ACTION_CANCEL:
onStopTrackingTouch();
setPressed(false);
handler.removeMessages(1);
lastX = 0;
lastY = 0;
break;
}
return true;
}
// 定义长按接口
// define longclick interface
public interface LongClickListener {
void onLongClick();
}
public void setLongClickListener(LongClickListener longClickListener) {
this.longClickListener = longClickListener;
}
}
3.带刻度的SeekBar (只支持垂直,如需水平可以修改 onDraw 方法)
TickMarkSeekBar 继承于 VerticalSeekbar,是为了实现垂直的带刻度 SeekBar
/**
* Created by Administrator on 2018/6/4.
* 带刻度尺的 SeekBar
*/
public class TickMarkSeekBar extends VerticalSeekBar {
private int mTickMarkCount;
private int mColor;
public TickMarkSeekBar(Context context) {
super(context);
}
public TickMarkSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray =context.obtainStyledAttributes(attrs, R.styleable.TickMarkSeekBar);
try {
mTickMarkCount = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_count,12);
mColor = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_color,Color.GRAY);
}finally {
typedArray.recycle();
}
}
public TickMarkSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
float startX ;
float startY ;
float stopX ;
float stopY ;
Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(mColor);
float mTickDiliver = getMeasuredHeight() / mTickMarkCount;
// 画刻度线
for (int i = 0; i< mTickMarkCount; i++) {
stopX = getPaddingLeft()+(i*mTickDiliver);
startX = stopX;
c.drawLine(startX,getMeasuredHeight(),stopX,0,linePaint);
}
}
}
4.使用控件
<com.feng.launcher.view.TickMarkSeekBar
android:id="@+id/flashlight_seekbar"
android:layout_width="124dp"
android:layout_height="382dp"
android:maxHeight="200dp"
android:progressDrawable="@drawable/seekbar"
android:thumb="@null"
app:tick_mark_count="5"
android:thumbOffset="0dp"
/>
如有错误,欢迎指正