Android之SeekBar(0在中間)
本文主要記錄一些零碎的東西
公司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);
}
}
屬性檔案 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 {
}
}
相關推薦
Android之SeekBar(0在中間)
本文主要記錄一些零碎的東西 公司UI想出一個SeekBar,中間是0 ,往左是負的,往右是正的,自帶的完全無法滿足,只能自己擼了,想了一下,有些思路 主要是Canvas 類畫直線,畫圓,畫圓角矩形,看看效果 效果看著還可以,因為要支援最左邊的點是0,所以設定了兩種模式的切
android之4.0控制元件switch自定義開關背景圖片和控制寬度
<style name="widget_gender_switch"> <item name="android:layout_height">wrap_content</item> <item name=
android之4.0的系統主題style修改android:Theme.Holo.Light
1.修改主介面背景顏色 轉自http://www.dewen.io/q/9466 在AndroidMenifest.xml application 屬性設定時不使用系統的 android:theme="@android:style/Theme.Holo", 而是自定義的
android自己定義之 5.0 風格progressBar
rcp 方式 rem listen angle 一個 arr 5.0 自己 近期做項目,用到了ProgressBar 。就想到了要使用Android5.0 的效果,就隨手實現了一下。 效果圖: 大概的思路: 1. 圓圈通過Canvas去繪
[Android FrameWork 6.0源碼學習] View的重繪過程之Layout
種子 noop cond req cor ide boolean 需要 bound View繪制的三部曲,測量,布局,繪畫現在我們分析布局部分測量部分在上篇文章中已經分析過了。不了解的可以去我的博客裏找一下View的布局和測量一樣,都是從ViewRootImpl中發起,Vi
[Android FrameWork 6.0源碼學習] View的重繪過程之Draw
鐘表 store 傳遞 lan play deb kill gre 學習 View繪制的三部曲,測量,布局,繪畫現在我們分析繪畫部分測量和布局 在前兩篇文章中已經分析過了。不了解的可以去我的博客裏找一下 下面進入正題,開始分析調用以及函數原理 private void
Android - 圖片處理之Glide4.0
郭大神關於Glide文章的連線,很詳細 Android圖片載入框架最全解析(一),Glide的基本用法 Android圖片載入框架最全解析(二),從原始碼的角度理解Glide的執行流程 Android圖片載入框架最全解析(三),深入探究Glide的快取機制 Android圖
Android Studio 3.0踩坑篇之自定義apk名稱
報錯日誌 Cannot set the value of read-only property ‘outputFile’ Android Studio3.0之前用法如下 applicationVariants.all { variant ->
Android響應式程式設計之RxJava2.0
前言 優點: 1、鏈式操作 2、非同步優化 實戰 先來個簡單的使用示例 Observable .create(new ObservableOnSubscribe<String>() {
Android Studio 3.0+以上 版本 填坑之依賴報錯。
Error:java.lang.RuntimeException: Annotation processors must be explicitly declared now. The following dependencies on the compile classpath a
Android動態許可權之6.0以上及6.0以下動態許可權申請遇到的問題
今天來記錄一個問題,因為專案需要用到zxing,那就必須要用到攝像頭,那麼就需要動態的去申請許可權,先貼個6.0以上的申請許可權的程式碼 這個在網上隨便搜一搜都有很多程式碼塊 就不做過多的解釋了 今天主要討論的是在6.0以下遇到的一個問題 /** * 請求許可
Android常用控制元件之SeekBar的使用
SeekBar的應用非常廣,比如用來顯示音量條、播放進度條,有水平顯示也有垂直顯示,但Android只給我們提供了水平的,可以用系統預設的樣式也可以用我們自定義的樣式,總之進度條的用法多種多樣,如果Android沒有提供也能我們自己去定製,先上圖 使用圖片自定義水平進度
Gradle之Could not find com.android.tools.buildgradle3.0.0. Searched in the followi
Could not find com.android.tools.buildgradle3.0.0. Searched 解決辦法:這是因為在gradle中的repositories 缺少了 ‘goog
Android Studio 3.0 填坑之依賴報錯。
Error:java.lang.RuntimeException: Annotation processors must be explicitly declared now. The follow
Android之JNI① AS3.0以下DNK下載配置和第一個JNI程式
一、JNI介紹 JNI(Java Native Interface):一個協議,這個協議用來溝通java程式碼和外部的原生代碼(c/c++), 外部的c/c++程式碼也可以呼叫java程式碼。 1.1 C語言的優勢: ①效率上 C/C++是本地語言,比java更高效;
[Android] 環境配置之正式版Android Studio 1.0
昨天看見 Android Studio 1.0 正式版本釋出了;心裡挺高興的。 算是忠實使用者了吧,從去年開發者大會一開始出現 AS 後就開始使用了;也是從那時開始就基本沒有用過 Eclipse 了;一路走來,遇到過 BUG ,也不斷的去國外找資源 找解決辦法。總的來說挺
《Android 之美 從0到1 -- 高手之路》
Android 之美 從0到1 – 高手之路 隨著Android 面試題總結,已經形成比較多的篇幅,為了方便大家閱讀,本篇將作為面試題總結導讀,也將成為Android 面試題的大綱,也只是Android 之美 從0到1 的一部分,陸續補充和完善,希望大家
android 開發 EventBus3.0不同之處詳細介紹
compile 'org.greenrobot:eventbus:3.0.0' 如果你看了之前的文章,應該已經會簡單使用EventBus了,上一節,我們用了onEventMainThread這個方法。其實在3.0之前還有其它的一些方法 onEven
Android之Android 6.0許可權機制及開發流程詳解
許可權機制變更的背景 在Android6.0之前,app安裝時會提示使用者此app需要使用哪些許可權,但使用者不能單獨對某項許可權進行授權或拒絕,只要使用者選擇了安裝,即表示使用者接受了app對這些許可權的使用,如果使用者不希望app獲取某些涉及隱私的資訊,例如讀取
一步一坑學android之禁用Appt2(andriod studio3.0)
唔,你的問題是什麼呢?1)上方提示R檔案缺失?2)Error:java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs fo