公司有個需求需要自定義的滑動設置升高體重的界面。首先感謝github上的這個開源讓我學習。https://github.com/LichFaker/ScaleView。因爲需求有點不一樣。需要自定義顏色。所以在它的基礎上改了畫線的位置和刻度的顏色。看一下界面。
上代碼。首先需要有個基礎的基類BaseScaleView。
public abstract class BaseScaleView extends View {
public static final int[] ATTR = {
R.attr.scale_view_min,
R.attr.scale_view_max,
R.attr.scale_view_margin,
R.attr.scale_view_height,
};
public static final int SCALE_MIN = 0;
public static final int SCALE_MAX = 1;
public static final int SCALE_MARGIN = 2;
public static final int SCALE_HEIGHT = 3;
private static final String TAG = null;
protected int mMax; //最大刻度
protected int mMin; // 最小刻度
protected int mCountScale; //滑動的總刻度
protected int mScaleScrollViewRange;
protected int mScaleMargin; //刻度間距
protected int mScaleHeight; //刻度線的高度
public int getmScaleMargin() {
return mScaleMargin;
}
public void setmScaleMargin(int mScaleMargin) {
this.mScaleMargin = mScaleMargin;
}
public int getmScaleHeight() {
return mScaleHeight;
}
public void setmScaleHeight(int mScaleHeight) {
this.mScaleHeight = mScaleHeight;
}
protected int mScaleMaxHeight; //整刻度線高度
protected int mRectWidth; //總寬度
protected int mRectHeight; //高度
protected Scroller mScroller;
protected int mScrollLastX;
protected int mTempScale; // 用於判斷滑動方向
protected int mMidCountScale; //中間刻度
private int width;
protected OnScrollListener mScrollListener;
public interface OnScrollListener {
void onScaleScroll(int scale);
}
public BaseScaleView(Context context) {
super(context);
init(null);
}
public BaseScaleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public BaseScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
// @TargetApi(Build.VERSION_CODES.LOLLIPOP)
// public BaseScaleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// super(context, attrs, defStyleAttr, defStyleRes);
// init(attrs);
// }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void init(AttributeSet attrs) {
// 獲取自定義屬性
TypedArray ta = getContext().obtainStyledAttributes(attrs, ATTR);
mMin = ta.getInteger(SCALE_MIN, 0);
mMax = ta.getInteger(SCALE_MAX, 240);
mScaleMargin = ta.getDimensionPixelOffset(SCALE_MARGIN, 15);
mScaleHeight = ta.getDimensionPixelOffset(SCALE_HEIGHT, 20);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
width = wm.getDefaultDisplay().getWidth();
ta.recycle();
mScroller = new Scroller(getContext());
initVar();
}
@Override
protected void onDraw(Canvas canvas) {
// 畫筆
Paint paint = new Paint();
paint.setColor(Color.GRAY);
// 抗鋸齒
paint.setAntiAlias(true);
// 設定是否使用圖像抖動處理,會使繪製出來的圖片顏色更加平滑和飽滿,圖像更加清晰
paint.setDither(true);
// 空心
paint.setStyle(Paint.Style.STROKE);
// 文字居中
paint.setTextAlign(Paint.Align.CENTER);
onDrawLine(canvas, paint);
onDrawScale(canvas, paint); //畫刻度
onDrawPointer(canvas, paint); //畫指針
super.onDraw(canvas);
}
protected abstract void initVar();
// 畫線
protected abstract void onDrawLine(Canvas canvas, Paint paint);
// 畫刻度
protected abstract void onDrawScale(Canvas canvas, Paint paint);
// 畫指針
protected abstract void onDrawPointer(Canvas canvas, Paint paint);
/**
* 使用Scroller時需重寫
*/
@Override
public void computeScroll() {
super.computeScroll();
// 判斷Scroller是否執行完畢
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
// 通過重繪來不斷調用computeScroll
invalidate();
}
}
public void smoothScrollBy(int dx, int dy) {
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
}
public void smoothScrollTo(int fx, int fy) {
int dx = fx - mScroller.getFinalX();
int dy = fy - mScroller.getFinalY();
smoothScrollBy(dx, dy);
}
/**
* 設置回調監聽
*
* @param listener
*/
public void setOnScrollListener(OnScrollListener listener) {
this.mScrollListener = listener;
}
public void setmMidCountScale(int mMidCountScale) {
this.mMidCountScale = mMidCountScale;
}
public int getmMidCountScale() {
return mMidCountScale;
}
public int getmScaleScrollViewRange() {
return mScaleScrollViewRange;
}
}
其中垂直刻度尺和水平刻度尺都繼承於它。首先看一下垂直刻度尺的類。public class VerticalScaleScrollView extends BaseScaleView {
private static final String TAG = "VerticalScaleScrollView";
public static int defaultY = 0;
public VerticalScaleScrollView(Context context) {
super(context);
}
public VerticalScaleScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//@TargetApi(Build.VERSION_CODES.LOLLIPOP)
// public VerticalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// super(context, attrs, defStyleAttr, defStyleRes);
// }
@Override
protected void initVar() {
mRectHeight = (mMax - mMin) * mScaleMargin;
Log.e(TAG, "mRectHeight"+mRectHeight+"mMax"+mMax+"mMin"+mMin+":::"+mScaleScrollViewRange);
mRectWidth = mScaleHeight * 8;
mScaleMaxHeight = mScaleHeight * 2;
// 設置layoutParams
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(mRectWidth, mRectHeight);
this.setLayoutParams(lp);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// int width = MeasureSpec.makeMeasureSpec(mRectWidth, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mScaleScrollViewRange = getMeasuredHeight();
mTempScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
mMidCountScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
}
@Override
protected void onDrawLine(Canvas canvas, Paint paint) {
//canvas.drawLine(0, 0, 0, mRectHeight, paint);
}
@Override
protected void onDrawScale(Canvas canvas, Paint paint) {
paint.setTextSize(mRectWidth / 4);
Paint linePaint = new Paint();
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
if (i % 10 == 0) { //整值,第三個參數可以修改線的長短
linePaint.setTextSize(mRectWidth / 6);
linePaint.setColor(Color.GREEN);
linePaint.setStrokeWidth(10);
//畫字的筆
Paint textPaint = new Paint();
textPaint.setTextSize(mRectWidth /5);
textPaint.setColor(Color.BLACK);
textPaint.setAntiAlias(true);
textPaint.setStrokeWidth(7);
canvas.drawLine(width-130, i * mScaleMargin, width, i * mScaleMargin, linePaint);
//整值文字
canvas.drawText(String.valueOf(k), width-150, i * mScaleMargin + paint.getTextSize()+paint.getTextSize()/3, textPaint);
canvas.drawText("cm", width-100, i * mScaleMargin + paint.getTextSize()+paint.getTextSize()/3, textPaint);
k += 10;
} else if(i%5==0){
linePaint.setColor(Color.GRAY);
linePaint.setTextSize(mRectWidth/4);
linePaint.setStrokeWidth(5);
canvas.drawLine(width-50, i *mScaleMargin, width, i * mScaleMargin, linePaint);
}
else {
linePaint.setColor(Color.GRAY);
linePaint.setTextSize(mRectWidth/4);
linePaint.setStrokeWidth(5);
canvas.drawLine(width-20, i * mScaleMargin, width, i * mScaleMargin, linePaint);
}
}
}
private boolean flag = true;
@Override
protected void onDrawPointer(Canvas canvas, Paint paint) {
paint.setColor(Color.RED);
paint.setStrokeWidth(7);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int width = wm.getDefaultDisplay().getWidth();
//每一屏幕刻度的個數/2
int countScale = mScaleScrollViewRange / mScaleMargin / 2;
//根據滑動的距離,計算指針的位置【指針始終位於屏幕中間】
int finalY = mScroller.getFinalY();
int tmpCountScale = 0;
//滑動的刻度
tmpCountScale = (int) Math.rint((double) finalY / (double) mScaleMargin); //四捨五入取整
//總刻度 countScale
mCountScale = tmpCountScale + countScale + mMin;
if (mScrollListener != null) { //回調方法
mScrollListener.onScaleScroll(mCountScale);
}
canvas.drawLine(width-150, countScale * mScaleMargin + finalY,
//mScaleMaxHeight + mScaleHeight,
width,
countScale * mScaleMargin + finalY, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mScroller != null && !mScroller.isFinished()) {
mScroller.abortAnimation();
}
mScrollLastX = y;
return true;
case MotionEvent.ACTION_MOVE:
int dataY = mScrollLastX - y;
if (mCountScale - mTempScale < 0) { //向下邊滑動
if (mCountScale <= mMin && dataY <= 0) //禁止繼續向下滑動
return super.onTouchEvent(event);
} else if (mCountScale - mTempScale > 0) { //向上邊滑動
if (mCountScale >= mMax && dataY >= 0) //禁止繼續向上滑動
return super.onTouchEvent(event);
}
smoothScrollBy(0, dataY);
mScrollLastX = y;
postInvalidate();
mTempScale = mCountScale;
return true;
case MotionEvent.ACTION_UP:
if (mCountScale < mMin) mCountScale = mMin;
if (mCountScale > mMax) mCountScale = mMax;
int finalY = (mCountScale - mMidCountScale) * mScaleMargin;
mScroller.setFinalY(finalY); //糾正指針位置
postInvalidate();
return true;
}
return super.onTouchEvent(event);
}
}
其中onDrawScale()方法是畫刻度和數字的方法。而onDrawPointer()是畫中間紅色指標的方法。
接下來是水平的刻度尺。
public class HorizontalScaleScrollView extends BaseScaleView {
public HorizontalScaleScrollView(Context context) {
super(context);
}
public HorizontalScaleScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// public HorizontalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// super(context, attrs, defStyleAttr, defStyleRes);
// }
@Override
protected void initVar() {
mRectWidth = (mMax - mMin) * mScaleMargin;
mRectHeight = mScaleHeight * 8;
mScaleMaxHeight = mScaleHeight * 2;
// 設置layoutParams
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(mRectWidth, mRectHeight);
this.setLayoutParams(lp);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height=MeasureSpec.makeMeasureSpec(mRectHeight, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, height);
mScaleScrollViewRange = getMeasuredWidth();
mTempScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
mMidCountScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
}
@Override
protected void onDrawLine(Canvas canvas, Paint paint) {
//canvas.drawLine(0, mRectHeight, mRectWidth, mRectHeight, paint);
}
@Override
protected void onDrawScale(Canvas canvas, Paint paint) {
paint.setTextSize(mRectHeight / 4);
paint.setStrokeWidth(5);
paint.setColor(Color.GRAY);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int height = wm.getDefaultDisplay().getHeight();
Paint linePaint = new Paint();
for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
if (i % 10 == 0) { //整值
linePaint.setTextSize(mRectHeight / 6);
linePaint.setColor(Color.GREEN);
linePaint.setStrokeWidth(10);
Paint textPaint = new Paint();
textPaint.setTextSize(30);
textPaint.setAntiAlias(true);
textPaint.setColor(Color.BLACK);
canvas.drawLine(i * mScaleMargin, height/2, i * mScaleMargin, mRectHeight - mScaleMaxHeight-20, linePaint);
//整值文字
canvas.drawText(String.valueOf(k), i * mScaleMargin-textPaint.getTextSize()+linePaint.getStrokeWidth(), mRectHeight - mScaleMaxHeight - 20, textPaint);
k += 10;
}else if(i%5==0){
canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight-10, paint);
}
else {
canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight, paint);
}
}
}
@Override
protected void onDrawPointer(Canvas canvas, Paint paint) {
paint.setColor(Color.RED);
//每一屏幕刻度的個數/2
int countScale = mScaleScrollViewRange / mScaleMargin / 2;
Log.e("TAG", "屏幕刻度的個數"+countScale);
//根據滑動的距離,計算指針的位置【指針始終位於屏幕中間】
int finalX = mScroller.getFinalX();
//滑動的刻度
int tmpCountScale = (int) Math.rint((double) finalX / (double) mScaleMargin); //四捨五入取整
//總刻度
mCountScale = tmpCountScale + countScale + mMin;
if (mScrollListener != null) { //回調方法
mScrollListener.onScaleScroll(mCountScale);
}
canvas.drawLine(countScale * mScaleMargin + finalX, mRectHeight,
countScale * mScaleMargin + finalX, mRectHeight - mScaleMaxHeight - mScaleHeight-30, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mScroller != null && !mScroller.isFinished()) {
mScroller.abortAnimation();
}
mScrollLastX = x;
return true;
case MotionEvent.ACTION_MOVE:
int dataX = mScrollLastX - x;
if (mCountScale - mTempScale < 0) { //向右邊滑動
if (mCountScale <= mMin && dataX <= 0) //禁止繼續向右滑動
return super.onTouchEvent(event);
} else if (mCountScale - mTempScale > 0) { //向左邊滑動
if (mCountScale >= mMax && dataX >= 0) //禁止繼續向左滑動
return super.onTouchEvent(event);
}
smoothScrollBy(dataX, 0);
mScrollLastX = x;
postInvalidate();
mTempScale = mCountScale;
return true;
case MotionEvent.ACTION_UP:
if (mCountScale < mMin) mCountScale = mMin;
if (mCountScale > mMax) mCountScale = mMax;
int finalX = (mCountScale - mMidCountScale) * mScaleMargin;
mScroller.setFinalX(finalX); //糾正指針位置
postInvalidate();
return true;
}
return super.onTouchEvent(event);
}
}
其實垂直和水平的刻度尺的寫法都大同小異。只是把刻度和文字的畫的位置不同而已。線的粗細長短顏色都可以根據需求自己修改。對了,忘了加上刻度尺的幾個自定義屬性。在values文件夾中加上scale_attrs.xml屬性文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="scale_view_max" format="integer" />
<attr name="scale_view_min" format="integer" />
<attr name="scale_view_height" format="dimension" />
<attr name="scale_view_margin" format="dimension" />
</resources>
目前支持的自定義屬性:
scale_view_max
最大值scale_view_min
最小值scale_view_height
刻度的高度scale_view_margin
刻度的間距layout_width
可動態調整
因爲無法設置默認選中刻度。這點有點不好。公司需求是需要默認選中170釐米和60公斤。後來通過各種方法把默認選中的問題解決了。這個後面說。下面是在佈局中的使用,添加使用下面代碼。
<com.hyx.fitbit.view.VerticalScaleScrollView
android:id="@+id/verticalScale"
android:layout_marginTop="60dp"
app:scale_view_max="240"
app:scale_view_min="140"
app:scale_view_height="15dip"
app:scale_view_margin="15dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
/>
下面是在代碼中使用垂直刻度尺VerticalScaleScrollView scaleScrollView = (VerticalScaleScrollView) findViewById(R.id.verticalScale);
下面是滑動時間刻度的監聽
scaleScrollView.setOnScrollListener(new HorizontalScaleScrollView.OnScrollListener() {
@Override
public void onScaleScroll(int scale) {
height_text.setText(String.valueOf(scale));
//float y = scaleScrollView.getScaleY();
}
});
水平刻度尺跟垂直刻度尺的用法也是一樣的。就不一一貼代碼了。
接下來說說默認選中的問題,就比如要選中170釐米來說,一開始我是去算最小值到170的每一個刻度指尖的距離。然後還要得到這個控件的高度。就是這個高度問題困擾了我很久。
直接用scaleScrollView.getHeight();scaleScrollView.getMeasuredHeight();無法得到控件的高度。試過好多方法還是不行。後來查到了是控件還沒初始化完全無法得到高度。後來查到了一個方法,是控件畫完之後的回調接口。在該接口裏面獲取了控件的高度。
ViewTreeObserver vto = scaleScrollView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
scaleScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
viewHeight = scaleScrollView.getHeight();
countScale = viewHeight/ getmScaleMargin / 2;
countScale = 30-countScale;
scaleScrollView.smoothScrollTo(0, countScale*getmScaleMargin);
Log.e(TAG, "高:"+viewHeight);
scaleScrollView.getWidth();
}
});
這樣設置了的話。刻度尺就會默認滑動到170。其中的30是最小值140到默認選中170間的值。然後水平公斤刻度尺的那個 也是同樣的做法讓它默認選中60公斤。代碼如下vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
scaleScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
width = scaleScrollView.getWidth();
countScale = width/getmScaleMargin/2;
countScale = 30 - countScale;
scaleScrollView.smoothScrollTo(countScale*getmScaleMargin, 0);
}
});
其實原理一樣。只不過獲取高度變成了獲取寬度。就寫到這裏了,因爲工作有點忙,代碼也沒好好整理。請多多指出不對的地方,共勉。