android尺子的自定义view——RulerView详解

项目中用到自定义尺子的样式:

原代码在github上找的,地址:https://github.com/QQabby/HorizontalRuler

原效果为

因为跟自己要使用的view稍有不同 所以做了一些修改,修改的注释都放在代码中了,特此记录一下。

首先是一个自定义View:

public class RuleView extends View {

  private Paint paint;

  private Context context;

  private int maxValue = 500;
  /**
   * 起点x的坐标
   */
  private float startX ;

  private float startY ;
  /**
   * 刻度线的长度
   */
  private float yLenght ;
  /**
   * 刻度的间隙
   */
//  private float gap = 8f;
  private float gap = 10;
  /**
   * 文本的间隙
   */
  private float textGap = 10f;
  /**
   * 短竖线的高度
   */
  private float smallHeight = 10f;
  /**
   * 长竖线的高度
   */
  private float largeHeight = 22f;

  /**
   * 文本显示格式化
   */
  private DecimalFormat format;

  private DisplayMetrics metrics = null;
  /**
   * 文本的字体大小
   */
  private float mFontSize;

  private Handler mScrollHandler = null;

  private MyHorizontalScrollView horizontalScrollView;

  private int mCurrentX = -999999999;
  /**
   * 刻度进制
   */
//  private float unit = 10f;
  private int unit = 10;//隔unit个刻度写一个数字
  //每个大刻度代表值iValue
  private int iValue = 10;

  boolean isDraw = true;

  public RuleView(Context context) {
    super(context);
    this.context = context;
    init();
  }

  public void setHorizontalScrollView(
      MyHorizontalScrollView horizontalScrollView) {
    this.horizontalScrollView = horizontalScrollView;

    this.horizontalScrollView.setOnTouchListener(new OnTouchListener() {

      @Override
      public boolean onTouch(View v, MotionEvent event) {

        final int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:

          break;
        case MotionEvent.ACTION_MOVE:

          mScrollHandler.removeCallbacks(mScrollRunnable);
          break;
        case MotionEvent.ACTION_UP:

          mScrollHandler.post(mScrollRunnable);
          break;
        }
        return false;
      }
    });
  }

  public RuleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;

    init();
  }

  public void init() {

//    format = new DecimalFormat("0.0");
    format = new DecimalFormat("0");//不使用浮点数格式

    metrics = new DisplayMetrics();
    WindowManager wmg = (WindowManager) context
        .getSystemService(Context.WINDOW_SERVICE);
    wmg.getDefaultDisplay().getMetrics(metrics);

    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setStyle(Paint.Style.FILL);
    paint.setStrokeWidth(getResources().getDimension(R.dimen.ui_1_dip));
//    paint.setStrokeWidth(2);
    paint.setColor(Color.parseColor("#999999"));

    mFontSize = ScreenUtil.dip2px(context, 16);
//    startY = ScreenUtil.dip2px(context, 20f);
    startY = ScreenUtil.dip2px(context, 0);//Y轴由0开始,即最顶端,不用设置适配布局文件RuleView的android:layout_marginTop="-20dp"
    yLenght = ScreenUtil.dip2px(context, 10);
//    gap = ScreenUtil.dip2px(context, 8f);
    gap = ScreenUtil.dip2px(context, 10);
//    startX = ScreenUtil.getScreenWidth(context)/ 2.0f- getResources().getDimension(R.dimen.ui_10_dip) ;
    startX = ScreenUtil.getScreenWidth(context)/ 2.0f;//X轴不减去10dp,则三角形顶点可以刚好最准0位置

    // + getResources().getDimension(R.dimen.text_h2)/2.0f
    // Util.dip2px(context, 13f) +

    mScrollHandler = new Handler(context.getMainLooper());

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    float width = maxValue * gap + ScreenUtil.getScreenWidth(context) - getResources().getDimension(R.dimen.ui_10_dip)*2.0f ;

    // int widthMode = MeasureSpec.getMode(heightMeasureSpec);
    // if(widthMode == MeasureSpec.AT_MOST){
    // Log.d("TAG", "mode::AT_MOST");
    // }else if(widthMode == MeasureSpec.EXACTLY){
    // Log.d("TAG", "mode::EXACTLY");
    // }else if(widthMode == MeasureSpec.UNSPECIFIED){
    // Log.d("TAG", "mode::UNSPECIFIED");
    // }

    setMeasuredDimension((int) width, heightMeasureSpec);
  }

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

    // 画刻度线
    paint.setColor(getResources().getColor(R.color.gray_bg_high));// 刻度颜色
    for (int i = 0; i <= maxValue; i++) {

      if (i % 5 == 0) {
        yLenght = ScreenUtil.dip2px(context, largeHeight);
      } else {
        yLenght = ScreenUtil.dip2px(context, smallHeight);
      }
      canvas.drawLine(i * gap + startX, startY, i * gap + startX, yLenght
          + startY, paint);
    }

    paint.setTextSize(mFontSize);

    // 每10个刻度写一个数字
    textGap = gap * unit;

    // 画刻度文字30
    paint.setColor(getResources().getColor(R.color.textGray));// 文字颜色
    for (int i = 0; i <= maxValue / unit; i++) {

//      String text = format.format(i + 1) + "";//从0开始计数时不用加1
      String text = format.format(i * iValue) + "";//乘以每刻度的值iValue
      // 获取文本的宽度
      float width = ScreenUtil.px2dip(context, calculateTextWidth(text)) / 2f;

      canvas.drawText(
          text,
          startX - width + i * textGap,
          (startY + ScreenUtil.dip2px(context, largeHeight))
              + ScreenUtil.dip2px(context, 24), paint);//字体大小
    }
  }

  /**
   * 获取TextView中文本的宽度
   */
  private float calculateTextWidth(String text) {
    if (TextUtils.isEmpty(text)) {
      return 0;
    }
    TextPaint textPaint = new TextPaint();
    textPaint.setTextSize(mFontSize * metrics.scaledDensity);
    final float textWidth = textPaint.measureText(text);

    return textWidth;
  }

  DecimalFormat df = new DecimalFormat("0.0");

  /**
   * 当滑动尺子的时候
   */
  int scrollWidth = 0;

  public void setScrollerChanaged(int l, int t, int oldl, int oldt) {
    // 滑动的距离
    scrollWidth = l;

    float number = scrollWidth / gap;
    float result = number / unit;

    listener.onSlide(result);
  }

  public onChangedListener listener;

  public interface onChangedListener {

    void onSlide(float number);
  }

  public void onChangedListener(onChangedListener listener) {
    this.listener = listener;
  }

  /**
   * 滚动监听线程
   */
  private Runnable mScrollRunnable = new Runnable() {

    @Override
    public void run() {
      if (mCurrentX == horizontalScrollView.getScrollX()) {// 滚动停止了

        try {

          float x = horizontalScrollView.getScrollX();
          float value = (x / (gap * unit));// 当前的值
          String s = df.format(value);

          // 滑动到11.0 ok
          int scrollX = (int) (Double.parseDouble(s) * gap * unit);

          horizontalScrollView.smoothScrollTo(scrollX, 0);

        } catch (NumberFormatException numExp) {
          numExp.printStackTrace();
        }
        mScrollHandler.removeCallbacks(this);
      } else {
        mCurrentX = horizontalScrollView.getScrollX();
        mScrollHandler.postDelayed(this, 50);
      }
    }
  };

  /**
   * 设置默认刻度尺的刻度值,不会滚动到相应的位置
   *
   * @param scaleValue
   */
  public void setDefaultScaleValue(float scaleValue) {

//    final int scrollX = (int) ((scaleValue - 1.0f) * gap * unit);//从0开始计数时不用减去1
    final int scrollX = (int) (scaleValue * gap * unit / 10);//每个值在设置刻度时会乘以10,所以除去

    new Handler().postDelayed(new Runnable() {

      @Override
      public void run() {

        horizontalScrollView.smoothScrollTo(scrollX, 0);
      }
    }, 100);
  }

  /**
   * 设置刻度值
   */
  public void setScaleValue(int iValue) {
    this.iValue = iValue;
  }

  /**
   * 设置刻度最小值
   */
  public void setMinScaleValue(Float minScaleValue) {
    // this.minScaleValue = minScaleValue;
  }

  /**
   * 获取刻度最大值
   */
  public Float getMaxScaleValue() {
    // return maxScaleValue;
    return 33.0f;
  }

  /**
   * 设置刻度最大值
   */
  public void setMaxScaleValue(Float maxScaleValue) {
    // this.maxScaleValue = maxScaleValue;
  }

  /**
   * 设置当前刻度尺的刻度值,并滚动到相应的位置
   *
   * @param scaleValue
   */
  public void setScaleScroll(float scaleValue) {

    int scrollX = (int) ((scaleValue - 1.0f) * gap * unit);

    horizontalScrollView.smoothScrollTo(scrollX, 0);
  }
}

另外用到一个自定义的scrollView:

public class MyHorizontalScrollView extends HorizontalScrollView {

  private OnScrollListener onScrollListener = null;

  public MyHorizontalScrollView(Context context) {
    this(context, null);
  }
  public MyHorizontalScrollView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public MyHorizontalScrollView(Context context, AttributeSet attrs,
      int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @Override
  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if(onScrollListener != null){
      onScrollListener.onScrollChanged(l, t, oldl, oldt);
    }
  }

  public interface OnScrollListener {
    public void onScrollChanged(int l, int t, int oldl, int oldt);
  }

  public void setOnScrollListener(OnScrollListener onScrollListener) {
    this.onScrollListener = onScrollListener;
  }
}

直尺上的黄色三角标其实是嵌在布局上的,在drawble文件中实现

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

  <item android:id="@+id/shape_id">
    <!--&lt;!&ndash; 正三角 &ndash;&gt;-->
    <!--<rotate-->
    <!--android:fromDegrees="45"-->
    <!--android:toDegrees="45"-->
    <!--android:pivotX="-40%"-->
    <!--android:pivotY="80%">-->
    <!-- 倒三角 -->
    <rotate
      android:fromDegrees="45"
      android:toDegrees="45"
      android:pivotX="135%"
      android:pivotY="15%"
      >
      <shape android:shape="rectangle">
        <solid android:color="@color/main_yellow"/>
      </shape>
    </rotate>
  </item>

</layer-list>

下面是布局文件,注意RuleView是嵌在ScrollView中的:

<RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="300px"
      android:layout_marginTop="30px"
      android:background="#fff">
    <MyHorizontalScrollView
      android:id="@+id/hor_scrollview"
      android:layout_width="match_parent"
      android:layout_height="300px"
      android:scrollbars="none" >

      <RuleView
        android:id="@+id/rule_view"
        android:background="@color/main_white"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="top"/>
    </MyHorizontalScrollView>
      <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/gray_bg"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true" />
      <View
        android:layout_width="30px"
        android:layout_height="30px"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"
        android:background="@drawable/shape_triangle" />
    </RelativeLayout>

最后在界面中的使用:

ruleView = (RuleView) findViewById(R.id.rule_view);

    horizontalScrollView = (MyHorizontalScrollView) findViewById(R.id.hor_scrollview);
    horizontalScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);// 去掉超出滑动后出现的阴影效果

    // 设置水平滑动
    ruleView.setHorizontalScrollView(horizontalScrollView);
    ruleView.setDefaultScaleValue(num);

    // 当滑动尺子的时候
    horizontalScrollView.setOnScrollListener(new MyHorizontalScrollView.OnScrollListener() {

      @Override
      public void onScrollChanged(int l, int t, int oldl, int oldt) {

        ruleView.setScrollerChanaged(l, t, oldl, oldt);
      }
    });

    ruleView.onChangedListener(new RuleView.onChangedListener() {
      @Override
      public void onSlide(float number) {

        int num = (int) (number * 10);
        tvNum.setText(num+"");
      }
    });

以上这篇android尺子的自定义view——RulerView详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Android view自定义带文字带进度的控件

    目标:自定义一个带文字带进度的控件,具体内容如下 效果图: 不啰嗦先看东西: 步骤分析 提取自定义属性 //提供对外暴露的属性,如有不够自己扩展 <declare-styleable name="DescProgressView"> <attr name="dpv_text_normal_color" format="color" /> <attr name="dpv_text_seleced_color&

  • Android控件CardView实现卡片布局

    CardView介绍 CardView是Android 5.0系统引入的控件,相当于FragmentLayout布局控件然后添加圆角及阴影的效果:CardView被包装为一种布局,并且经常在ListView和RecyclerView的Item布局中,作为一种容器使用.CardView应该被使用在显示层次性的内容时:在显示列表或网格时更应该被选择,因为这些边缘可以使得用户更容易去区分这些内容. 使用 先看效果 首先在build.gradle文件添加依赖库 dependencies { compil

  • Android自定义引导玩转ViewPager的方法详解

    ViewPager简介: ViewPager(android.support.v4.view.ViewPager)是android扩展包v4包中的类,这个类可以让用户左右切换当前的view,实现滑动切换的效果. 注意: ViewPager类直接继承了ViewGroup类,也就是说它和我们经常打交道的LinearLayout一样,都是一个容器,需要在里面添加我们想要显示的内容. ViewPager类需要一个PagerAdapter适配器类给它提供数据,这个和ListView类似. ViewPage

  • android尺子的自定义view——RulerView详解

    项目中用到自定义尺子的样式: 原代码在github上找的,地址:https://github.com/QQabby/HorizontalRuler 原效果为 因为跟自己要使用的view稍有不同 所以做了一些修改,修改的注释都放在代码中了,特此记录一下. 首先是一个自定义View: public class RuleView extends View { private Paint paint; private Context context; private int maxValue = 500

  • Android 开发订单流程view实例详解

     Android 开发订单流程view实例详解 先看看最终效果图: 怎么样,效果还是很不错的吧?群里有人说切四张图的.recycleview的.各种的都有啊,但是最简单的就是通过自定义view来实现了-接下来让我们来实现下这个(订单流程view). 首先我们定义好我们的自定义属性: attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleabl

  • Android使用xml自定义图片实例详解

    Android使用xml自定义图片实例详解 实现效果图: 白色圆角图片 bg_round_rectangle_white.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-

  • Android AS创建自定义布局案例详解

    先创建一个title.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" andr

  • Android自定义View构造函数详解

    初始Custom View的构造函数 之前写过一篇实现圆形进度条的博客(自定义圆形进度条),通常我们在实现Custom View的时候,都会先继承View并实现View的三个构造函数,例如: import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; public class MyCustomView exte

  • Android开发之自定义UI组件详解

    Android开发自定义UI组件实现红色小球跟随手指移动 要写实现自定义UI组件,要创建一个BallView类,继承View类,在BallView类中创建画笔,然后重写OnDraw()方法和OnTouchEvent()方法. /** * Created by nuist__NJUPT on 2021/5/9. * 自定义UI组件 * View组件在布局中是一个矩形的空白区域,没有任何内容 * 而UI组件之所以有内容,是因为继承了View组件之后在其提供的空白区域上重新绘制外观,这就是UI组件的实

  • Android 深入探究自定义view之事件的分发机制与处理详解

    目录 题引 Activity对事件的分发过程 父布局拦截的分发处理过程 ACTION_DOWN 事件 ACTION_MOVE 事件 父布局不拦截时的分发处理过程 ACTION_DOWN ACTION_MOVE 解决冲突方案 外部拦截 内部拦截 本文主要探讨下面几个问题: 学习事件分发机制是为了解决什么问题 Activity对事件的分发过程 父布局拦截的分发处理过程 父布局不拦截时的分发处理过程 冲突解决方案 题引 事件只有一个,多个人想要处理,处理的对象不是我们想给的对象就是事件冲突. 如上图,

  • Android开发之自定义View(视图)用法详解

    本文实例讲述了Android开发之自定义View(视图)用法.分享给大家供大家参考,具体如下: View类是Android的一个超类,这个类几乎包含了所有的屏幕类型.每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展.在游戏开发中往往需要自定义视图(View),这个画布的功能更能满足我们在游戏开发中的需要.在Android中,任何一个View类都只需重写onDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式等. 为了实现自定义View,需要创建

  • Android代码检查规则Lint的自定义与应用详解

    目录 前言: 什么是Lint 自定义Lint流程: 1. 新创建module,Module类型选择Java or Kotlin Library, 暂时命名lint_tools 2. 在build.gradle中引入lint的依赖 3. 本地创建个资源id命名检查规则,用来规范项目中的id统一命名 4. 实现IssueRegistry并添加对应的自定义Issue: 5. 在module(lint_tools)中对应的build.gradle中配置如下信息: 6. 在需要进行lint检查的modul

  • Android启动内置APK和动态发送接收自定义广播实例详解

    Android启动内置APK和动态发送接收自定义广播实例详解 工作中遇到这样一个需求,需要为按键添加一个亲情号,提供一个接口启动内置的APK,思考再三决定更改Framework,利用广播机制去实现. 一.代码动态自主启动内置APK 我们都知道Android系统为我们提供了很多服务管理类,PackageManager主要是管理应用程序包,通过它就可以获取应用程序信息并构建Intent,启动对应的应用.除此之外Android还未我们提供了一些对应的类来管理相关的xml文件,比如说可以通过Packag

随机推荐