您的位置:首页 > 编程语言

垂直和水平滑动刻度尺

2016-07-28 15:07 501 查看
公司有个需求需要自定义的滑动设置升高体重的界面。首先感谢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 = mScaleScrollVie
4000
wRange / 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);
}
});
其实原理一样。只不过获取高度变成了获取宽度。

就写到这里了,因为工作有点忙,代码也没好好整理。请多多指出不对的地方,共勉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息