前言
第一次书写技术类的文章,希望能够将自己的所学和成长记录下来。
内容
由于公司的项目需要,需要制作一个指针滑动的seekbar,如图。
查阅了网上大量的相关实例和知识,在此进行一个总结,如有问题希望大家能够提出。
功能需求是这样的:需要一个带有标尺的滑动条,滑动部分为游标而不是常见的滑动背景的标尺,滑动游标的时候其他的如TextView的控件可以直接拿到当前游标所指的值。因此考虑首先需要一个标尺,本来考虑标尺的展示使用图片,但是后来考虑到适配的问题较为繁琐,因此采用自定义控件的时候画出来。而游标方面,也考虑样式固定化,也采用的是绘制的方式。
绘制部分代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//画最下面的线
canvas.drawLine(PADDING_LEFT,getHeight(),widthOfProgress,getHeight(),mLinePaint);
for (int i = minProgress; i <= maxProgress; i=i+oneItemValue) {
if (i % oneGroupValue == 0 || i == minProgress) {
//起点x坐标10像素,画厘米线
canvas.drawLine(PADDING_LEFT, getHeight(), PADDING_LEFT, getHeight()/2+16*scaleX, mLinePaint);
//计算刻度数
String text = i + "";
Rect rect = new Rect();
//获取文本宽度
float txtWidth = mTextPaint.measureText(text);
mTextPaint.getTextBounds(text, 0, text.length(), rect);
//画标尺的数字
canvas.drawText(text, PADDING_LEFT - txtWidth / 2, getHeight()/2+16*scaleX-rect.height() , mTextPaint);
} else if (i % oneItemValue == 0) {
//每隔小单位画间隔线,lineHeight越长,线越短
double lineHeight = (getHeight()/2)*0.6;
canvas.drawLine(PADDING_LEFT, getHeight(), PADDING_LEFT, getHeight()/2+(float)lineHeight, mLinePaint);
}
// else {
// //画毫米线
// double lineHeight = (getHeight()/2)*0.4;
// canvas.drawLine(44, getHeight(), 44, getHeight()/2+(float)lineHeight, mLinePaint);
// }
//每隔一个单位像素移动一次达到划线效果
canvas.translate(widthOfItem, 0);
}
canvas.restore();
//画红线游标
canvas.drawLine(xProgress, getHeight()/2, xProgress, getHeight(), mRulerPaint);
Log.i("--++",cursorRadius+"");
canvas.drawCircle(xProgress, getHeight()/2, cursorRadius, mRulerPaint);
//测试用显示progress
// BigDecimal bd = new BigDecimal((progrees - 18) / 180);
// bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
// mTextPaint.setTextSize(36);
// float cursorTextWidth = mTextPaint.measureText(bd.floatValue()+"");
// canvas.drawText(progrees+"", getWidth()/2-cursorTextWidth/2, cursorTextWidth, mTextPaint);
}
attrs文件中的内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RangeSeekBar">
<attr name="line_color" format="color"></attr>
<attr name="cursor_color" format="color"></attr>
<attr name="text_color" format="color"></attr>
<attr name="max_progress" format="integer"></attr>
<attr name="min_progress" format="integer"></attr>
<attr name="oneitem_value" format="integer"></attr>
<attr name="one_group_value" format="integer"></attr>
<attr name="current_progress" format="float"></attr>
<attr name="linewidth" format="float"></attr>
<attr name="scrollable" format="boolean"></attr>
</declare-styleable>
</resources>
由于项目需要,该控件中的scrollable功能默认设置是true的,如果改为false,那么该控件就变成只能选择最大值和最小值。
该控件还做了滑动处理,当滑动到刻度中间时就自动滑动到附近最近的线上面。该部分的处理在该控件的事件处理当中,代码如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getX() > PADDING_LEFT + widthOfProgress || event.getX() < PADDING_LEFT){
isCanMove = false;
}else{
isCanMove = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (!isCanMove) {
return false;
}
float x = event.getX();
if (x<PADDING_LEFT || x>widthOfProgress){
return false;
}
if (scrollable){
xProgress = x;
progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
if (onRangeRulerChangeListener != null){
onRangeRulerChangeListener.onValueChanged((int)progrees);
}
invalidate();
}else{
if (x < widthOfProgress / 2){
xProgress = PADDING_LEFT;
}else{
xProgress = widthOfProgress;
}
progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
if (onRangeRulerChangeListener != null){
onRangeRulerChangeListener.onValueChanged((int)progrees);
}
invalidate();
}
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
float x1 = event.getX();
if (scrollable){
int number = Math.round((x1 - PADDING_LEFT)/ widthOfItem);
if (number < 0){
number = 0;
}else if (number >= numberOfItem){
number = numberOfItem;
}
xProgress = PADDING_LEFT + number * widthOfItem;
invalidate();
}else{
if (x1 >= widthOfProgress/2){
xProgress = PADDING_LEFT + numberOfItem * widthOfItem;
}else{
xProgress = PADDING_LEFT;
}
invalidate();
}
progrees = minProgress+Math.round((xProgress-PADDING_LEFT)/widthOfItem)*oneItemValue;
if (onRangeRulerChangeListener != null){
onRangeRulerChangeListener.onValueChanged((int)progrees);
}
break;
}
return true;
}
最后放上Main的逻辑,可以用来设置该控件的默认值和一些属性:
Main.java方面:
public class MainActivity extends AppCompatActivity {
private RangeSeekBar rangeSeekBar;
private TextView tv1;
private RangeSeekBar dayView;
private TextView tvDay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rangeSeekBar = (RangeSeekBar)findViewById(R.id.seekbar_money);
tv1 = (TextView)findViewById(R.id.tv_money);
dayView = (RangeSeekBar) findViewById(R.id.seekbar_day);
tvDay = (TextView) findViewById(R.id.tv_day);
rangeSeekBar.setOnRangeRulerChangeListener(new OnRangeRulerChangeListener() {
@Override
public void onValueChanged(int value) {
tv1.setText(value+"");
}
});
//设置当前值一定要在设置了监听之后
rangeSeekBar.setCurrentProgress(500);
dayView.setOnRangeRulerChangeListener(new OnRangeRulerChangeListener() {
@Override
public void onValueChanged(int value) {
tvDay.setText(value+"");
}
});
}
}
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"
android:background="#f0f0f0">
<LinearLayout
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="0dp"
android:layout_weight="4">
<TextView
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:id="@+id/tv_credirLine"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="17sp"/>
<RelativeLayout
android:layout_marginTop="15dp"
android:id="@+id/rl_money"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_centerVertical="true"
android:id="@+id/iv_money"
android:layout_width="20dp"
android:layout_height="20dp"
/>
<TextView
android:layout_marginLeft="15dp"
android:layout_toRightOf="@+id/iv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="借款金额"
android:textSize="16sp"/>
</RelativeLayout>
<RelativeLayout
android:padding="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
>
<TextView
android:layout_centerHorizontal="true"
android:id="@+id/tv_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="300"
android:textSize="18sp"/>
<TextView
android:layout_marginLeft="15dp"
android:layout_centerHorizontal="true"
android:layout_toRightOf="@+id/tv_money"
android:id="@+id/tv_1"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#333333"
android:text="元"
android:textSize="15sp"/>
</RelativeLayout>
<RelativeLayout
android:layout_marginTop="15dp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
>
<com.moyu.wh.testcustomseekbar.customview.RangeSeekBar
android:id="@+id/seekbar_money"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:line_color="#dddddd"
app:text_color="#dddddd"/>
<!--<ImageView-->
<!--android:layout_centerInParent="true"-->
<!--android:layout_width="15dp"-->
<!--android:layout_height="50dp"-->
<!--android:layout_alignParentBottom="true"-->
<!--android:src="@drawable/ic_scoll_line"-->
<!--/>-->
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_marginTop="50dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="0dp"
android:layout_weight="3">
<RelativeLayout
android:layout_marginTop="25dp"
android:id="@+id/rl_day"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_centerVertical="true"
android:id="@+id/iv_day"
android:layout_width="24dp"
android:layout_height="24dp"
/>
<TextView
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/iv_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="借款期数"
android:textSize="16sp"/>
</RelativeLayout>
<RelativeLayout
android:padding="15dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_centerHorizontal="true"
android:id="@+id/tv_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="7"
android:textSize="18sp"/>
<TextView
android:layout_centerVertical="true"
android:layout_marginLeft="3dp"
android:layout_toRightOf="@+id/tv_day"
android:layout_centerHorizontal="true"
android:id="@+id/tv_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#333333"
android:text="天"
android:textSize="15sp"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_day"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<com.moyu.wh.testcustomseekbar.customview.RangeSeekBar
android:id="@+id/seekbar_day"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:max_progress="14"
app:min_progress="7"
app:one_group_value="7"
app:oneitem_value="1"
app:scrollable="false"
app:current_progress="7"
app:line_color="#dddddd"
app:text_color="#dddddd"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_marginTop="50dp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
>
</LinearLayout>
</LinearLayout>
谢谢阅读,如果有问题,请提出,我必定会虚心请教。代码可能会有我没有发现的问题,敬请见谅。
Demo位置(CSDN)