驗證控件,自己定義控件-仿iphone之ToggleButtonamp;VoiceSeekBar

 2023-10-07 阅读 17 评论 0

摘要:由于項目中須要使用開關切換button,和聲音滑動控件,可是原生Android5.0版本號以下的控件實在是太挫了。盡管網上已經有非常多關于這兩個控件的blog。可是我實在是找不到像iPhone這樣簡潔樣式的,只是咱們程序猿總不能這點問題就被難道撒所以我決定仿照ip

由于項目中須要使用開關切換button,和聲音滑動控件,可是原生Android5.0版本號以下的控件實在是太挫了。盡管網上已經有非常多關于這兩個控件的blog。可是我實在是找不到像iPhone這樣簡潔樣式的,只是咱們程序猿總不能這點問題就被難道撒···所以我決定仿照iphone的樣式自己寫這兩個控件,。

效果圖例如以下:
這里寫圖片描寫敘述
這里寫圖片描寫敘述

一、ToggleButton

先直接上代碼。后面會一步步分析

package com.zuck.definitionview.views;import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.OvershootInterpolator;/*** 仿iPhone ToggleButton* * 2015-7-13* * @author zuck**/
public class ToggleButton extends View{private float radius;// 開啟顏色private int onColor;// 關閉顏色private int offColor;// 灰色帶顏色private int offBorderColor;// 手柄顏色private int spotColor;// 邊框顏色private int borderColor;// 畫筆private Paint paint ;// 開關狀態private boolean toggleOn = false;// 邊框大小 默覺得2pxprivate int borderWidth = 2;// 垂直中心private float centerY;// button的開始和結束位置private float startX, endX;// 手柄X位置的最小和最大值private float spotMinX, spotMaxX;// 手柄大小private int spotSize ;///  手柄X位置private float spotX;// 關閉時內部灰色帶高度private float offLineWidth;private RectF rect = new RectF();//開關切換監聽器private OnToggleChanged listener;//屬性動畫private ValueAnimator animation;public ToggleButton(Context context) {this(context, null);}public ToggleButton(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public void init() {initPaint();initColor();initAnimation();this.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {toggle();}});}private void initPaint() {//初始化畫筆(抗抖動)paint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制風格為填充paint.setStyle(Style.FILL);//筆觸風格為圓角paint.setStrokeCap(Cap.ROUND);}private void initColor() {onColor = Color.parseColor("#4ebb7f");offColor = Color.parseColor("#dadbda");offBorderColor = Color.parseColor("#ffffff");spotColor = Color.parseColor("#ffffff");//由于開始為關閉狀態。所以這里邊框背景色初始化為關閉狀態顏色borderColor = offColor;}@SuppressLint("NewApi")private void initAnimation() {animation = new ValueAnimator();animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {calculateToggleEffect(Double.parseDouble(animation.getAnimatedValue().toString()));}});//OvershootInterpolator : 結束時會超過給定數值,可是最后一定返回給定值animation.setInterpolator(new OvershootInterpolator(1.5f)); animation.setDuration(500);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);Resources r = Resources.getSystem();if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 55, r.getDisplayMetrics());widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);}if(heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST){heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);final int width = getWidth();final int height = getHeight();//由寬高計算圓角的半徑radius = Math.min(width, height) * 0.5f;centerY = radius;startX = radius;endX = width - radius;spotMinX = startX + borderWidth;spotMaxX = endX - borderWidth;spotSize = height - 4 * borderWidth;spotX = toggleOn ?

spotMaxX : spotMinX; offLineWidth = 0; } private int clamp(int value, int low, int high) { return Math.min(Math.max(value, low), high); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //1.繪制最外層邊框背景-圓角矩形 //繪制圓角矩形背景大小為測量的寬高 rect.set(0, 0, getWidth(), getHeight()); paint.setColor(borderColor); canvas.drawRoundRect(rect, radius, radius, paint); if(offLineWidth > 0){ //1.1繪制整個開關區域中除手柄外的白色區域帶 final float cy = offLineWidth * 0.5f; rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy); paint.setColor(offBorderColor); canvas.drawRoundRect(rect, cy, cy, paint); } //2.繪制圓形手柄邊框區域 rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius); paint.setColor(borderColor); canvas.drawRoundRect(rect, radius, radius, paint); //3.繪制圓形手柄區域 //圓形手柄的半徑大小(不包括邊框) final float spotR = spotSize * 0.5f; rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR); paint.setColor(spotColor); canvas.drawRoundRect(rect, spotR, spotR, paint); } private double mapValueFromRangeToRange(double value, double fromLow, double fromHigh, double toLow, double toHigh) { double fromRangeSize = fromHigh - fromLow; double toRangeSize = toHigh - toLow; double valueScale = (value - fromLow) / fromRangeSize; return toLow + (valueScale * toRangeSize); } /** * @param value */ private void calculateToggleEffect(final double value) { final float mapToggleX = (float) mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX); spotX = mapToggleX; float mapOffLineWidth = (float) mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize); offLineWidth = mapOffLineWidth; //開啟時候的背景色 final int fr = Color.red(onColor); final int fg = Color.green(onColor); final int fb = Color.blue(onColor); //關閉后的背景色 final int tr = Color.red(offColor); final int tg = Color.green(offColor); final int tb = Color.blue(offColor); //border顏色漸變 int sr = (int) mapValueFromRangeToRange(1 - value, 0, 1, fr, tr); int sg = (int) mapValueFromRangeToRange(1 - value, 0, 1, fg, tg); int sb = (int) mapValueFromRangeToRange(1 - value, 0, 1, fb, tb); sr = clamp(sr, 0, 255); sg = clamp(sg, 0, 255); sb = clamp(sb, 0, 255); borderColor = Color.rgb(sr, sg, sb); //重繪 if (Looper.myLooper() == Looper.getMainLooper()) { invalidate(); } else { postInvalidate(); } } @SuppressLint("NewApi") private void takeToggleAction(boolean isOn){ if(isOn) { animation.setFloatValues(0.f, 1.f); } else { animation.setFloatValues(1.f, 0.f); } animation.start(); } /** * 切換開關 */ public void toggle() { toggleOn = !toggleOn; takeToggleAction(toggleOn); if(listener != null){//觸發toggle事件 listener.onToggle(toggleOn); } } /** * 切換為打開狀態 */ public void toggleOn() { toggleOn = true; takeToggleAction(toggleOn); if(listener != null){//觸發toggle事件 listener.onToggle(toggleOn); } } /** * 切換為關閉狀態 */ public void toggleOff() { toggleOn = false; takeToggleAction(toggleOn); if(listener != null){//觸發toggle事件 listener.onToggle(toggleOn); } } /** * 設置顯示成打開樣式。不會觸發toggle事件 */ public void setToggleOn(){ toggleOn = true; calculateToggleEffect(1.0f); } /** * 設置顯示成關閉樣式。不會觸發toggle事件 */ public void setToggleOff() { toggleOn = false; calculateToggleEffect(0.0f); } /** * 狀態切換監聽器 */ public interface OnToggleChanged{ public void onToggle(boolean on); } public void setOnToggleChanged(OnToggleChanged onToggleChanged) { listener = onToggleChanged; } }

這個控件總體看來,還是比較簡單的,開始我們仍然是初始化一些須要用到的畫筆、顏色、以及屬性動畫。

驗證控件?這里畫筆和顏色就不說了,跟切白菜一樣。
簡單說說動畫:為什么要用到屬性動畫,大家能夠細致看看上面的效果圖,當點擊控件的時候手柄會從最左端移動到最右端,完畢一個打開的效果。

或者從最右端移動到最左端。完畢一個關閉的效果。

而且在手柄位移到最左端或者最右端的時候還會有一個回彈的效果。

而這些所有都是依據屬性動畫

    private void initAnimation() {animation = new ValueAnimator();animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {calculateToggleEffect(Double.parseDouble(animation.getAnimatedValue().toString()));}});//OvershootInterpolator : 結束時會超過給定數值,可是最后一定返回給定值animation.setInterpolator(new OvershootInterpolator(1.5f)); animation.setDuration(500);}

這里屬性動畫大家應該都非常熟悉了,假設還有不懂他的基本使用方法的。能夠去這個鏈接去學習下關于屬性動畫的使用:Android屬性動畫全然解析(上)。初識屬性動畫的基本使用方法。如今我們僅僅看OvershootInterpolator這個API。

android studio中button控件。這里寫圖片描寫敘述

從上圖能夠看到OvershootInterpolator繼承BaseInterpolator。那么Interpolator是什么?引用郭霖blog中的一句話:“Interpolator這個東西非常難進行翻譯。直譯過來的話是補間器的意思,它的主要作用是能夠控制動畫的變化速率”。那么按我的理解就是控制view動畫效果的節奏。
從這個繼承關系圖我們能夠看到,Android為我們提供了非常多Interpolator,有興趣的童鞋能夠挨個試一試。

看看他們的效果。這里帶大家看看OvershootInterpolator:

/**
* An interpolator where the change flings forward and overshoots the last value
* then comes back.
*/

這是API中關于OvershootInterpolator的介紹。我的英文稀爛,大致意思是:值在變化時,快結束的時候值會超過給定的最大值,然后再返回給定的最大值。也就是說,當我們有例如以下設置的時候

animation.setFloatValues(0.f, 1.f);

android自定義組合控件,onAnimationUpdate()回調方法最后會按 0.9f->1.0f->1.1f->1.2f->….1.0f。這種方式返回數值。

通過這種數值去控制手柄的執行軌跡從而達到一個回彈的效果。


另一點:在我創建OvershootInterpolator的時候傳入一個tension參數,tension表示張力。默覺得2.0f。tension越大,那么最后超過給定最大邊界值后繼續增大數值也就越大,反之越小。


radiobutton控件單選。

ok,以上就是關于初始化操作。

以下也就是按流程一步一步看了:onMeasure() -> onLayout() -> onDraw();
這些都是老生長談了:

onMeasure

在onMeasure方法中去測量控件的大小這塊邏輯非常easy。

這里僅僅處理了AT_MOST和UNSPECIFIED兩種模式。當我們把寬度或者高度設置為wrap_content或者設置了weight,那么相應我們讓ToggleButton的長寬也為默認值。

其他方式。測量方法中不做干涉。

radiobuttonlist控件的用法、用戶設置的寬高是多少,那么控件的寬高就是多少。

onLayout

在onLayout方法中去計算了該控件在繪制的時候須要用到的幾個參數,比如包括邊框的手柄半徑、手柄的開始位置、結束位置、手柄大小等等…看看也沒啥好說的,都是一系列計算。

onDraw

最后看onDraw方法。控件的繪制主要分為四步,上述代碼中有明確的凝視:

1.繪制控件最外層邊框,這里注意這個邊框是圓矩形,他們的rx 和ry的值也就是在onLayout中計算的radius值
2. 繪制圓角矩形內除去手柄區域以外的區域,該區域的大小是由offLineWidth去控制的,而關于offLineWidth是怎么算出來的,等下會跟大家講明確的
3. 繪制手柄的邊框區域。該區域的位置是由手柄的位置spotX去控制的。而spotX的計算,等下和offLineWidth的計算一并跟大家說明確(ps:為什么要一起,由于計算他們兩個的值都是用的同一種算法)
4. 繪制手柄區域和繪制手柄邊框區域差點兒是一致的,唯一不同的是繪制手柄區域的半徑大小比繪制手柄邊框區域的大小要小2個單位值。由于他們繪制的位置是一樣的,而大小不一樣,這樣也就讓整個手柄有了邊框的效果同一時候看起來更加有扁平感了。

同一時候該手柄區域的位置也是由spotX去控制的

commandbutton控件、到此從init() 到 onMeasure() -> onLayout() -> onDraw()這些方法邏輯思路在大家心中應該是比較清晰明了的。


calculateToggleEffect -> mapValueFromRangeToRange

radiobutton控件默認選中、以下就來說說該控件中最重要的算法問題了,事實上這個算法也是相當的簡單
當我們點擊控件。內部是如何驅動手柄左右位移的呢,圓矩形內顏色是如何轉變的呢?這一切要歸功于calculateToggleEffect()這種方法,上面講述為什么要使用屬性動畫的時候。我曾貼過一小段代碼,在onAnimationUpdate()回調方法中我們調用了calculateToggleEffect()方法,依據屬性動畫回傳給我們的值去計算控制位移、顏色變化等效果的三個參數值

    private void calculateToggleEffect(final double value) {final float mapToggleX = (float) mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX);spotX = mapToggleX;float mapOffLineWidth = (float) mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize);offLineWidth = mapOffLineWidth;//開啟時候的背景色final int fr = Color.red(onColor);final int fg = Color.green(onColor);final int fb = Color.blue(onColor);//關閉后的背景色final int tr = Color.red(offColor);final int tg = Color.green(offColor);final int tb = Color.blue(offColor);//border顏色漸變int sr = (int) mapValueFromRangeToRange(1 - value, 0, 1, fr, tr);int sg = (int) mapValueFromRangeToRange(1 - value, 0, 1, fg, tg);int sb = (int) mapValueFromRangeToRange(1 - value, 0, 1, fb, tb);sr = clamp(sr, 0, 255);sg = clamp(sg, 0, 255);sb = clamp(sb, 0, 255);borderColor = Color.rgb(sr, sg, sb);//重繪if (Looper.myLooper() == Looper.getMainLooper()) {invalidate();} else {postInvalidate();}}

這段方法中。大家能夠看到我們分別去計算了spotX、offLineWidth、borderColor這三個實例變量的值
而計算這三個實例變量的值主要是由mapValueFromRangeToRange()方法來完畢,那么mapValueFromRangeToRange()方法是個什么鬼?

    private double mapValueFromRangeToRange(double value, double fromLow,double fromHigh, double toLow, double toHigh) {double fromRangeSize = fromHigh - fromLow;double toRangeSize = toHigh - toLow;double valueScale = (value - fromLow) / fromRangeSize;return toLow + (valueScale * toRangeSize);}

一眼望去,各種加減乘除,不喜歡算法的童鞋頭都大了。事實上這些運算是相當的簡單,基本的思想就是:以一個范圍內的值,去映射另外一個給定的范圍。通俗講就是:給你一個范圍[0,1]rangeA。在給你一個用于映射的范圍[50,100]rangeB。那么如今給你一個在rangeA中的值 0.3。你去在rangeB中計算出相應比例的值。

這個夠簡單吧。

result = 50 + ((0.3-0) / (1- 0))*(100 - 50) ;

上面的代碼中僅僅是將每一步拆分開來,大家能夠合在一起看看是不是與上面的式子相等,ok,這樣我們就計算出spotX和offLineWidth的值,所以spotX的值也就是會在spotMinX 到 spotMaxX的范圍中遞增或者遞減(由于屬性動畫回傳過來的值也是遞增或者遞減)。同理offLineWidth和背景色的計算也是一樣。我們搞懂mapValueFromRangeToRange后calculateToggleEffect()方法中的邏輯就非常非常easy了。我就不累贅了~那么到此為止ToggleButton的實現已經帶大家所有分析完畢。

html定義滑塊控件類型。

二、VoiceSeekBar

直接上代碼
package com.zuck.definitionview.views;import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;import com.zuck.definitionview.R;/*** 仿iPhone VoiceSeekBar* * 2015-7-16* * @author zuck**/
@SuppressLint("NewApi")
public class VoiceSeekBar extends View {/*** 手柄半徑比率*/private static final float SPOTRADIUSRATE = 13.f/40.f;/*** 橫條默認高度*/private static final int BAR_HEIGHT = 2;/*** 默認間隙寬度*/private static final int SPACE = 30;/*** 最高級別[級別從0開始,而且級別的段數則為:MAXLEVEL + 1]*/private static final int MAXLEVEL = 15;/*** 條形棒寬度*/private int barWidth;/*** 手柄的區域*/private Region spotRegion;/*** 繪制條形棒的矩形*/private Rect rect;// 左邊條形棒的顏色,右邊條形棒的顏色,手柄的顏色private int leftBarColor, rightBarColor, spotColor;// 手指按下的時候x坐標private float pressX;// 條形棒矩形的左上角定點private int barLeft ,barTop;// 左側符號的X坐標,Y坐標private int signX,signY;// 手柄半徑private float radius;private Paint paint, spotPaint;private Bitmap signLeft, signRigh;private int currentLevel;private OnSeekBarChangeListener onSeekBarChangeListener;public interface OnSeekBarChangeListener {void onProgressChanged(VoiceSeekBar seekBar, int progress);void onStartTrackingTouch(VoiceSeekBar seekBar);void onStopTrackingTouch(VoiceSeekBar seekBar);}public VoiceSeekBar(Context context) {this(context, null);}public VoiceSeekBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public VoiceSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 關閉硬件加速setLayerType(LAYER_TYPE_SOFTWARE, null);  init();}private void init() {//初始化當前級別currentLevel = 0;// 初始化載入左邊與右邊的兩張符號圖標signLeft = BitmapFactory.decodeResource(getResources(), R.drawable.minus);signRigh = BitmapFactory.decodeResource(getResources(), R.drawable.plus);// 手柄左邊/右邊的條形棒顏色leftBarColor = Color.parseColor("#4ebb7f");rightBarColor = Color.parseColor("#dadbda");// 手柄顏色spotColor = rightBarColor;paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);paint.setStyle(Paint.Style.FILL);paint.setStrokeCap(Paint.Cap.ROUND);spotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);spotPaint.setColor(spotColor);spotPaint.setStyle(Paint.Style.FILL);// 為手柄加入陰影效果spotPaint.setShadowLayer(10, 0, 1, Color.DKGRAY);// 手柄所在的區域spotRegion = new Region();rect = new Rect();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {Resources r = Resources.getSystem();//測量高度,高度默覺得35int heigthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, r.getDisplayMetrics());heightMeasureSpec = MeasureSpec.makeMeasureSpec(heigthSize, MeasureSpec.EXACTLY);//測量寬度int widthSize = MeasureSpec.getSize(widthMeasureSpec);int minWidth = (int) (signLeft.getWidth() + signRigh.getWidth() + SPACE * 4 +  heigthSize * SPOTRADIUSRATE);widthSize = Math.max(minWidth, widthSize);widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {super.onLayout(changed, left, top, right, bottom);final int width = getWidth();final int height = getHeight();//手柄的半徑radius = height * SPOTRADIUSRATE;//條形棒寬度barWidth = (int) (width - (signLeft.getWidth() + signRigh.getWidth() + SPACE * 4 + radius));//計算條形棒開始繪制的左上角頂點位置barLeft = (int) Math.ceil((width - barWidth) / 2.f);barTop = (int) Math.ceil((height - BAR_HEIGHT) / 2.f);//計算左邊符號繪制的位置signX = (int) (barLeft - signLeft.getWidth() - SPACE);signY = (height - signLeft.getHeight()) / 2;calcCurrentPressX();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//0.繪制左右兩邊的符號canvas.drawBitmap(signLeft, signX, signY, paint);canvas.drawBitmap(signRigh, barWidth + barLeft + SPACE, signY, paint);//1.繪制圓形手柄//計算手柄圓點坐標x值float cx = 0;float cy = barTop + BAR_HEIGHT / 2; if(pressX <= radius + barLeft){//手指觸摸的范圍在:從最左側到橫條開始的一個手柄半徑距離范圍cx = barLeft + radius;}else if (pressX > barLeft + radius && pressX <= barWidth + barLeft - radius){//手指觸摸點在 橫條最左側開始加上一個手柄半徑距離到橫條最右側減去一個手柄半徑距離范圍之內cx = pressX;} else {//手指觸摸點超過橫條最右側減去一個手柄半徑距離的范圍cx = barLeft + barWidth - radius;}canvas.drawCircle(cx, cy, radius, spotPaint);//2.繪制左邊的條形棒int leftBarRight = (int) (cx - radius);int barBottom = barTop + BAR_HEIGHT;rect.set(barLeft, barTop, leftBarRight, barBottom);paint.setColor(leftBarColor);canvas.drawRect(rect, paint);//3.繪制右邊的條形棒int rightBarLeft = (int) (leftBarRight + radius * 2);rect.set(rightBarLeft, barTop, barWidth + barLeft, barBottom);paint.setColor(rightBarColor);canvas.drawRect(rect, paint);//4.記錄當前手柄所在的區域(區域的范圍擴大一個半徑范圍)int regionLeft = (int)(cx - 2 * radius);spotRegion.set(regionLeft, (int) -radius, (int)(radius * 4) + regionLeft, (int)(radius * 4));}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {getParent().requestDisallowInterceptTouchEvent(true);return super.dispatchTouchEvent(ev);}@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:pressX = event.getX();float pressY = event.getY();if(!spotRegion.contains((int)pressX, (int)pressY)) {return false;}if(onSeekBarChangeListener != null) onSeekBarChangeListener.onStartTrackingTouch(this);break;case MotionEvent.ACTION_MOVE:pressX = event.getX();int level = CalcCurrentLevel();if(onSeekBarChangeListener != null && currentLevel != level) {currentLevel = level;onSeekBarChangeListener.onProgressChanged(this, level);}invalidate();break;case MotionEvent.ACTION_UP:if(onSeekBarChangeListener != null) onSeekBarChangeListener.onStopTrackingTouch(this);break;}return true;}/*** 設置當前級別* @param currentLevel*/public void setCurrentLevel(int currentLevel) {if(currentLevel < 0){this.currentLevel = 0;} else {this.currentLevel = currentLevel >= MAXLEVEL ?

togglebutton控件, MAXLEVEL : currentLevel; } } /** * 獲取當前級別 * @return */ public int getCurrentLevel() { return currentLevel; } /** * 依據當前級別計算當前pressX值(pressX用來計算手柄所在位置) */ private void calcCurrentPressX() { pressX = barWidth * ((float)currentLevel / MAXLEVEL) + barLeft; } /** * 計算當前的級別 * @return */ private int CalcCurrentLevel() { int level ; if(pressX <= radius + barLeft){//手指觸摸的范圍在:從最左側到橫條開始的一個手柄半徑距離范圍 level = 0; }else if (pressX > barLeft + radius && pressX <= barWidth + barLeft - radius){//手指觸摸點在 橫條最左側開始加上一個手柄半徑距離到橫條最右側減去一個手柄半徑距離范圍之內 level = (int) (((pressX - barLeft) * MAXLEVEL) / barWidth); } else {//手指觸摸點超過橫條最右側減去一個手柄半徑距離的范圍 level = MAXLEVEL; } return level; } public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener) { this.onSeekBarChangeListener = onSeekBarChangeListener; } }

該控件實現邏輯思路與ToggleButton差點兒一致。

我也就不打算講述了。記錄在這里,供以后方便查看。以上代碼。能夠直接copy粘貼到項目中使用。

改下包名就可以。


代碼傳送門

轉載于:https://www.cnblogs.com/liguangsunls/p/7220968.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/125744.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息