Android实现滚动刻度尺效果

缘起

最近在帮人做一个计步器,其中涉及到身高、体重等信息的采集;我参考了众多app的实现,觉得"乐动力"中滑动刻度的方式比较优雅。于是乎,反编译了该app,结果发现它是采用图片的方式实现的,即ScrollView内嵌了一张带刻度的图片。
个人觉得该方式太不灵活,且对美工的依赖较大,于是便想自定义一个刻度尺控件。

需求分析

  1. 绘制刻度,区分整值刻度和普通刻度
  2. 红色指针始终在刻度尺的中间,表示当前的刻度
  3. 刻度的最大值和最小值可动态设置
  4. 刻度尺的高度或宽度可设置,设置后中间刻度不变
  5. 可滑动,滑动后当前刻度随之改变

涉及的知识点

  1. View的机制
  2. canvas绘图
  3. Scroller工具类的使用
  4. 自定义View的属性
  5. 点击、滑动事件的处理

最终效果

由于简书上无法嵌入gif,为不影响效果,请移步github查看,如果觉得不错,帮忙给个star ^_^https://github.com/LichFaker/ScaleView

实现过程

1、新建一个class:HorizontalScaleScrollView, 继承自View

2、在构造方法中获取自定义属性:

protected void init(AttributeSet attrs) {
 // 获取自定义属性
 TypedArray ta = getContext().obtainStyledAttributes(attrs, ATTR);
 mMin = ta.getInteger(LF_SCALE_MIN, 0);
 mMax = ta.getInteger(LF_SCALE_MAX, 200);
 mScaleMargin = ta.getDimensionPixelOffset(LF_SCALE_MARGIN, 15);
 mScaleHeight = ta.getDimensionPixelOffset(LF_SCALE_HEIGHT, 20);
 ta.recycle();
 mScroller = new Scroller(getContext());
}

3、重写onMeasure,计算中间刻度

@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;
}

4、重写onDraw,绘制刻度和指针

protected void onDrawScale(Canvas canvas, Paint paint) {
 paint.setTextSize(mRectHeight / 4);
 for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
   if (i % 10 == 0) {
     //整值
     canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleMaxHeight, paint);
     //整值文字
     canvas.drawText(String.valueOf(k), i * mScaleMargin, mRectHeight - mScaleMaxHeight - 20, paint);
     k += 10;
   } else {
     canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight, paint);
   }
 }
}
protected void onDrawPointer(Canvas canvas, Paint paint) {
 paint.setColor(Color.RED);
 //每一屏幕刻度的个数/2
 int countScale = mScaleScrollViewRange / mScaleMargin / 2;
 //根据滑动的距离,计算指针的位置【指针始终位于屏幕中间】
 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, paint);
}

处理滑动事件

  1. 在手指按下时,记录当前的x坐标(针对水平刻度尺)。
  2. 在手指滑动过程中,判断当前指针所指的刻度是否已经超出了边界,如果超出,则禁止滑动,同时刷新当前界面。
  3. 在手指抬起时,校正当前的刻度。
@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);
}

最后的说明

以上只是针对水平滑动刻度的实现,垂直滑动原理一致,在源码中已经实现,其中也有许多不够完善的地方,如:

  1. 第一次快速滑动时,可以超出边界,之后则不会;
  2. 开放的自定义属性不够(根据具体情况);
  3. 可以考虑将水平和垂直的实现,在一个类中完成,因为在实现过程中发现其实有很多代码都是类似的,只是个别参数属性的不同,在坐标系中,垂直可以看成是水平旋转了90°,之后有时间可以朝这个方向尝试下。
(0)

相关推荐

  • Android 自定义圆形带刻度渐变色的进度条样式实例代码

    效果图 一.绘制圆环 圆环故名思意,第一个首先绘制是圆环 1:圆环绘制函数 圆环API public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 参数说明 oval:圆弧所在的椭圆对象. startAngle:圆弧的起始角度. sweepAngle:圆弧的角度. useCenter:是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不

  • Android实现滚动刻度尺效果

    缘起 最近在帮人做一个计步器,其中涉及到身高.体重等信息的采集:我参考了众多app的实现,觉得"乐动力"中滑动刻度的方式比较优雅.于是乎,反编译了该app,结果发现它是采用图片的方式实现的,即ScrollView内嵌了一张带刻度的图片. 个人觉得该方式太不灵活,且对美工的依赖较大,于是便想自定义一个刻度尺控件. 需求分析 绘制刻度,区分整值刻度和普通刻度 红色指针始终在刻度尺的中间,表示当前的刻度 刻度的最大值和最小值可动态设置 刻度尺的高度或宽度可设置,设置后中间刻度不变 可滑动,滑

  • android实现滚动文本效果

    本文实例为大家分享了android实现滚动文本效果的具体代码,供大家参考,具体内容如下 效果图 实现方法 直接上代码 首先是一个自定义layout,继承自FrameLayout public class AnimationTextLayout extends FrameLayout { private static final String TAG = "AnimationTextLayout"; private List<String> tipList; private

  • Android实现滑动刻度尺效果

    最近群里的开发人员咨询怎样实现刻度尺的滑动效果去选择身高体重等信息.给个横着的效果,自己试着去改编或者修改一下,看看通过自己的能力能不能做出竖着的效果来,过两天我再把竖着的那个滑动选择效果分享出来.废话不多说了,上代码. 效果图如下: 第一步:activity_mian.xml布局: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://s

  • Android 使用CoordinatorLayout实现滚动标题栏效果的实例

    在Material Design里,CoordinatorLayout通常用来作为顶层视图,来协调处理各个子View之间的动作,从而实现各种动画效果,如Snackbar与FloatingActionButton的配合显示效果,就是以CoordinatorLayout作为根布局来实现的 CoordinatorLayout提供Behaviors接口,子View通过实现Behaviors接口来协调和其它View之间的显示效果,可以这么理解: CoordinatorLayout让其子View之间互相知道

  • Android仿淘宝头条基于TextView实现上下滚动通知效果

    最近有个项目需要实现通知栏的上下滚动效果,仿淘宝头条的那种. 我从网上看了一些代码,把完整的效果做了出来.如图所示: 具体代码片段如下: 1.在res文件夹下新建anmin文件夹,在这个文件夹里创建两个文件 (1).anim_marquee_in.xml进入时动画 <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/ap

  • Android自定义控件实现按钮滚动选择效果

    本文实例为大家分享了Android实现按钮滚动选择效果的具体代码,供大家参考,具体内容如下 效果图 代码实现 package com.demo.ui.view; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Handler; import android.support.v4.content.ContextCompat;

  • Android实现文字滚动播放效果的代码

    在开发时,我们会遇到文字过长,TextView不能完全展示,但又不想文字换行展示,这样有时会影响美观.这时我们就需要文字滚动播放,让用户可以看到所有的文字. 话不多说,直接上代码: import android.content.Context; import android.util.AttributeSet; import android.widget.TextView; public class MarqueTextView extends TextView { public MarqueT

  • Android用viewPager2实现UI界面翻页滚动的效果

    1.先在build.gradle(Module)下添加引用viewPager2的库 implementation 'androidx.viewpager2:viewpager2:1.0.0' 2.在MainActivity下新建一个viewPager2 ViewPager2 viewPager = findViewById(R.id.viewPager); ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(); viewPager.

  • Android实现带动画效果的可点击展开TextView

    本文为大家分享了Android实现带动画效果的可点击展开TextView 制作代码,效果图: 收起(默认)效果: 点击展开后的效果: 源码: 布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@+id/activity_main" xmlns:android="http://schemas.android.com/apk/res/a

  • Android ScrollView实现反弹效果的实例

    Android ScrollView实现反弹效果 自定义ScrollView控件: /** * ScrollView反弹效果的实现 */ public class BounceScrollView extends ScrollView { private View inner;// 孩子View private float y;// 点击时y坐标 private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.) private boole

随机推荐