Android之SeekBar(0在中间)
来源:互联网 发布:js如何实现继承 编辑:程序博客网 时间:2024/06/05 07:31
本文主要记录一些零碎的东西
公司UI想出一个SeekBar,中间是0 ,往左是负的,往右是正的,自带的完全无法满足,只能自己撸了,想了一下,有些思路
主要是Canvas 类画直线,画圆,画圆角矩形,看看效果
效果看着还可以,因为要支持最左边的点是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); }}
<?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); }}
0 0
- Android之SeekBar(0在中间)
- Android之SeekBar
- android控件之SeekBar
- Android组件之SeekBar
- Android入门之SeekBar
- android示例之SeekBar
- Android组件之SeekBar
- Android控件之SeekBar
- Android控件之SeekBar
- Android UI 之 SeekBar
- android开发之Seekbar
- Android开发之SeekBar
- Android控件之SeekBar
- Android 组件之 SeekBar
- Android控件之SeekBar
- android之seekBar
- Android学习之SeekBar控件
- android学习笔记之SeekBar
- 【Android】IntentService & HandlerThread源码解析
- javascript学习——constructor、prototype、_proto_的区别
- java开发中的思维
- 离开代码多年后,突然的心痒痒
- 扫描线填充算法
- Android之SeekBar(0在中间)
- 安装ruby
- hssfworkbook导出excel的问题
- js实现默认值参数
- Electron 发生错误 "Cannot find module app"
- 基础总结篇之一:Activity生命周期
- 学习笔记---ClassLoader
- 在spring的bean中注入内部类
- 20161215C语言基础15_函数2