Android自定义标尺滑动选择值效果

本文实例为大家分享了Android实现滑动标尺选择值,效果图

1.自定义属性attrs.xml

<declare-styleable name="RulerView">
    <attr name="textColor" format="color" />
    <attr name="textSize" format="dimension" />
    <attr name="lineColor" format="color" />
    <attr name="lineSpaceWidth" format="dimension" />
    <attr name="lineWidth" format="dimension" />
    <attr name="lineMaxHeight" format="dimension" />
    <attr name="lineMidHeight" format="dimension" />
    <attr name="lineMinHeight" format="dimension" />
    <attr name="textMarginTop" format="dimension" />
    <attr name="alphaEnable" format="boolean" />
    <attr name="minValue" format="float"/>
    <attr name="maxValue" format="float"/>
    <attr name="selectorValue" format="float"/>
    <attr name="perValue" format="float"/>
</declare-styleable>

2.自定义RulerView

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;

public class RulerView extends View {

  private int mMinVelocity;
  private Scroller mScroller; //Scroller是一个专门用于处理滚动效果的工具类  用mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动
  private VelocityTracker mVelocityTracker; //主要用跟踪触摸屏事件(flinging事件和其他gestures手势事件)的速率。
  private int mWidth;
  private int mHeight;

  private float mSelectorValue = 50.0f; // 未选择时 默认的值 滑动后表示当前中间指针正在指着的值
  private float mMaxValue = 200;    // 最大数值
  private float mMinValue = 100.0f;   //最小的数值
  private float mPerValue = 1;     //最小单位 如 1:表示 每2条刻度差为1.  0.1:表示 每2条刻度差为0.1
  // 在demo中 身高mPerValue为1 体重mPerValue 为0.1

  private float mLineSpaceWidth = 5;  // 尺子刻度2条线之间的距离
  private float mLineWidth = 4;     // 尺子刻度的宽度

  private float mLineMaxHeight = 420;  // 尺子刻度分为3中不同的高度。 mLineMaxHeight表示最长的那根(也就是 10的倍数时的高度)
  private float mLineMidHeight = 30;  // mLineMidHeight 表示中间的高度(也就是 5 15 25 等时的高度)
  private float mLineMinHeight = 17;  // mLineMinHeight 表示最短的那个高度(也就是 1 2 3 4 等时的高度)

  private float mTextMarginTop = 10;  //o
  private float mTextSize = 30;     //尺子刻度下方数字 textsize

  private boolean mAlphaEnable = false; // 尺子 最左边 最后边是否需要透明 (透明效果更好点)

  private float mTextHeight;      //尺子刻度下方数字 的高度

  private Paint mTextPaint;       // 尺子刻度下方数字( 也就是每隔10个出现的数值) paint
  private Paint mLinePaint;       // 尺子刻度 paint

  private int mTotalLine;        //共有多少条 刻度
  private int mMaxOffset;        //所有刻度 共有多长
  private float mOffset;        // 默认状态下,mSelectorValue所在的位置 位于尺子总刻度的位置
  private int mLastX, mMove;
  private OnValueChangeListener mListener; // 滑动后数值回调

  private int mLineColor = Color.GRAY;  //刻度的颜色
  private int mTextColor = Color.BLACK;  //文字的颜色

  public RulerView(Context context) {
    this(context, null);

  }

  public RulerView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public RulerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
  }

  protected void init(Context context, AttributeSet attrs) {
    mScroller = new Scroller(context);
    this.mLineSpaceWidth = myfloat(25.0F);
    this.mLineWidth = myfloat(2.0F);
    this.mLineMaxHeight = myfloat(100.0F);
    this.mLineMidHeight = myfloat(60.0F);
    this.mLineMinHeight = myfloat(40.0F);
    this.mTextHeight = myfloat(40.0F);

    final TypedArray typedArray = context.obtainStyledAttributes(attrs,
        R.styleable.RulerView);

    mAlphaEnable = typedArray.getBoolean(R.styleable.RulerView_alphaEnable, mAlphaEnable);

    mLineSpaceWidth = typedArray.getDimension(R.styleable.RulerView_lineSpaceWidth, mLineSpaceWidth);
    mLineWidth = typedArray.getDimension(R.styleable.RulerView_lineWidth, mLineWidth);
    mLineMaxHeight = typedArray.getDimension(R.styleable.RulerView_lineMaxHeight, mLineMaxHeight);
    mLineMidHeight = typedArray.getDimension(R.styleable.RulerView_lineMidHeight, mLineMidHeight);
    mLineMinHeight = typedArray.getDimension(R.styleable.RulerView_lineMinHeight, mLineMinHeight);
    mLineColor = typedArray.getColor(R.styleable.RulerView_lineColor, mLineColor);

    mTextSize = typedArray.getDimension(R.styleable.RulerView_textSize, mTextSize);
    mTextColor = typedArray.getColor(R.styleable.RulerView_textColor, mTextColor);
    mTextMarginTop = typedArray.getDimension(R.styleable.RulerView_textMarginTop, mTextMarginTop);

    mSelectorValue = typedArray.getFloat(R.styleable.RulerView_selectorValue, 0.0f);
    mMinValue = typedArray.getFloat(R.styleable.RulerView_minValue, 0.0f);
    mMaxValue = typedArray.getFloat(R.styleable.RulerView_maxValue, 100.0f);
    mPerValue = typedArray.getFloat(R.styleable.RulerView_perValue, 0.1f);

    mMinVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();

    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setColor(mTextColor);
    mTextHeight = getFontHeight(mTextPaint);

    mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mLinePaint.setStrokeWidth(mLineWidth);
    mLinePaint.setColor(mLineColor);

    // setValue(1990, 1940, 2016, 1);

  }

  public static int myfloat(float paramFloat) {
    return (int) (0.5F + paramFloat * 1.0f);
  }

  private float getFontHeight(Paint paint) {
    Paint.FontMetrics fm = paint.getFontMetrics();
    return fm.descent - fm.ascent;
  }

  /**
   * @param selectorValue 未选择时 默认的值 滑动后表示当前中间指针正在指着的值
   * @param minValue   最大数值
   * @param maxValue   最小的数值
   * @param per      最小单位 如 1:表示 每2条刻度差为1.  0.1:表示 每2条刻度差为0.1 在demo中 身高mPerValue为1 体重mPerValue 为0.1
   */
  public void setValue(float selectorValue, float minValue, float maxValue, float per) {
    this.mSelectorValue = selectorValue;
    this.mMaxValue = maxValue;
    this.mMinValue = minValue;
    this.mPerValue = (int) (per * 10.0f);
    this.mTotalLine = ((int) ((mMaxValue * 10 - mMinValue * 10) / mPerValue)) + 1;

    mMaxOffset = (int) (-(mTotalLine - 1) * mLineSpaceWidth);
    mOffset = (mMinValue - mSelectorValue) / mPerValue * mLineSpaceWidth * 10;
    invalidate();
    setVisibility(VISIBLE);
  }

  public void setOnValueChangeListener(OnValueChangeListener listener) {
    mListener = listener;
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    super.onSizeChanged(w, h, oldw, oldh);
    if (w > 0 && h > 0) {
      mWidth = w;
      mHeight = h;
    }
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    float left, height;
    String value;
    int alpha = 0;
    float scale;
    int srcPointX = mWidth / 2;
    for (int i = 0; i < mTotalLine; i++) {
      left = srcPointX + mOffset + i * mLineSpaceWidth;

      if (left < 0 || left > mWidth) {
        continue; // 先画默认值在正中间,左右各一半的view。 多余部分暂时不画(也就是从默认值在中间,画旁边左右的刻度线)
      }

      /*文字*/
      if (i % 10 == 0) {
        value = String.valueOf((int) (mMinValue + i * mPerValue / 10));
        if (mAlphaEnable) {
          mTextPaint.setAlpha(alpha);
        }
        canvas.drawText(value, left - mTextPaint.measureText(value) / 2,
            mTextHeight, mTextPaint);  // 在为整数时,画 数值
      }

      /*线条*/
      if (i % 10 == 0) {
        height = mLineMinHeight;
      } else if (i % 5 == 0) {
        height = mLineMidHeight;
      } else {
        height = mLineMaxHeight;
      }
      if (mAlphaEnable) {
        scale = 1 - Math.abs(left - srcPointX) / srcPointX;
        alpha = (int) (255 * scale * scale);

        mLinePaint.setAlpha(alpha);
      }
      canvas.drawLine(left, mLineMaxHeight + mTextMarginTop + mTextHeight, left, height, mLinePaint);

    }
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    int xPosition = (int) event.getX();

    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        mScroller.forceFinished(true);
        mLastX = xPosition;
        mMove = 0;
        break;
      case MotionEvent.ACTION_MOVE:
        mMove = (mLastX - xPosition);
        changeMoveAndValue();
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        countMoveEnd();
        countVelocityTracker();
        return false;
      default:
        break;
    }

    mLastX = xPosition;
    return true;
  }

  private void countVelocityTracker() {
    mVelocityTracker.computeCurrentVelocity(1000); //初始化速率的单位
    float xVelocity = mVelocityTracker.getXVelocity(); //当前的速度
    if (Math.abs(xVelocity) > mMinVelocity) {
      mScroller.fling(0, 0, (int) xVelocity, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
    }
  }

  /**
   * 滑动结束后,若是指针在2条刻度之间时,改变mOffset 让指针正好在刻度上。
   */
  private void countMoveEnd() {

    mOffset -= mMove;
    if (mOffset <= mMaxOffset) {
      mOffset = mMaxOffset;
    } else if (mOffset >= 0) {
      mOffset = 0;
    }

    mLastX = 0;
    mMove = 0;

    mSelectorValue = mMinValue + Math.round(Math.abs(mOffset) * 1.0f / mLineSpaceWidth) * mPerValue / 10.0f;
    mOffset = (mMinValue - mSelectorValue) * 10.0f / mPerValue * mLineSpaceWidth;

    notifyValueChange();
    postInvalidate();
  }

  /**
   * 滑动后的操作
   */
  private void changeMoveAndValue() {
    mOffset -= mMove;

    if (mOffset <= mMaxOffset) {
      mOffset = mMaxOffset;
      mMove = 0;
      mScroller.forceFinished(true);
    } else if (mOffset >= 0) {
      mOffset = 0;
      mMove = 0;
      mScroller.forceFinished(true);
    }
    mSelectorValue = mMinValue + Math.round(Math.abs(mOffset) * 1.0f / mLineSpaceWidth) * mPerValue / 10.0f;

    notifyValueChange();
    postInvalidate();
  }

  private void notifyValueChange() {
    if (null != mListener) {
      mListener.onValueChange(mSelectorValue);
    }
  }

  /**
   * 滑动后的回调
   */
  public interface OnValueChangeListener {
    void onValueChange(float value);
  }

  @Override
  public void computeScroll() {
    super.computeScroll();
    if (mScroller.computeScrollOffset()) {   //mScroller.computeScrollOffset()返回 true表示滑动还没有结束
      if (mScroller.getCurrX() == mScroller.getFinalX()) {
        countMoveEnd();
      } else {
        int xPosition = mScroller.getCurrX();
        mMove = (mLastX - xPosition);
        changeMoveAndValue();
        mLastX = xPosition;
      }
    }
  }
}

3.xml中使用activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:visibility="visible">

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:includeFontPadding="false"
      android:maxHeight="17.0sp"
      android:text="身高(cm)"
      android:textColor="#cc222222"
      android:textSize="15.0sp" />

    <TextView
      android:id="@+id/tv_info_height_value"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="11.0dip"
      android:includeFontPadding="false"
      android:maxHeight="24.0sp"
      android:textColor="#cc222222"
      android:textSize="24.0sp" />

    <RelativeLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content">

      <com.demo.ruleview.RulerView
        android:id="@+id/ruler_height"
        android:layout_width="fill_parent"
        android:layout_height="68.0dip"
        android:layout_marginTop="24.0dip"
        app:alphaEnable="true"
        app:lineColor="@color/gray"
        app:lineMaxHeight="40dp"
        app:lineMidHeight="30dp"
        app:lineMinHeight="20dp"
        app:lineSpaceWidth="10dp"
        app:lineWidth="2dip"
        app:maxValue="250.0"
        app:minValue="80.0"
        app:perValue="1"
        app:textColor="@color/black" />

      <ImageView
        android:layout_width="14.0dip"
        android:layout_height="46.0dip"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="40.0dip"
        android:scaleType="fitXY"
        android:src="@drawable/info_ruler" />
    </RelativeLayout>

    <Button
      android:id="@+id/click"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="10dp"
      android:text="点击改变" />

  </LinearLayout>
</LinearLayout>

4.Activity中使用MainActivity

public class MainActivity extends AppCompatActivity {

  private int maxValue = 250;
  private int minValue = 80;

  private RulerView rulerHeight;
  private TextView tvHeightValue;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    rulerHeight = (RulerView) findViewById(R.id.ruler_height);
    tvHeightValue = (TextView) findViewById(R.id.tv_info_height_value);

    showNumInt();

    findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {

        showNumInt();

      }
    });

    rulerHeight.setOnValueChangeListener(new RulerView.OnValueChangeListener() {
      @Override
      public void onValueChange(float value) {
        tvHeightValue.setText(String.valueOf(value));
      }
    });
  }

  private void showNumInt() {
    Random rand = new Random();
    int value = rand.nextInt(maxValue - minValue + 1) + minValue;
    rulerHeight.setValue(value, minValue, maxValue, 1);
    tvHeightValue.setText(String.valueOf(Integer.valueOf(value)));
  }
}

PS:可自行根据需要绘制线条和文字,上下选择文字位置。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View实现左右滑动选择出生年份

    自定义view的第三篇,模仿的是微博运动界面的个人出生日期设置view,先看看我的效果图: 支持设置初始年份,左右滑动选择出生年份,对应的TextView的值也会改变.这个动画效果弄了好久,感觉还是比较生硬,与微博那个还是有点区别.大家有改进的方案,欢迎一起交流. 自定义View四部曲,这里依旧是这个套路,看看怎么实现的. 1.自定义view的属性: 在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性以及声明我们的整个样式. <?xml version="1.

  • android view实现横向滑动选择

    本文实例为大家分享了android view实现横向滑动选择的具体代码,供大家参考,具体内容如下 做文字编辑,从网上找来的. HorizontalScrollSelectView: public boolean mAlwaysOverrideTouch = true; protected ListAdapter mAdapter; private int mLeftViewIndex = -1; private int mRightViewIndex = 0; protected int mCu

  • Android实现滑动选择控件实例代码

    前言 最近做了个滑动选择的小控件,拿出来给大家分享一下,先上图 运行效果 实现步骤 这里分解为3个动作:Down.Move.Up来进行分析,博主文采不好,大家直接看流程图吧!! 代码分析 前置知识 1.这个地方使用的是RecyclerView的代码,使用RecyclerView只能使用LinearLayoutManager,ListView的运行效果稍微要比RecyclerView差一些 //这里使用dispatchTouchEvent,因为onTouchEvent容易被OnTouchListe

  • Android自定义标尺滑动选择值效果

    本文实例为大家分享了Android实现滑动标尺选择值,效果图 1.自定义属性attrs.xml <declare-styleable name="RulerView"> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> <attr name=&qu

  • Android自定义view之3D正方体效果实例

    目录 前言 一.小提 二.将传感器改成事件分发机制 三.使用 四.源码 总结 前言 在之前写了一篇关于3D效果的文章,借助传感器展示,有小伙伴问可不可以改成手势滑动操作(事件分发),所以出一篇文章 传感器相关文章链接:Android 3D效果的实现 一.小提 相对于常见的自定义view而言,继承的GLSurfaceView只有两个构造函数.可以理解为没有提供获取自定义属性的方法. public TouchSurfaceView(Context context) { super(context);

  • Android自定义View实现折线图效果

    下面就是结果图(每种状态用一个表情图片表示): 一.主页面的布局文件如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height=&quo

  • Android自定义横向滑动菜单的实现

    本文讲述了Android自定义横向滑动菜单的实现.分享给大家供大家参考,具体如下: 前言 开发安卓过程中,经常会用到标题栏的样式,有时候传统方式不能满足开发者的需要,这时候就需要自定义控件来实现.(注意:本文提供思路,有关键代码,但是代码不全) 标题栏说明 自定义标题栏ColumnHorizontalScrollView继承HorizontalScrollView 这个安卓原生的控件,HorizontalScrollView是一种FrameLayout(框架布局),其子项被滚动查看时是整体移动的

  • Android自定义日历滑动控件

    本文实例为大家分享了Android自定义日历滑动控件的使用方法,供大家参考,具体内容如下 最近公司项目需要做这个需求,自己才疏学浅,总算能写出个大概来,遂在这里记录下来. 分析 先来分析一下: 首先,我们的需求是可以左右点击查看跳转到下一个月,中间的日历控件可以水平滚动选择日期,所以我们中间的日历控件用一个RecycleView来做,左右两位的为ImageVeiw. LRCalendarView 总体流程: 编写LRCalendarView的布局R.layout.calendar_view 新建

  • Android自定义View实现星星评分效果

    目录 前言 1.测量与图片的绘制 2.事件的交互与计算 3. 回调处理与自定义属性抽取 后记 前言 在前面的学习中,我们基本了解了一些 Canvas 的绘制,那么这一章我们一起复习一下图片的绘制几种方式,和事件的简单交互方式. 我们从易到难,作为基础的进阶控件,我们从最简单的交互开始,那就自定义一个星星评分的控件吧. 一个 App 必不可少的评论系统打分的控件,可以展示评分,可以点击评分,可以滑动评分.它的实现总体上可以分为以下的步骤: 强制测量大小为我们指定的大小 先绘制Drawable未评分

  • Android中Activity滑动关闭的效果

    最近感觉有一个Activity关闭的效果挺不错的,就是手势滑动就可以关闭当前Activity,于是就想写一篇博客和大家一起分享下!废话不多说,老规矩,还先上效果图,更直观! 项目地址:https://github.com/xinyitiandi/SlidingFinishDemo 上代码: 1.第一个Activity: package com.ekeguan.slidingfinishdemo; import android.content.Intent; import android.os.B

  • Android自定义VIew实现卫星菜单效果浅析

     一 概述: 最近一直致力于Android自定义VIew的学习,主要在看<android群英传>,还有CSDN博客鸿洋大神和wing大神的一些文章,写的很详细,自己心血来潮,学着写了个实现了类似卫星效果的一个自定义的View,分享到博客上,望各位指点一二.写的比较粗糙,见谅.(因为是在Linux系统下写的,效果图我直接用手机拍的,难看,大家讲究下就看个效果,勿喷). 先来看个效果图,有点不忍直视: 自定义VIew准备: (1)创建继承自View的类; (2)重写构造函数; (3)定义属性. (

  • Android自定义Seekbar滑动条 Pop提示跟随滑动按钮滑动

    本文实例为大家分享了Android自定义Seekbar滑动条的具体代码,供大家参考,具体内容如下 由于项目需要做出此效果,自定义写了一个. 效果图 思路: 原始的seekbar只有滑动条并没有下方的提示文字,所以我们必须要继承Seekbar重写这个控件. 代码: 在values文件夹下新建attrs.xml,用于设置跟随滑动按钮的文字大小,颜色,背景. <declare-styleable name="MySeekBar"> <attr name="text

  • Android自定义双向滑动控件

    本文实例为大家分享了Android自定义双向滑动控件的具体代码,供大家参考,具体内容如下 先看一下效果图 1.SeekBarPressure工具类 public class SeekBarPressure extends View {     private static final String TAG = "SeekBarPressure";     private static final int CLICK_ON_LOW = 1;      //点击在前滑块上     priv

随机推荐