自定义实现了一个选定范围值的控件,刻度尺为等分的100等分。模仿二手车app中筛选选定区间的控件。


实现效果:

首先将这个控件分成四部分,位置标记,刻度,尺子,游标。


Android RangeSeekBarView 选定范围值的控件_ide

Android RangeSeekBarView 选定范围值的控件_控件_02

一,测量子控件的尺度,继承onMeasure().由子空间的尺寸来确定控件的尺寸。


[java]  ​​view plain​​​  ​​copy​

 

​​​​ ​​​

  1. @Override
  2. protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. //测量子控件
  4. super.onMeasure(widthMeasureSpec, heightMeasureSpec);

  5. int mWidth=MeasureSpec.getSize(widthMeasureSpec);
  6. 2;
  7. mLeftLimit=proPaddingLeftAndRight;
  8. mRightLimit=mWidth-proPaddingLeftAndRight;

  9. //位置标记的高度+尺子的刻度高度+尺子的高度+游标的高度
  10. setMeasuredDimension(mWidth,mThumbPlaceHeight+RULE_HEIGHT_PX+mProgressBarHeight+mThumbLeft.getMeasuredHeight());
  11. }



二,对控件进行布局。

由于游标需要去监听他的滑动事件,所以这里自定义一个游标控件ThumbView。


[java]    ​​view plain​​​    ​​copy​​    

​​​​​ ​​​​
1. @Override
2. protected void onLayout(boolean changed, int l, int t, int r, int b) {
3. int heightSum=0;
4.
5. heightSum+=mThumbPlaceHeight;
6.
7. heightSum+=RULE_HEIGHT_PX;
8.
9. heightSum+=mProgressBarHeight;
10.
11. float)mMaxValue; //计算一份所占的宽度 一定要用float
12.
13. //设置可以移动的范围
14. 0,heightSum,mThumbLeft.getMeasuredWidth(),b-10); //设置在父布局的位置
15.
16. mThumbRight.setLimit(mLeftLimit,mRightLimit);
17. 0,heightSum,mThumbLeft.getMeasuredWidth(),b-10);
18.
19. //layout调用后调用的方法,比如设置thumb limit
20. }


三,绘制尺子,尺子的刻度,尺子上的位置标记控件。

绘制尺子,其中mProBaseline为尺子top的位置。尺子的range实际是两个不同的矩形框。


[java]    ​​view plain​​​    ​​copy​​    

​​​​​ ​​​​
1. /**
2. * 画尺子
3. *@param canvas
4. */
5. protected void drawProgressBar(Canvas canvas){
6. //画背景
7. new Paint();
8. true);
9. paint.setColor(getResources().getColor(R.color.grey));
10. new Rect(mLeftLimit,mProBaseline,mRightLimit,mProBaseline+mProgressBarHeight);
11. canvas.drawRect(rect,paint);
12.
13. //画进度
14. paint.setColor(getResources().getColor(R.color.blue));
15. new Rect(mThumbLeft.getCenterX(),mProBaseline,mThumbRight.getCenterX(),mProBaseline+mProgressBarHeight);
16. canvas.drawRect(rect,paint);
17. }

绘制刻度,这里将尺子分成了100等分,然后前5份和后5份不画,中间部分每两份画一条线,每10份画一条长线。


[java]    ​​view plain​​​    ​​copy​​    

​​​​​ ​​​​
1. protected void drawRule(Canvas canvas){
2. new Paint();
3. 1);
4. paint.setColor(getResources().getColor(R.color.grey));
5. 20);
6. paint.setTextAlign(Paint.Align.CENTER);
7. true);
8.
9. //一次遍历两份,绘制的位置都是在奇数位置
10. for(int i=5;i<=mMaxValue;i+=2){
11. if(i<PART_ITEM||i>mMaxValue-PART_ITEM){
12. continue;
13. }
14.
15.
16. float degX=mLeftLimit+i*mPartWidth;
17. int degY;
18.
19. if((i-PART_ITEM)%(PART_ITEM*2)==0){
20. degY=mProBaseline-DensityUtil.dip2px(getContext(),LONGLINE_HEIGHT);
21. 5)/10]+unitStr,degX,degY,paint); //画文字
22. else{
23. degY=mProBaseline-DensityUtil.dip2px(getContext(),SHORTLINE_HEIGHT);
24. }
25. canvas.drawLine(degX,mProBaseline,degX,degY,paint);
26. }
27. }

绘制标记控件,根据游标ThumbView的isMoving属性来判断是否需要绘制标记数值。isMoving true绘制,false 不 绘制



[java]    ​​view plain​​​    ​​copy​​        
​​​​​ ​​​​
1. /**
2. * 画 Thumb 位置的数值(标记)
3. */
4. protected void drawRodPlaceValue(Canvas canvas,ThumbView thumbView){
5. int centerX=thumbView.getCenterX();
6. new Paint();
7. BitmapDrawable bd= (BitmapDrawable) mThumbPlaceDrawable;
8. 2,0,paint);
9.
10. paint.setColor(Color.WHITE);
11. paint.setTextAlign(Paint.Align.CENTER);
12. 30);
13. "",centerX,mThumbDrawable.getIntrinsicHeight()/2,paint);
14. }



四,相关计算

要根据游标在刻度尺上的位置,计算进度。


[java]    ​​view plain​​​    ​​copy​​    

​​​​​ ​​​​
1. private int geneareThumbValue(ThumbView view){
2. //todo 这里只是计算了100之多少的值,需要自行转换成刻度上的值
3. int proValue=mMaxValue*(view.getCenterX()-mLeftLimit)/(mRightLimit-mLeftLimit);
4. return proValue;
5. }



五,游标控件 ThumbView


1,mLeftLimit, mRightLimit是游标能浮动的位置。

2,当Touch事件为move时,来改变自身的位置。


[java]    ​​view plain​​​    ​​copy​​    


​​​​​ ​​​​
1. package com.jayce.uidefine;
2.
3. import android.content.Context;
4. import android.graphics.Rect;
5. import android.util.AttributeSet;
6. import android.view.MotionEvent;
7. import android.widget.ImageView;
8.
9. /**
10. * @author jayce
11. * @date 2015/3/12
12. */
13. public class ThumbView extends ImageView {
14.
15. private RangeSeekBar rangeSeekBar;
16.
17. private int mDownX=0;
18. private int mWidth;
19.
20. private int mLeftLimit=0;
21. private int mRightLimit=Integer.MAX_VALUE;
22.
23. private Rect rect;
24.
25. private int mCenterX; //游标的中心位置
26.
27. private boolean mIsMoving; //游标是否正在移动
28.
29. private OnThumbListener listener;
30.
31. public ThumbView(Context context) {
32. this(context, null);
33. }
34.
35. public ThumbView(Context context, AttributeSet attrs) {
36. this(context, attrs, 0);
37. }
38.
39. public ThumbView(Context context, AttributeSet attrs, int defStyleAttr) {
40. super(context, attrs, defStyleAttr);
41. }
42.
43. public void setRangeSeekBar(RangeSeekBar rangeSeekBar) {
44. this.rangeSeekBar = rangeSeekBar;
45. }
46.
47. public void setLimit(int mLeftLimit,int mRightLimit) {
48. this.mLeftLimit = mLeftLimit;
49. this.mRightLimit=mRightLimit;
50. }
51.
52. public int getCenterX() {
53. return mCenterX;
54. }
55.
56. /**
57. * 设置中心位置,不超过左右的limit,就刷新整个控件,并且回调onThumbChange()
58. * @param centerX
59. */
60. public void setCenterX(int centerX) {
61. int left=centerX-mWidth/2,right=centerX+mWidth/2;
62. if(centerX<mLeftLimit) {
63. 2;
64. 2;
65. }
66.
67. if(centerX>mRightLimit){
68. 2;
69. 2;
70. }
71.
72. this.mCenterX = (left+right)/2;
73.
74. if(left!=rect.left||right!=rect.right){
75. rect.union(left,rect.top,right,rect.bottom);
76. layout(left, rect.top, right, rect.bottom);
77. //invalidate(rect);
78. rangeSeekBar.invalidate();
79.
80. if(listener!=null){
81. 100*((left+right)/2-mLeftLimit)/(mRightLimit-mLeftLimit));
82. }
83. }
84. }
85.
86. public boolean isMoving() {
87. return mIsMoving;
88. }
89.
90. public void setOnThumbListener(OnThumbListener listener) {
91. this.listener = listener;
92. }
93.
94. @Override
95. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
96. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
97.
98. mWidth=getMeasuredWidth();
99. }
100.
101. @Override
102. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
103. super.onLayout(changed, left, top, right, bottom);
104.
105. new Rect(left,top,right,bottom);
106. }
107.
108. @Override
109. public boolean onTouchEvent(MotionEvent event) {
110. switch (event.getAction()) {
111. case MotionEvent.ACTION_DOWN:
112. int) event.getX();
113. false;
114.
115. break;
116. case MotionEvent.ACTION_MOVE:
117. int nowX = (int) event.getX();
118.
119. int left = rect.left + nowX - mDownX;
120. int right = rect.right + nowX - mDownX;
121. true;
122. 2);
123. break;
124. case MotionEvent.ACTION_UP:
125. false;
126. rangeSeekBar.invalidate();
127. break;
128. }
129. return true;
130. }
131.
132. public interface OnThumbListener{
133. public void onThumbChange(int i);
134. }
135. }




使用中的问题:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="vertical">

</LinearLayout>

<com.jayce.uidefine.RangeSeekBar
style="@style/progress_horizontal_my"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>


</LinearLayout>

这样的布局会出现底部剪头不显示的问题,如下即可解决:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="vertical">
<com.jayce.uidefine.RangeSeekBar
style="@style/progress_horizontal_my"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


</LinearLayout>