前言
第一次書寫技術類的文章,希望能夠將自己的所學和成長記錄下來。
內容
由於公司的專案需要,需要製作一個指標滑動的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)