首页 > 其他分享 >Android之SeekBar(0在中间)

Android之SeekBar(0在中间)

时间:2023-02-22 10:36:22浏览次数:39  
标签:return paint SeekBar private 中间 int progress Android android


公司UI想出一个SeekBar,中间是0 ,往左是负的,往右是正的,自带的完全无法满足,只能自己撸了,想了一下,有些思路

主要是Canvas 类画直线,画圆,画圆角矩形,看看效果

Android之SeekBar(0在中间)_进度条

效果看着还可以,因为要支持最左边的点是0,所以设置了两种模式的切换

跟随手势移动的处理:
touch事件,判断touch点是否在Thumb上,如果是在拉动圆点

/**
* 判断是否 touch 在 seekBar thumb 上
*
* @param event
* @return
*/
private boolean isTouchingTarget(MotionEvent event) {
isTouchLegal = event.getRawX() > progressPosition - DEFAULT_TOUCH_TARGET_SIZE
&& event.getRawX() < progressPosition + DEFAULT_TOUCH_TARGET_SIZE;
// Log.i("slack", "isTouchLegal " + isTouchLegal);
return isTouchLegal;
}


根据手在屏幕上的位置以及该View的布局(宽度高度)计算出当前的progress


 

/**
* return progress
* -maxProgress minProgress maxProgress
* \------------------------0---------------------------\
* min center touch-->\ max
* (min center touch max are positions in the screen)
* touch progress = (touch - center) / (max - center) * maxProgress;
*/
private float clamp(int value) {
if (mIsCenterState) {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value > centerX) {
if (value >= max) {
return maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else if (value < centerX) {
if (value <= min) {
return -maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else {
return minProgress;
}
} else {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value >= max) {
return maxProgress;
} else if (value <= min) {
return minProgress;
} else {
return (maxProgress - minProgress) * (value - min) / width ;
}
}
}


移动时增加自定义动画


/**
* 自定义动画 ofFloat(Object target, String propertyName, float... values)
* 第一个参数用于指定这个动画要操作的是哪个控件
* 第二个参数用于指定这个动画要操作这个控件的哪个属性
* 第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪
* 对于自定义的属性,d第二个参数需要提供setXXX 方法,like: public void setMThumbRadius(int mThumbRadius)
* set方法 属性的第一个字母记得要大写 ! ObjectAnimator.ofInt , ofInt 对应参数的类型,如果float 为ofFloat
*/
private ObjectAnimator getTargetAnimator(boolean touching) {
final ObjectAnimator anim = ObjectAnimator.ofFloat(this,
"mThumbRadius",
mThumbRadius,
touching ? mThumbPressedRadius : mThumbNormalRadius);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Log.i("slack","onAnimationUpdate...");
postInvalidate(); // 在子线程中使用刷新View
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Log.i("slack","onAnimationEnd...");
anim.removeAllListeners();
}
});
anim.setInterpolator(new AccelerateInterpolator());
return anim;
}

public void setMThumbRadius(float mThumbRadius) {
// Log.i("slack","setmThumbRadius...");
this.mThumbRadius = mThumbRadius;
}

看看完整代码 com.cl.slack.seekbar.CenterSeekBar

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;

/**
* 从中间可以向两边滑动 左负右正
* Created by slack on 2016/12/12 19:00.
*/

public class CenterSeekBar extends View {

private static final int DEFAULT_TOUCH_TARGET_SIZE = 40;
private final int DEFAULT_TEXT_PADDING = 10;

private Paint paint;

private float width = 800; // need <= getWidth()

/**
* progress start max
*/
private int minProgress = 0;

/**
* progress end max
*/
private int maxProgress = 100;

/**
* 进度条的颜色 底色 背景色
*/
@ColorInt
private int progressBackColor = Color.BLACK;

/**
* 进度条的底色 高度
*/
private float progressBackHeight = 10;

/**
* 进度条 底色 圆角矩形边框 描边
*/
@ColorInt
private int progressFrameColor = Color.WHITE;

/**
* 进度条圆角矩形边框 高度
*/
private float progressFrameHeight = 3;

/**
* 进度条的颜色
*/
@ColorInt
private int progressColor = Color.GREEN;

/**
* 进度条的 高度
*/
private float progressHeight = 20;

/**
* 如果0在中间,负进度条的颜色
*/
@ColorInt
private int progressMinusColor = Color.RED;

/**
* current progress
*/
private int progress = 50;

/**
* seekBar Thumb normal radius
*/
private float mThumbNormalRadius = 14;

/**
* seekBar Thumb radius when touched
*/
private float mThumbPressedRadius = 24;

/**
* seekBar Thumb color
*/
@ColorInt
private int mThumbColor = Color.BLUE;

/**
* progress 字体大小
*/
private float mTextSize = 40;

/**
* progress 字体 color
*/
@ColorInt
private int mTextColor = Color.WHITE;

/**
* progress 字体 背景 color
*/
@ColorInt
private int mTextBackColor = 0x7DD2D3D4;

/**
* progress 字体 背景 radius
*/
private float mTextBackRadius = 10;

/**
* 判断是否是 0 在中间
*/
private boolean mIsCenterState = false;

private float mThumbRadius = mThumbNormalRadius;
private float progressPosition;
private boolean isTouchLegal = false;
private ObjectAnimator mAnimator; // seekBar Thumb Animator
private RectF mTextRectF,mBackRectF,mProgressRectF;

private OnSeekBarProgressListener mOnSeekBarProgressListener;
private OnSeekBarFinishedListener mOnSeekBarFinishedListener;

public CenterSeekBar(Context context) {
this(context, null);
}

public CenterSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CenterSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

paint = new Paint();

if (attrs != null) {
TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs,
R.styleable.CenterSeekBar, 0, 0);
maxProgress = styledAttrs.getInteger(R.styleable.CenterSeekBar_maxProgress,100);
minProgress = styledAttrs.getInteger(R.styleable.CenterSeekBar_minProgress,0);
width = styledAttrs.getDimension(R.styleable.CenterSeekBar_width,800);
mIsCenterState = styledAttrs.getBoolean(R.styleable.CenterSeekBar_centerState,false);
progressBackColor = styledAttrs.getColor(R.styleable.CenterSeekBar_backColor,Color.BLACK);
progressBackHeight = styledAttrs.getDimension(R.styleable.CenterSeekBar_backHeight,10);
progressFrameColor = styledAttrs.getColor(R.styleable.CenterSeekBar_backFrameColor,Color.WHITE);
progressFrameHeight = styledAttrs.getDimension(R.styleable.CenterSeekBar_backFrameSize,3);
progressColor = styledAttrs.getColor(R.styleable.CenterSeekBar_progressColor,Color.GREEN);
progressHeight = styledAttrs.getDimension(R.styleable.CenterSeekBar_progressHeight,progressBackHeight);
progressMinusColor = styledAttrs.getColor(R.styleable.CenterSeekBar_progressMinusColor,Color.RED);
progress = styledAttrs.getInteger(R.styleable.CenterSeekBar_progress,50);
mThumbNormalRadius = styledAttrs.getDimension(R.styleable.CenterSeekBar_thumbNormalRadius,14);
mThumbPressedRadius = styledAttrs.getDimension(R.styleable.CenterSeekBar_thumbPressRadius,24);
mThumbColor = styledAttrs.getColor(R.styleable.CenterSeekBar_thumbColor,Color.BLUE);
progressColor = styledAttrs.getColor(R.styleable.CenterSeekBar_progressColor,Color.BLUE);
mTextColor = styledAttrs.getColor(R.styleable.CenterSeekBar_textColor,Color.WHITE);
mTextSize = styledAttrs.getDimension(R.styleable.CenterSeekBar_textSize,40);
mTextBackColor = styledAttrs.getColor(R.styleable.CenterSeekBar_textBackColor,0x7DD2D3D4);
mTextBackRadius = styledAttrs.getDimension(R.styleable.CenterSeekBar_textBackRadius,10);

styledAttrs.recycle();
}

mAnimator = getTargetAnimator(false);
mTextRectF = new RectF();
mBackRectF = new RectF();
mProgressRectF = new RectF();
}

/**
* 设置是否是 0 在中间
* @param enable
*/
public CenterSeekBar setCenterModeEnable(boolean enable) {
// 将负值 变成正值
if(mIsCenterState){
if(!enable){
if(progress < 0){
progress = -progress;
}
}
}
mIsCenterState = enable;
invalidate();
return this;
}

public CenterSeekBar setProgress(int progress){
if(progress < maxProgress && progress > minProgress){
this.progress = progress;
} else {
progress = minProgress;
}
return this;
}

public CenterSeekBar OnSeekBarProgressListener(OnSeekBarProgressListener l){
mOnSeekBarProgressListener = l;
return this;
}

public CenterSeekBar OnSeekBarFinishedListener(OnSeekBarFinishedListener l){
mOnSeekBarFinishedListener = l;
return this;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Log.i("slack","onDraw... " + mThumbRadius);
int centerX = getWidth() / 2; // x 是center
int centerY = 3 * getHeight() / 4; // y 是 3/4 高度
float startX = centerX - width / 2;

// draw background line
paint.setColor(progressBackColor);
paint.setStrokeWidth(progressBackHeight);
paint.setStyle(Paint.Style.FILL); // 实心
mBackRectF.left = startX;
mBackRectF.top = centerY - progressBackHeight;
mBackRectF.right = startX + width;
mBackRectF.bottom = centerY;
canvas.drawRoundRect(mBackRectF,mTextBackRadius,mTextBackRadius,paint);
paint.setColor(progressFrameColor);
paint.setStrokeWidth(progressFrameHeight);
paint.setStyle(Paint.Style.STROKE); // 空心
canvas.drawRoundRect(mBackRectF,mTextBackRadius,mTextBackRadius,paint);

// canvas.drawLine(startX, centerY, centerX + width / 2, centerY, paint);

paint.setStyle(Paint.Style.FILL);
paint.setColor(progressColor);

// draw progress
paint.setStrokeWidth(progressHeight);
paint.setColor(progressColor);
if (mIsCenterState) {
// if (progress < 0) {
// paint.setColor(progressMinusColor);
// }
startX = centerX;
progressPosition = startX + (int) ((progress * (width / 2f) / (maxProgress - minProgress)));
}else {
progressPosition = startX + ((progress * width / (maxProgress - minProgress)));
}
mProgressRectF.top = centerY - progressBackHeight;
mProgressRectF.bottom = centerY;
if(progress > 0) {
mProgressRectF.left = startX;
mProgressRectF.right = progressPosition;
}else {
mProgressRectF.left = progressPosition;
mProgressRectF.right = startX;
}
canvas.drawRoundRect(mProgressRectF,mTextBackRadius,mTextBackRadius,paint);

// canvas.drawLine(startX, centerY, progressPosition, centerY, paint);

// draw point
paint.setColor(mThumbColor);
canvas.drawCircle(progressPosition, centerY- progressBackHeight/2, mThumbRadius, paint);

/** mThumbRadius will change
* mThumbRadius : mThumbNormalRadius ----------------- mThumbPressedRadius
* alpha : 0 ------------------ 255
*
*/
int alpha = (int)(255 * (mThumbRadius - mThumbNormalRadius) / (mThumbPressedRadius - mThumbNormalRadius));

// draw text progress
paint.setColor(mTextBackColor);
paint.setAlpha(alpha);
mTextRectF.bottom = centerY - mThumbPressedRadius - DEFAULT_TEXT_PADDING;
mTextRectF.right = progressPosition + mTextSize + DEFAULT_TEXT_PADDING;
mTextRectF.top = mTextRectF.bottom - mTextSize - DEFAULT_TEXT_PADDING * 3;
mTextRectF.left = progressPosition - mTextSize - DEFAULT_TEXT_PADDING;
canvas.drawRoundRect(mTextRectF, mTextBackRadius, mTextBackRadius, paint);
paint.setTextSize(mTextSize);
paint.setColor(mTextColor);
paint.setAlpha(alpha);
// 为了让text 在背景的中心
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(progress + "%", progressPosition, mTextRectF.bottom - DEFAULT_TEXT_PADDING * 2, paint);

}

@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i("slack","onTouchEvent " + event.toString());
if (!isEnabled())
return false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
checkTouchingTarget(event);
break;
case MotionEvent.ACTION_MOVE:
if (isTouchLegal) {
progress = (int)clamp((int) event.getRawX());
// Log.i("slack","progress " + progress);
invalidate(); // 在UI线程中使用 刷新View
if(mOnSeekBarProgressListener != null){
mOnSeekBarProgressListener.onProgress(progress);
}
}
break;
case MotionEvent.ACTION_UP:
if (isTouchLegal) {
mAnimator.cancel();
mAnimator = getTargetAnimator(false);
mAnimator.start();
if(mOnSeekBarFinishedListener != null){
mOnSeekBarFinishedListener.onFinished(progress);
}
}
break;
}
return true;
}

/**
* if touch , seekBar Thumb Animation
*/
private void checkTouchingTarget(MotionEvent event) {
if (isTouchingTarget(event)) {
mAnimator.cancel();
mAnimator = getTargetAnimator(true);
mAnimator.start();
}
}

/**
* 判断是否 touch 在 seekBar thumb 上
*
* @param event
* @return
*/
private boolean isTouchingTarget(MotionEvent event) {
isTouchLegal = event.getRawX() > progressPosition - DEFAULT_TOUCH_TARGET_SIZE
&& event.getRawX() < progressPosition + DEFAULT_TOUCH_TARGET_SIZE;
// Log.i("slack", "isTouchLegal " + isTouchLegal);
return isTouchLegal;
}

/**
* 自定义动画 ofFloat(Object target, String propertyName, float... values)
* 第一个参数用于指定这个动画要操作的是哪个控件
* 第二个参数用于指定这个动画要操作这个控件的哪个属性
* 第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪
* 对于自定义的属性,d第二个参数需要提供setXXX 方法,like: public void setMThumbRadius(int mThumbRadius)
* set方法 属性的第一个字母记得要大写 ! ObjectAnimator.ofInt , ofInt 对应参数的类型,如果float 为ofFloat
*/
private ObjectAnimator getTargetAnimator(boolean touching) {
final ObjectAnimator anim = ObjectAnimator.ofFloat(this,
"mThumbRadius",
mThumbRadius,
touching ? mThumbPressedRadius : mThumbNormalRadius);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Log.i("slack","onAnimationUpdate...");
postInvalidate(); // 在子线程中使用刷新View
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Log.i("slack","onAnimationEnd...");
anim.removeAllListeners();
}
});
anim.setInterpolator(new AccelerateInterpolator());
return anim;
}

public void setMThumbRadius(float mThumbRadius) {
// Log.i("slack","setmThumbRadius...");
this.mThumbRadius = mThumbRadius;
}

/**
* return progress
* -maxProgress minProgress maxProgress
* \------------------------0---------------------------\
* min center touch-->\ max
* (min center touch max are positions in the screen)
* touch progress = (touch - center) / (max - center) * maxProgress;
*/
private float clamp(int value) {
if (mIsCenterState) {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value > centerX) {
if (value >= max) {
return maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else if (value < centerX) {
if (value <= min) {
return -maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else {
return minProgress;
}
} else {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value >= max) {
return maxProgress;
} else if (value <= min) {
return minProgress;
} else {
return (maxProgress - minProgress) * (value - min) / width ;
}
}
}

public interface OnSeekBarProgressListener {
void onProgress(int progress);
}

public interface OnSeekBarFinishedListener {
void onFinished(int progress);
}

}

属性文件 values/seekbar.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CenterSeekBar">
<attr name="maxProgress" format="integer"/> <!--max progress -->
<attr name="minProgress" format="integer"/> <!-- min minProgress -->
<attr name="width" format="dimension"/><!-- seekbar width needs <= seekbar veiw layout_width -->
<attr name="centerState" format="boolean"/> <!-- 是否 0 在中心 -->
<attr name="backColor" format="color"/><!-- 背景 拉条杆 底色 -->
<attr name="backHeight" format="dimension"/><!-- 背景 拉条杆 height -->
<attr name="backFrameColor" format="color"/><!-- 背景 圆角边框 描边-->
<attr name="backFrameSize" format="dimension"/><!-- 背景 圆角边框 宽度 -->
<attr name="progressColor" format="color"/><!--progress 进度 颜色-->
<attr name="progressHeight" format="dimension"/><!--progress height -->
<attr name="progressMinusColor" format="color"/><!-- 0在中心 时 负数 的颜色 -->
<attr name="progress" format="integer"/><!--当前 progress-->
<attr name="thumbNormalRadius" format="dimension"/><!-- thumb 圆 半径 -->
<attr name="thumbPressRadius" format="dimension"/><!-- thumb 圆 触摸时 半径 -->
<attr name="thumbColor" format="color"/><!-- thumb 圆 颜色 -->
<attr name="textSize" format="dimension"/><!--进度字体大小-->
<attr name="textColor" format="color"/><!--进度字体 颜色-->
<attr name="textBackColor" format="color"/><!--进度字体 背景 颜色-->
<attr name="textBackRadius" format="dimension"/><!--进度字体背景 圆角矩形 弧度-->
</declare-styleable>
</resources>

调用就很简单啦

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
android:orientation="vertical"
tools:context="com.cl.slack.seekbar.MainActivity">

<com.cl.slack.seekbar.CenterSeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/colorAccent"
app:maxProgress="100"
app:minProgress="0"
app:backColor="#FFFFFF"
app:backHeight="5dp"
app:backFrameColor="#C9C6C6"
app:backFrameSize="1dp"
app:thumbColor="#F96650"
app:thumbNormalRadius="10dp"
app:thumbPressRadius="12dp"
app:progressColor="#FCB2A7"
app:progress="0"
/>

<Button
android:onClick="reFlush"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text"/>
</LinearLayout>

activity

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

private CenterSeekBar mSeekBar;
private boolean center = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSeekBar = (CenterSeekBar) findViewById(R.id.seekbar);
}

public void reFlush(View view) {
center = !center;
mSeekBar.setCenterModeEnable(center);
}
}

-----update 2017.05.20----------

减少刷新频率,修复布局非全屏时部分问题

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;

import com.benqu.core.util.D;
import com.benqu.wuta.R;

/**
* 从中间可以向两边滑动 左负右正
* Created by slack on 2016/12/16 10:11.
*/
public class SeekBarView extends View {

private static final int DEFAULT_TOUCH_TARGET_SIZE = 40;
private final int DEFAULT_TEXT_PADDING = 10;
private final int DEFAULT_THUMB_COLOR = Color.GRAY;

private final Paint paint;

private float width = 800; // need <= getWidth()

/**
* progress start max
*/
private int minProgress = 0;

/**
* progress end max
*/
private int maxProgress = 100;

/**
* 进度条的颜色 底色 背景色
*/
@ColorInt
private int progressBackColor = Color.BLACK;

/**
* 进度条的底色 高度
*/
private float progressBackHeight = 10;

/**
* 进度条 底色 圆角矩形边框 描边
*/
@ColorInt
private int progressFrameColor = Color.WHITE;

/**
* 进度条圆角矩形边框 高度
*/
private float progressFrameHeight = 3;

/**
* 进度条的颜色
*/
@ColorInt
private int progressColor = Color.GREEN;

/**
* 进度条的 高度
*/
private float progressHeight = 20;

/**
* 如果0在中间,负进度条的颜色
*/
@ColorInt
private int progressMinusColor = Color.RED;

/**
* current progress
*/
private int progress = 50;

/**
* seekBar Thumb normal radius
*/
private float mThumbNormalRadius = 14;

/**
* seekBar Thumb radius when touched
*/
private float mThumbPressedRadius = 24;

/**
* seekBar Thumb color
*/
@ColorInt
private int mThumbColor = DEFAULT_THUMB_COLOR;

private float mTextLocation = 1;
/**
* progress 字体大小
*/
private float mTextSize = 40;

/**
* progress 字体 color
*/
@ColorInt
private int mTextColor = Color.WHITE;

/**
* progress 字体 背景 color
*/
@ColorInt
private int mTextBackColor = 0x7DD2D3D4;

/**
* progress 字体 背景 radius
*/
private float mTextBackRadius = 10;

/**
* 判断是否是 0 在中间
*/
private boolean mIsCenterState = false;

private float mThumbRadius = mThumbNormalRadius;
private float progressPosition;
private boolean isTouchLegal = false;
private ObjectAnimator mAnimator; // seekBar Thumb Animator
private RectF mTextRectF, mBackRectF, mProgressRectF;
private int mThumbDrawColor = DEFAULT_THUMB_COLOR;

private OnSeekBarChangeListener mOnSeekBarChangeListener;
private OnSeekBarProgressListener mOnSeekBarProgressListener;
private OnSeekBarFinishedListener mOnSeekBarFinishedListener;

public SeekBarView(Context context) {
this(context, null);
}

public SeekBarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public SeekBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

paint = new Paint();
paint.setAntiAlias(true);

if (attrs != null) {
TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs,
R.styleable.SeekBarView, 0, 0);
maxProgress = styledAttrs.getInteger(R.styleable.SeekBarView_S_maxProgress, 100);
minProgress = styledAttrs.getInteger(R.styleable.SeekBarView_S_minProgress, 0);
width = styledAttrs.getDimension(R.styleable.SeekBarView_S_width, 800);
mIsCenterState = styledAttrs.getBoolean(R.styleable.SeekBarView_S_centerState, false);
progressBackColor = styledAttrs.getColor(R.styleable.SeekBarView_S_backColor, Color.BLACK);
progressBackHeight = styledAttrs.getDimension(R.styleable.SeekBarView_S_backHeight, 10);
progressFrameColor = styledAttrs.getColor(R.styleable.SeekBarView_S_backFrameColor, Color.WHITE);
progressFrameHeight = styledAttrs.getDimension(R.styleable.SeekBarView_S_backFrameSize, 3);
progressColor = styledAttrs.getColor(R.styleable.SeekBarView_S_progressColor, Color.GREEN);
progressHeight = styledAttrs.getDimension(R.styleable.SeekBarView_S_progressHeight, progressBackHeight);
progressMinusColor = styledAttrs.getColor(R.styleable.SeekBarView_S_progressMinusColor, Color.RED);
progress = styledAttrs.getInteger(R.styleable.SeekBarView_S_progress, 50);
mThumbNormalRadius = styledAttrs.getDimension(R.styleable.SeekBarView_S_thumbNormalRadius, 14);
mThumbPressedRadius = styledAttrs.getDimension(R.styleable.SeekBarView_S_thumbPressRadius, 24);
mThumbColor = styledAttrs.getColor(R.styleable.SeekBarView_S_thumbColor, Color.BLUE);
progressColor = styledAttrs.getColor(R.styleable.SeekBarView_S_progressColor, Color.BLUE);
mTextLocation = styledAttrs.getInteger(R.styleable.SeekBarView_S_textLocation, 1);
mTextColor = styledAttrs.getColor(R.styleable.SeekBarView_S_textColor, Color.WHITE);
mTextSize = styledAttrs.getDimension(R.styleable.SeekBarView_S_textSize, 40);
mTextBackColor = styledAttrs.getColor(R.styleable.SeekBarView_S_textBackColor, 0x7DD2D3D4);
mTextBackRadius = styledAttrs.getDimension(R.styleable.SeekBarView_S_textBackRadius, 10);

mThumbRadius = mThumbNormalRadius;
mThumbDrawColor = mThumbColor;
styledAttrs.recycle();
}

mAnimator = getTargetAnimator(false);
mTextRectF = new RectF();
mBackRectF = new RectF();
mProgressRectF = new RectF();
}

/**
* 设置是否是 0 在中间
*
* @param enable
*/
public SeekBarView setCenterModeEnable(boolean enable) {
// 将负值 变成正值
if (mIsCenterState) {
if (!enable) {
if (progress < 0) {
progress = -progress;
}
}
}
mIsCenterState = enable;
invalidate();
return this;
}

public SeekBarView setProgress(int progress) {
if (mIsCenterState) {
if (progress <= maxProgress && progress >= minProgress - maxProgress) {
this.progress = progress;
} else {
this.progress = minProgress;
}
} else {
if (progress <= maxProgress && progress >= minProgress) {
this.progress = progress;
} else {
this.progress = minProgress;
}
}
invalidate();
return this;
}

/**
* 是否可用
*/
public SeekBarView setProgressEnable(boolean enable) {
if (enable) {
this.setEnabled(true);
mThumbDrawColor = mThumbColor;
} else {
this.setEnabled(false);
this.progress = 0;
mThumbDrawColor = DEFAULT_THUMB_COLOR;
}
invalidate();
return this;
}

public int getProgress() {
return progress;
}

public SeekBarView setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
mOnSeekBarChangeListener = l;
return this;
}

public SeekBarView setOnSeekBarProgressListener(OnSeekBarProgressListener l) {
mOnSeekBarProgressListener = l;
return this;
}

public SeekBarView setOnSeekBarFinishedListener(OnSeekBarFinishedListener l) {
mOnSeekBarFinishedListener = l;
return this;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Log.i("slack","onDraw... " + mThumbRadius);
int centerX = getWidth() / 2; // x 是center
int centerY = getHeight() / 2; // y 是 2/3 高度
float startX = centerX - width / 2 ;

// draw background line
paint.setColor(progressBackColor);
paint.setStrokeWidth(progressBackHeight);
paint.setStyle(Paint.Style.FILL); // 实心
mBackRectF.left = startX;
mBackRectF.top = centerY - progressBackHeight;
mBackRectF.right = startX + width;
mBackRectF.bottom = centerY;
canvas.drawRoundRect(mBackRectF, mTextBackRadius, mTextBackRadius, paint);
paint.setColor(progressFrameColor);
paint.setStrokeWidth(progressFrameHeight);
paint.setStyle(Paint.Style.STROKE); // 空心
canvas.drawRoundRect(mBackRectF, mTextBackRadius, mTextBackRadius, paint);

// canvas.drawLine(startX, centerY, centerX + width / 2, centerY, paint);

paint.setStyle(Paint.Style.FILL);
paint.setColor(progressColor);

// draw progress
paint.setStrokeWidth(progressHeight);
paint.setColor(progressColor);
if (mIsCenterState) {
// if (progress < 0) {
// paint.setColor(progressMinusColor);
// }
startX = centerX;
progressPosition = startX + (int) ((progress * (width / 2f) / (maxProgress - minProgress)));
} else {
progressPosition = startX + ((progress * width / (maxProgress - minProgress)));
}
mProgressRectF.top = centerY - progressBackHeight;
mProgressRectF.bottom = centerY;
if (progress > 0) {
mProgressRectF.left = startX;
mProgressRectF.right = progressPosition;
} else {
mProgressRectF.left = progressPosition;
mProgressRectF.right = startX;
}
canvas.drawRoundRect(mProgressRectF, mTextBackRadius, mTextBackRadius, paint);

// canvas.drawLine(startX, centerY, progressPosition, centerY, paint);

// draw point
paint.setColor(mThumbDrawColor);
canvas.drawCircle(progressPosition, centerY - progressBackHeight / 2, mThumbRadius, paint);

/** mThumbRadius will change
* mThumbRadius : mThumbNormalRadius ----------------- mThumbPressedRadius
* alpha : 0 ------------------ 255
*
*/
int alpha = (int) (255 * (mThumbRadius - mThumbNormalRadius) / (mThumbPressedRadius - mThumbNormalRadius));

// draw text progress up the Thumb code is ok

if (mTextLocation == 1) {
paint.setColor(mTextBackColor);
paint.setAlpha(alpha);
mTextRectF.bottom = centerY - mThumbPressedRadius - DEFAULT_TEXT_PADDING;
mTextRectF.right = progressPosition + mTextSize + DEFAULT_TEXT_PADDING;
mTextRectF.top = mTextRectF.bottom - mTextSize - DEFAULT_TEXT_PADDING * 3;
mTextRectF.left = progressPosition - mTextSize - DEFAULT_TEXT_PADDING;
canvas.drawRoundRect(mTextRectF, mTextBackRadius, mTextBackRadius, paint);
paint.setTextSize(mTextSize);
paint.setColor(mTextColor);
paint.setAlpha(alpha);
// 为了让text 在背景的中心
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(progress + "%", progressPosition, mTextRectF.bottom - DEFAULT_TEXT_PADDING * 2, paint);
} else if (mTextLocation == 2) {
// draw text in Thumb
paint.setTextSize(mTextSize);
paint.setColor(mTextColor);
paint.setAlpha(alpha);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(progress + "%", progressPosition, centerY, paint);
}
}

// private int mLastProgress;
private long mLastTime;

@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i("slack","onTouchEvent " + event.toString());
if (!isEnabled())
return false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
checkTouchingTarget(event);
break;
case MotionEvent.ACTION_MOVE:
if (isTouchLegal) {
progress = (int) clamp((int) event.getRawX() - getLeft());
// if (mLastProgress == progress) {
// // 两次一样就不需要重画
// break;
// }

long currentTime = System.currentTimeMillis();
if (currentTime - mLastTime < 50) {
// 刷新 FPS 不超过 20 fps
break;
}
mLastTime = currentTime;

// Log.i("slack","progress " + progress);
invalidate(); // 在UI线程中使用 刷新View
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgress(progress);
} else if (mOnSeekBarProgressListener != null) {
mOnSeekBarProgressListener.onProgress(progress);
}
}
break;
case MotionEvent.ACTION_UP:
invalidate();
// mLastProgress = -1;
if (isTouchLegal) {
mAnimator.cancel();
mAnimator = getTargetAnimator(false);
mAnimator.start();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onFinished(progress);
} else if (mOnSeekBarFinishedListener != null) {
mOnSeekBarFinishedListener.onFinished(progress);
}
}
break;
}
return true;
}

/**
* if touch , seekBar Thumb Animation
*/
private void checkTouchingTarget(MotionEvent event) {
if (isTouchingTarget(event)) {
mAnimator.cancel();
mAnimator = getTargetAnimator(true);
mAnimator.start();
}
}

/**
* 判断是否 touch 在 seekBar thumb 上
*
* @param event
* @return
*/
private boolean isTouchingTarget(MotionEvent event) {
float location = progressPosition + getLeft();
isTouchLegal = event.getRawX() > location - DEFAULT_TOUCH_TARGET_SIZE
&& event.getRawX() < location + DEFAULT_TOUCH_TARGET_SIZE;
D.i("slack", "isTouchLegal " + isTouchLegal + " " + event.getRawX() + " " +
event.getRawY() + " " + progressPosition);
return isTouchLegal;
}

/**
* 自定义动画 ofFloat(Object target, String propertyName, float... values)
* 第一个参数用于指定这个动画要操作的是哪个控件
* 第二个参数用于指定这个动画要操作这个控件的哪个属性
* 第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪
* 对于自定义的属性,d第二个参数需要提供setXXX 方法,like: public void setMThumbRadius(int mThumbRadius)
* set方法 属性的第一个字母记得要大写 ! ObjectAnimator.ofInt , ofInt 对应参数的类型,如果float 为ofFloat
*/
private ObjectAnimator getTargetAnimator(boolean touching) {
final ObjectAnimator anim = ObjectAnimator.ofFloat(this,
"mThumbRadius",
mThumbRadius,
touching ? mThumbPressedRadius : mThumbNormalRadius);
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Log.i("slack","onAnimationUpdate...");
postInvalidate(); // 在子线程中使用刷新View
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Log.i("slack","onAnimationEnd...");
anim.removeAllListeners();
}
});
anim.setInterpolator(new AccelerateInterpolator());
return anim;
}

public void setMThumbRadius(float mThumbRadius) {
// Log.i("slack","setmThumbRadius...");
this.mThumbRadius = mThumbRadius;
}

/**
* return progress
* -maxProgress minProgress maxProgress
* \------------------------0---------------------------\
* min center touch-->\ max
* (min center touch max are positions in the screen)
* touch progress = (touch - center) / (max - center) * maxProgress;
*/
private float clamp(int value) {
if (mIsCenterState) {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value > centerX) {
if (value >= max) {
return maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else if (value < centerX) {
if (value <= min) {
return -maxProgress;
} else {
return (int) ((maxProgress - minProgress) * (value - centerX) / (width / 2f));
}
} else {
return minProgress;
}
} else {
int centerX = getWidth() / 2;
float min = centerX - width / 2;// the start point
float max = centerX + width / 2;// the end point
if (value >= max) {
return maxProgress;
} else if (value <= min) {
return minProgress;
} else {
return (maxProgress - minProgress) * (value - min) / width;
}
}
}

public interface OnSeekBarProgressListener {
void onProgress(int progress);
}

public interface OnSeekBarFinishedListener {
void onFinished(int progress);
}

public interface OnSeekBarChangeListener extends OnSeekBarProgressListener, OnSeekBarFinishedListener {
}

}

属性文件 values/seekbar.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CenterSeekBar">
<attr name="maxProgress" format="integer"/> <!--max progress -->
<attr name="minProgress" format="integer"/> <!-- min minProgress -->
<attr name="width" format="dimension"/><!-- seekbar width needs <= seekbar veiw layout_width -->
<attr name="S_centerState" format="boolean"/> <!-- 是否 0 在中心 -->
<attr name="S_backColor" format="color"/><!-- 背景 拉条杆 底色 -->
<attr name="S_backHeight" format="dimension"/><!-- 背景 拉条杆 height -->
<attr name="S_backFrameColor" format="color"/><!-- 背景 圆角边框 描边-->
<attr name="S_backFrameSize" format="dimension"/><!-- 背景 圆角边框 宽度 -->
<attr name="S_progressColor" format="color"/><!--progress 进度 颜色-->
<attr name="S_progressHeight" format="dimension"/><!--progress height -->
<attr name="S_progressMinusColor" format="color"/><!-- 0在中心 时 负数 的颜色 -->
<attr name="S_progress" format="integer"/><!--当前 progress-->
<attr name="S_thumbNormalRadius" format="dimension"/><!-- thumb 圆 半径 -->
<attr name="S_thumbPressRadius" format="dimension"/><!-- thumb 圆 触摸时 半径 -->
<attr name="S_thumbColor" format="color"/><!-- thumb 圆 颜色 -->
<attr name="S_textSize" format="dimension"/><!--进度字体大小-->
<attr name="S_textColor" format="color"/><!--进度字体 颜色-->
<attr name="S_textBackColor" format="color"/><!--进度字体 背景 颜色-->
<attr name="S_textBackRadius" format="dimension"/><!--进度字体背景 圆角矩形 弧度-->
</declare-styleable>
</resources>

标签:return,paint,SeekBar,private,中间,int,progress,Android,android
From: https://blog.51cto.com/u_4387387/6078248

相关文章

  • Android单元测试
    什么是单元测试?单元测试就是针对类中的某一个方法进行验证是否正确的过程,单元就是指独立的粒子,在Android和Java中大都是指方法。为什么要进行单元测试?使用单元测试可以提高......
  • MASA MAUI Plugin (八)Android相册多选照片(Intent 方式)
    背景MAUI的出现,赋予了广大.Net开发者开发多平台应用的能力,MAUI是Xamarin.Forms演变而来,但是相比Xamarin性能更好,可扩展性更强,结构更简单。但是MAUI对于平台相关的实现并......
  • Android studio开发工具下载
    参考:(2条消息)Android基础——2:开发环境搭建及开发工具_ThePromonkeyOf_HeLuo的博客-CSDN博客_thecollectionofandroidplatformapis,toolsand下载前提前安装好jdk......
  • RabbitMQ中间件
    RabbitMQ配置环境安装erlang环境以及RabbitMQRabbitMQ端口号:5672去官网下载https://www.rabbitmq.com然后重启RabbitMQ服务RabbitMQ安装教程开放端口15672这里,......
  • 自研的内存分析利器开源了!Android Bitmap Monitor 助你定位不合理的图片使用
    大家好,我是shixin。在日常工作中,我们往往只关注Java内存使用情况,这主要是因为Java内存分析相关的工具比较多。与之不同的是,图片内存分析的工具比较少,当分析图片内......
  • mysql之中间件Cetus
    #################https://www.bookstack.cn/books/Cetus               ############################## ......
  • Android开发环境的搭建-Android Studio
    开始android的学习,首先要搭建开发环境,并下载AndroidStudio。并且测试连接手机jdk的安装之前已经完成了,sdk的安装会在Androidstudio的安装之后自动完成,故不需要担心。找到......
  • Android Studio的下载与安装
    一、下载下载官网:DownloadAndroidStudio&AppTools-AndroidDevelopers  点击绿色下载 ......
  • scrcpy不使用adb远程控制android
    1.开启服务器CLASSPATH=/data/local/tmp/scrcpy-server.jarapp_process/com.genymobile.scrcpy.Server1.23log_level=infobit_rate=8000000cleanup=falsetunnel_......
  • RabbitMQ消息中间件
    课程简介课程导读:RabbitMQ中间件本课程带你轻松入门,深度掌握RabbitMQ。 RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务......