Android——自定义带刻度的SeekBar单向拖动条
2017-04-21 11:29
507 查看
时间过得真快,才发现好久没来逛逛了。没写博客的这段时间一直在做项目,连续完成了两个大型app,这个过程很享受,这是独立开发的,所以中途有很多很多的问题需要自己一个一个的去解决,现在接近尾声了,发现自己在这个阶段成长了不少,当然需要学习的知识还有很多很多,就让我们大家一起学习吧!
今天就分享一个自己在项目中,客户要求的功能,拖动条设置ListView列表中item的金额。这边主要的就是说seekbar这个东西,那我们开始吧!
看下效果:
大概就是这样,上面的刻度值是可以动态设置的,下面详细说一下,先看下自定义的代码块:
取拖动条的最大值代码块:
这边的maxValue 值是从缓存中取出来的,如果在缓存中这个key(maxValue)为空的话,默认值为0,为0的时候从本地的配置文件arrays.xml中拿取默认的最大值(100),该文件放置在values目录下面,代码如下:
还有一段就是获取刻度的array。
一样的 存储方式,放在SharePreferences内存当中,默认为null,默认获取的就是上面的arrays.xml文件。不为空则在本内存中获取,那么这边可能有人会问,SharePreferences怎么存储和获取数组元素,我这边贴出来一下:
上述是我项目中用到的,需要的可以看一下,另外分享一个小技巧,如果你想知道app中存入在Sp内存中的数据怎么看呢?可以在android studio中去查看,工具导航栏Tools–>Android–>Android Device Monitor ,打开之后点击右侧导航菜单的File Explorer ,找到data–>data–>自己的项目包名–>shared_prefs–>自己定义的sp名字,这边定义的是sp_cache,然后该目录下就存在一个sp_cache.xml的文件,点击右上角的图标
第一个导出电脑就可以查看了。好了,原归正传:
我怎么动态设置数组呢?先看下效果图:
不知你们有没有发现一些细节,每次拖动条上面的刻度文字跟跳转另一个activity输入框的数字是顺序大小一样,还有动态去设置刻度值得时候我没有从0开始,而是大于0的数字开始,我也做了启用停用的开关,停用之后不能使用拖动条功能,这个状态也是保存在SP内存里面的。
接下来看一下拖动条的对话框,我使用fragment做的,这边也一起说一下吧!
代码如下:
那怎么使用这个fragment呢?首先不用思考的,其父类必须继承FragmentActivity,然后在父类的layout中必须有
然后可以通过view来触发这个fragment。
需要注意的是要用V4的getSupportFragmentManager。
MyDialogFragment 的layout中需要使用自己定义的seekbar,如下:
该控件上的属性封装在attrs.xml中:
这边要注意progressHintResId属性,设置刻度提示背景必须使用.9的图片。
另一个跳转的activity页面我就不贴代码了,逻辑很简单,把输入的数字封装成数组,存入SP中,然后你每次弹出拖动条对话框,它都会去判断缓存中是否有值。
好了,今天先分享这个,感谢!
今天就分享一个自己在项目中,客户要求的功能,拖动条设置ListView列表中item的金额。这边主要的就是说seekbar这个东西,那我们开始吧!
看下效果:
大概就是这样,上面的刻度值是可以动态设置的,下面详细说一下,先看下自定义的代码块:
package com.ds.platform.view; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.NinePatch; import android.graphics.Paint; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Typeface; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.ds.platform.R; import com.ds.platform.utils.SharePreferencesUtils; /** * @author: Allen * @date: 2017/3/13 * @description: 自定义SeekBar 带刻度 */ public class RangeSeekBar extends View { private static final float DEFAULT_RADIUS = 0.5f; //default seekbar's padding left and right private int DEFAULT_PADDING_LEFT_AND_RIGHT; private int defaultPaddingTop; //进度提示的背景 The background of the progress private final int mProgressHintBGId; // 按钮的背景 The background of the Drag button private final int mThumbResId; //刻度模式:number根据数字实际比例排列;other 均分排列 //Scale mode: // number according to the actual proportion of the number of arranged; // other equally arranged private final int mCellMode; //single是Seekbar模式,range是RangeSeekbar //single is Seekbar mode, range is angeSeekbar //single = 1; range = 2 private final int mSeekBarMode; //默认为1,当大于1时自动切回刻度模式 //The default is 1, and when it is greater than 1, // it will automatically switch back to the scale mode private int cellsCount = 1; //刻度与进度条间的间距 //The spacing between the scale and the progress bar private int textPadding; //进度提示背景与按钮之间的距离 //The progress indicates the distance between the background and the button private int mHintBGPadding; private int mSeekBarHeight; private int mThumbSize; //两个按钮之间的最小距离 //The minimum distance between two buttons private int reserveCount; private int mCursorTextHeight; private int mPartLength; private int heightNeeded; private int lineCorners; private int lineWidth; //选择过的进度条颜色 // the color of the selected progress bar private int colorLineSelected; //未选则的进度条颜色 // the color of the unselected progress bar private int colorLineEdge; //The foreground color of progress bar and thumb button. private int colorPrimary; //The background color of progress bar and thumb button. private int colorSecondary; //刻度文字与提示文字的大小 //Scale text and prompt text size private int mTextSize; private int mTextColor; private int lineTop, lineBottom, lineLeft, lineRight; //进度提示背景的高度,宽度如果是0的话会自适应调整 //Progress prompted the background height, width, // if it is 0, then adaptively adjust private float mHintBGHeight; private float mHintBGWith; private float offsetValue; private float cellsPercent; private float reserveValue; private float reservePercent; private float maxValue, minValue; //真实的最大值和最小值 //True maximum and minimum values private float mMin, mMax; private boolean isEnable = true; private final boolean mHideProgressHint; //刻度上显示的文字 private CharSequence[] mTextArray; private Bitmap mProgressHintBG; private Paint mMainPaint = new Paint(); private Paint mCursorPaint = new Paint(); private Paint mProgressPaint; private RectF line = new RectF(); private SeekBar leftSB; private SeekBar rightSB; private SeekBar currTouch; private OnRangeChangedListener callback; public RangeSeekBar(Context context) { this(context, null); } public RangeSeekBar(Context context, AttributeSet attrs) { super(context, attrs); TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.RangeSeekBar); cellsCount = t.getInt(R.styleable.RangeSeekBar_cells, 1); reserveValue = t.getFloat(R.styleable.RangeSeekBar_reserve, 0); mMin = t.getFloat(R.styleable.RangeSeekBar_min, 0);//最小取值 //从缓存中拿值 float maxValue = SharePreferencesUtils.getFloat(context, "maxValue", 0); if (0 == maxValue) { mMax = t.getFloat(R.styleable.RangeSeekBar_max, 100);//最大取值 } else { mMax = maxValue; } mThumbResId = t.getResourceId(R.styleable.RangeSeekBar_seekBarResId, 0); mProgressHintBGId = t.getResourceId(R.styleable.RangeSeekBar_progressHintResId, 0); colorLineSelected = t.getColor(R.styleable.RangeSeekBar_lineColorSelected, 0xFF4BD962); colorLineEdge = t.getColor(R.styleable.RangeSeekBar_lineColorEdge, 0xFF0000); colorPrimary = t.getColor(R.styleable.RangeSeekBar_thumbPrimaryColor, 0); colorSecondary = t.getColor(R.styleable.RangeSeekBar_thumbSecondaryColor, 0); //从缓存中拿值 CharSequence tempArray[] = SharePreferencesUtils.getStringSet(context, "textArray", null); if (tempArray == null || tempArray.length == 0) { mTextArray = t.getTextArray(R.styleable.RangeSeekBar_markTextArray); } else { mTextArray = tempArray; } mHideProgressHint = t.getBoolean(R.styleable.RangeSeekBar_hideProgressHint, false); textPadding = (int) t.getDimension(R.styleable.RangeSeekBar_textPadding, dp2px(context, 20)); mTextSize = (int) t.getDimension(R.styleable.RangeSeekBar_textSize, dp2px(context, 12)); mTextColor = t.getColor(R.styleable.RangeSeekBar_textColor, ContextCompat.getColor(context, R.color.main_text)); mHintBGHeight = t.getDimension(R.styleable.RangeSeekBar_hintBGHeight, 0); mHintBGWith = t.getDimension(R.styleable.RangeSeekBar_hintBGWith, 0); mSeekBarHeight = (int) t.getDimension(R.styleable.RangeSeekBar_seekBarHeight, dp2px(context, 2)); mHintBGPadding = (int) t.getDimension(R.styleable.RangeSeekBar_hintBGPadding, 0); mThumbSize = (int) t.getDimension(R.styleable.RangeSeekBar_thumbSize, dp2px(context, 26)); mCellMode = t.getInt(R.styleable.RangeSeekBar_cellMode, 0); mSeekBarMode = t.getInt(R.styleable.RangeSeekBar_seekBarMode, 2); if (mSeekBarMode == 2) { leftSB = new SeekBar(-1); rightSB = new SeekBar(1); } else { leftSB = new SeekBar(-1); } // if you don't set the mHintBGWith or the mHintBGWith < default value, if will use default value if (mHintBGWith == 0) { DEFAULT_PADDING_LEFT_AND_RIGHT = dp2px(context, 25); } else { DEFAULT_PADDING_LEFT_AND_RIGHT = Math.max((int) (mHintBGWith / 2 + dp2px(context, 5)), dp2px(context, 25)); } setRules(mMin, mMax, reserveValue, cellsCount); initPaint(); initBitmap(); t.recycle(); defaultPaddingTop = mSeekBarHeight / 2; mHintBGHeight = mHintBGHeight == 0 ? (mCursorPaint.measureText("国") * 3) : mHintBGHeight; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); heightNeeded = 2 * (lineTop) + mSeekBarHeight; /** * onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值 * MeasureSpec.EXACTLY 是精确尺寸 * MeasureSpec.AT_MOST 是最大尺寸 * MeasureSpec.UNSPECIFIED 是未指定尺寸 */ if (heightMode == MeasureSpec.EXACTLY) { heightSize = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); } else if (heightMode == MeasureSpec.AT_MOST) { heightSize = MeasureSpec.makeMeasureSpec( heightSize < heightNeeded ? heightSize : heightNeeded, MeasureSpec.EXACTLY); } else { heightSize = MeasureSpec.makeMeasureSpec( heightNeeded, MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightSize); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //计算进度条的位置,并根据它初始化两个按钮的位置 // Calculates the position of the progress bar and initializes the positions of // the two buttons based on it lineLeft = DEFAULT_PADDING_LEFT_AND_RIGHT + getPaddingLeft(); lineRight = w - lineLeft - getPaddingRight(); lineTop = (int) mHintBGHeight + mThumbSize / 2 - mSeekBarHeight / 2 + 30; lineBottom = lineTop + mSeekBarHeight; lineWidth = lineRight - lineLeft; line.set(lineLeft, lineTop, lineRight, lineBottom); lineCorners = (int) ((lineBottom - lineTop) * 0.45f); leftSB.onSizeChanged(lineLeft, lineBottom, mThumbSize, lineWidth, cellsCount > 1, mThumbResId, getContext()); if (mSeekBarMode == 2) { rightSB.onSizeChanged(lineLeft, lineBottom, mThumbSize, lineWidth, cellsCount > 1, mThumbResId, getContext()); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制刻度,并且根据当前位置是否在刻度范围内设置不同的颜色显示 // Draw the scales, and according to the current position is set within // the scale range of different color display if (mTextArray != null) { mPartLength = lineWidth / (mTextArray.length - 1); for (int i = 0; i < mTextArray.length; i++) { final String text2Draw = mTextArray[i].toString(); float x; //平分显示 if (mCellMode == 1) { mCursorPaint.setColor(mTextColor); mCursorPaint.setAntiAlias(true); x = lineLeft + i * mPartLength - mCursorPaint.measureText(text2Draw) / 2; } else { float num = Float.parseFloat(text2Draw); float[] result = getCurrentRange(); if (compareFloat(num, result[0]) != -1 && compareFloat(num, result[1]) != 1 && mSeekBarMode == 2) { mCursorPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent)); } else { mCursorPaint.setColor(mTextColor); mCursorPaint.setAntiAlias(true); } //按实际比例显示 x = lineLeft + lineWidth * (num - mMin) / (mMax - mMin) - mCursorPaint.measureText(text2Draw) / 2; } float y = lineTop - textPadding; canvas.drawText(text2Draw, x, y, mCursorPaint); } } //绘制进度条 // draw the progress bar mMainPaint.setColor(colorLineEdge); canvas.drawRoundRect(line, lineCorners, lineCorners, mMainPaint); mMainPaint.setColor(colorLineSelected); if (mSeekBarMode == 2) { canvas.drawRect(leftSB.left + leftSB.widthSize / 2 + leftSB.lineWidth * leftSB.currPercent, lineTop, rightSB.left + rightSB.widthSize / 2 + rightSB.lineWidth * rightSB.currPercent, lineBottom, mMainPaint); } else { canvas.drawRect(leftSB.left + leftSB.widthSize / 2, lineTop, leftSB.left + leftSB.widthSize / 2 + leftSB.lineWidth * leftSB.currPercent, lineBottom, mMainPaint); } leftSB.draw(canvas); if (mSeekBarMode == 2) { rightSB.draw(canvas); } } /** * 初始化画笔 * init the paints */ private void initPaint() { mMainPaint.setStyle(Paint.Style.FILL); mMainPaint.setColor(colorLineEdge); mCursorPaint.setStyle(Paint.Style.FILL); mCursorPaint.setColor(colorLineEdge); mCursorPaint.setTextSize(mTextSize); mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mProgressPaint.setTypeface(Typeface.DEFAULT); mProgressPaint.setColor(0xFF0000); //计算文字的高度 //Calculate the height of the text Paint.FontMetrics fm = mCursorPaint.getFontMetrics(); mCursorTextHeight = (int) (Math.ceil(fm.descent - fm.ascent) + 2); } /** * 初始化进度提示的背景 */ private void initBitmap() { if (mProgressHintBGId != 0) { mProgressHintBG = BitmapFactory.decodeResource(getResources(), mProgressHintBGId); } else { mProgressHintBG = BitmapFactory.decodeResource(getResources(), R.drawable.progress_hint_bg); } } //*********************************** SeekBar ***********************************// private class SeekBar { private int lineWidth; private int widthSize, heightSize; private int left, right, top, bottom; private float currPercent; private float material = 0; public boolean isShowingHint; private boolean isLeft; private Bitmap bmp; private ValueAnimator anim; private RadialGradient shadowGradient; private Paint defaultPaint; private String mHintText2Draw; private Boolean isPrimary = true; public SeekBar(int position) { if (position < 0) { isLeft = true; } else { isLeft = false; } } /** * 计算每个按钮的位置和尺寸 * Calculates the position and size of each button * * @param x * @param y * @param hSize * @param parentLineWidth * @param cellsMode * @param bmpResId * @param context */ protected void onSizeChanged(int x, int y, int hSize, int parentLineWidth, boolean cellsMode, int bmpResId, Context context) { heightSize = hSize; widthSize = heightSize; left = x - widthSize / 2; right = x + widthSize / 2; top = y - heightSize / 2; bottom = y + heightSize / 10; if (cellsMode) { lineWidth = parentLineWidth; } else { lineWidth = parentLineWidth; } if (bmpResId > 0) { Bitmap original = BitmapFactory.decodeResource(context.getResources(), bmpResId); if (original != null) { Matrix matrix = new Matrix(); float scaleHeight = mThumbSize * 1.0f / original.getHeight(); float scaleWidth = scaleHeight; matrix.postScale(scaleWidth, scaleHeight); bmp = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true); } } else { defaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG); int radius = (int) (widthSize * DEFAULT_RADIUS); int barShadowRadius = (int) (radius * 0.95f); int mShadowCenterX = widthSize / 2; int mShadowCenterY = heightSize / 2; shadowGradient = new RadialGradient(mShadowCenterX, mShadowCenterY, barShadowRadius, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP); } } /** * 绘制按钮和提示背景和文字 * Draw buttons and tips for background and text * * @param canvas */ protected void draw(Canvas canvas) { int offset = (int) (lineWidth * currPercent); canvas.save(); canvas.translate(offset, 0); String text2Draw = ""; int hintW = 0, hintH = 0; float[] result = getCurrentRange(); if (mHideProgressHint) { isShowingHint = false; } else { if (isLeft) { if (mHintText2Draw == null) { text2Draw = (int) result[0] + ""; } else { text2Draw = mHintText2Draw; } // if is the start,change the thumb color isPrimary = (compareFloat(result[0], mMin) == 0); } else { if (mHintText2Draw == null) { text2Draw = (int) result[1] + ""; } else { text2Draw = mHintText2Draw; } isPrimary = (compareFloat(result[1], mMax) == 0); } hintH = (int) mHintBGHeight - 20;//设置提示背景的高度 hintW = (int) (mHintBGWith == 0 ? (mCursorPaint.measureText(text2Draw) + DEFAULT_PADDING_LEFT_AND_RIGHT) : mHintBGWith); if (hintW < 1.5f * hintH) hintW = (int) (1.5f * hintH); } if (bmp != null) { canvas.drawBitmap(bmp, left, lineTop - bmp.getHeight() / 2, null); if (isShowingHint) { Rect rect = new Rect(); rect.left = left - (hintW / 2 - bmp.getWidth() / 2); rect.top = bottom - hintH - bmp.getHeight(); rect.right = rect.left + hintW; rect.bottom = rect.top + hintH - 10; drawNinePath(canvas, mProgressHintBG, rect); mCursorPaint.setColor(Color.WHITE);//提示字的颜色 mCursorPaint.setTextSize(mTextSize); int x = (int) (left + (bmp.getWidth() / 2) - mCursorPaint.measureText(text2Draw) / 2); int y = bottom - hintH - bmp.getHeight() + hintH / 2; canvas.drawText(text2Draw, x, y, mCursorPaint); } } else { canvas.translate(left, 0); if (isShowingHint) { Rect rect = new Rect(); rect.left = widthSize / 2 - hintW / 2; rect.top = defaultPaddingTop; rect.right = rect.left + hintW; rect.bottom = rect.top + hintH - 10; drawNinePath(canvas, mProgressHintBG, rect); mCursorPaint.setColor(Color.WHITE); mCursorPaint.setTextSize(mTextSize); int x = (int) (widthSize / 2 - mCursorPaint.measureText(text2Draw) / 2); // TODO: 2017/2/6 //这里和背景形状有关,暂时根据本图形状比例计算 //Here and the background shape, temporarily based on the shape of this figure ratio calculation int y = hintH / 3 + defaultPaddingTop + mCursorTextHeight / 2; canvas.drawText(text2Draw, x, y, mCursorPaint); } drawDefault(canvas); } canvas.restore(); } /** * 绘制 9Path * * @param c * @param bmp * @param rect */ public void drawNinePath(Canvas c, Bitmap bmp, Rect rect) { NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null); patch.draw(c, rect); } /** * 如果没有图片资源,则绘制默认按钮 * <p> * If there is no image resource, draw the default button * * @param canvas */ private void drawDefault(Canvas canvas) { int centerX = widthSize / 2; int centerY = lineBottom - mSeekBarHeight / 2; int radius = (int) (widthSize * DEFAULT_RADIUS); // draw shadow defaultPaint.setStyle(Paint.Style.FILL); canvas.save(); canvas.translate(0, radius * 0.25f); canvas.scale(1 + (0.1f * material), 1 + (0.1f * material), centerX, centerY); defaultPaint.setShader(shadowGradient); canvas.drawCircle(centerX, centerY, radius, defaultPaint); defaultPaint.setShader(null); canvas.restore(); // draw body defaultPaint.setStyle(Paint.Style.FILL); if (isPrimary) { //if not set the color,it will use default color if (colorPrimary == 0) { defaultPaint.setColor(te.evaluate(material, 0xFFFFFFFF, 0xFFE7E7E7)); } else { defaultPaint.setColor(colorPrimary); } } else { if (colorSecondary == 0) { defaultPaint.setColor(te.evaluate(material, 0xFFFFFFFF, 0xFF0000)); } else { defaultPaint.setColor(colorSecondary); } } canvas.drawCircle(centerX, centerY, radius, defaultPaint); // draw border defaultPaint.setStyle(Paint.Style.STROKE); defaultPaint.setColor(0xFFD7D7D7); canvas.drawCircle(centerX, centerY, radius, defaultPaint); } final TypeEvaluator<Integer> te = new TypeEvaluator<Integer>() { @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int alpha = (int) (Color.alpha(startValue) + fraction * (Color.alpha(endValue) - Color.alpha(startValue))); int red = (int) (Color.red(startValue) + fraction * (Color.red(endValue) - Color.red(startValue))); int green = (int) (Color.green(startValue) + fraction * (Color.green(endValue) - Color.green(startValue))); int blue = (int) (Color.blue(startValue) + fraction * (Color.blue(endValue) - Color.blue(startValue))); return Color.argb(alpha, red, green, blue); } }; /** * 拖动检测 * * @param event * @return */ protected boolean collide(MotionEvent event) { float x = event.getX(); float y = event.getY(); int offset = (int) (lineWidth * currPercent); return x > left + offset && x < right + offset && y > top && y < bottom; } private void slide(float percent) { if (percent < 0) percent = 0; else if (percent > 1) percent = 1; currPercent = percent; } private void materialRestore() { if (anim != null) anim.cancel(); anim = ValueAnimator.ofFloat(material, 0); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { material = (float) animation.getAnimatedValue(); invalidate(); } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { material = 0; invalidate(); } }); anim.start(); } public void setProgressHint(String hint) { mHintText2Draw = hint; } } //*********************************** SeekBar ***********************************// public interface OnRangeChangedListener { void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser); } public void setOnRangeChangedListener(OnRangeChangedListener listener) { callback = listener; } public void setValue(float min, float max) { min = min + offsetValue; max = max + offsetValue; if (min < minValue) { throw new IllegalArgumentException("setValue() min < (preset min - offsetValue) . #min:" + min + " #preset min:" + minValue + " #offsetValue:" + offsetValue); } if (max > maxValue) { throw new IllegalArgumentException("setValue() max > (preset max - offsetValue) . #max:" + max + " #preset max:" + maxValue + " #offsetValue:" + offsetValue); } if (reserveCount > 1) { if ((min - minValue) % reserveCount != 0) { throw new IllegalArgumentException("setValue() (min - preset min) % reserveCount != 0 . #min:" + min + " #preset min:" + minValue + "#reserveCount:" + reserveCount + "#reserve:" + reserveValue); } if ((max - minValue) % reserveCount != 0) { throw new IllegalArgumentException("setValue() (max - preset min) % reserveCount != 0 . #max:" + max + " #preset min:" + minValue + "#reserveCount:" + reserveCount + "#reserve:" + reserveValue); } leftSB.currPercent = (min - minValue) / reserveCount * cellsPercent; if (mSeekBarMode == 2) { rightSB.currPercent = (max - minValue) / reserveCount * cellsPercent; } } else { leftSB.currPercent = (min - minValue) / (maxValue - minValue); if (mSeekBarMode == 2) { rightSB.currPercent = (max - minValue) / (maxValue - minValue); } } if (callback != null) { if (mSeekBarMode == 2) { callback.onRangeChanged(this, leftSB.currPercent, rightSB.currPercent, false); } else { callback.onRangeChanged(this, leftSB.currPercent, leftSB.currPercent, false); } } invalidate(); } public void setValue(float value) { setValue(value, mMax); } public void setRange(float min, float max) { setRules(min, max, reserveCount, cellsCount); } public void setRules(float min, float max, float reserve, int cells) { if (max <= min) { throw new IllegalArgumentException("setRules() max must be greater than min ! #max:" + max + " #min:" + min); } mMax = max; mMin = min; if (min < 0) { offsetValue = 0 - min; min = min + offsetValue; max = max + offsetValue; } minValue = min; maxValue = max; if (reserve < 0) { throw new IllegalArgumentException("setRules() reserve must be greater than zero ! #reserve:" + reserve); } if (reserve >= max - min) { throw new IllegalArgumentException("setRules() reserve must be less than (max - min) ! #reserve:" + reserve + " #max - min:" + (max - min)); } if (cells < 1) { throw new IllegalArgumentException("setRules() cells must be greater than 1 ! #cells:" + cells); } cellsCount = cells; cellsPercent = 1f / cellsCount; reserveValue = reserve; reservePercent = reserve / (max - min); reserveCount = (int) (reservePercent / cellsPercent + (reservePercent % cellsPercent != 0 ? 1 : 0)); if (cellsCount > 1) { if (mSeekBarMode == 2) { if (leftSB.currPercent + cellsPercent * reserveCount <= 1 && leftSB.currPercent + cellsPercent * reserveCount > rightSB.currPercent) { rightSB.currPercent = leftSB.currPercent + cellsPercent * reserveCount; } else if (rightSB.currPercent - cellsPercent * reserveCount >= 0 && rightSB.currPercent - cellsPercent * reserveCount < leftSB.currPercent) { leftSB.currPercent = rightSB.currPercent - cellsPercent * reserveCount; } } else { if (1 - cellsPercent * reserveCount >= 0 && 1 - cellsPercent * reserveCount < leftSB.currPercent) { leftSB.currPercent = 1 - cellsPercent * reserveCount; } } } else { if (mSeekBarMode == 2) { if (leftSB.currPercent + reservePercent <= 1 && leftSB.currPercent + reservePercent > rightSB.currPercent) { rightSB.currPercent = leftSB.currPercent + reservePercent; } else if (rightSB.currPercent - reservePercent >= 0 && rightSB.currPercent - reservePercent < leftSB.currPercent) { leftSB.currPercent = rightSB.currPercent - reservePercent; } } else { if (1 - reservePercent >= 0 && 1 - reservePercent < leftSB.currPercent) { leftSB.currPercent = 1 - reservePercent; } } } invalidate(); } public float getMax() { return mMax; } public float getMin() { return mMin; } public float[] getCurrentRange() { float range = maxValue - minValue; if (mSeekBarMode == 2) { return new float[]{-offsetValue + minValue + range * leftSB.currPercent, -offsetValue + minValue + range * rightSB.currPercent}; } else { return new float[]{-offsetValue + minValue + range * leftSB.currPercent, -offsetValue + minValue + range * 1.0f}; } } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); this.isEnable = enabled; } public void setProgressDescription(String progress) { if (leftSB != null) { leftSB.setProgressHint(progress); } if (rightSB != null) { rightSB.setProgressHint(progress); } } public void setLeftProgressDescription(String progress) { if (leftSB != null) { leftSB.setProgressHint(progress); } } public void setRightProgressDescription(String progress) { if (rightSB != null) { rightSB.setProgressHint(progress); } } @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnable) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: boolean touchResult = false; if (rightSB != null && rightSB.currPercent >= 1 && leftSB.collide(event)) { currTouch = leftSB; touchResult = true; } else if (rightSB != null && rightSB.collide(event)) { currTouch = rightSB; touchResult = true; } else if (leftSB.collide(event)) { currTouch = leftSB; touchResult = true; } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } return touchResult; case MotionEvent.ACTION_MOVE: float percent; float x = event.getX(); currTouch.material = currTouch.material >= 1 ? 1 : currTouch.material + 0.1f; if (currTouch == leftSB) { if (cellsCount > 1) { if (x < lineLeft) { percent = 0; } else { percent = (x - lineLeft) * 1f / (lineWidth); } int touchLeftCellsValue = Math.round(percent / cellsPercent); int currRightCellsValue; if (mSeekBarMode == 2) { currRightCellsValue = Math.round(rightSB.currPercent / cellsPercent); } else { currRightCellsValue = Math.round(1.0f / cellsPercent); } percent = touchLeftCellsValue * cellsPercent; while (touchLeftCellsValue > currRightCellsValue - reserveCount) { touchLeftCellsValue--; if (touchLeftCellsValue < 0) break; percent = touchLeftCellsValue * cellsPercent; } } else { if (x < lineLeft) { percent = 0; } else { percent = (x - lineLeft) * 1f / (lineWidth); } if (mSeekBarMode == 2) { if (percent > rightSB.currPercent - reservePercent) { percent = rightSB.currPercent - reservePercent; } } else { if (percent > 1.0f - reservePercent) { percent = 1.0f - reservePercent; } } } leftSB.slide(percent); leftSB.isShowingHint = true; //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } } else if (currTouch == rightSB) { if (cellsCount > 1) { if (x > lineRight) { percent = 1; } else { percent = (x - lineLeft) * 1f / (lineWidth); } int touchRightCellsValue = Math.round(percent / cellsPercent); int currLeftCellsValue = Math.round(leftSB.currPercent / cellsPercent); percent = touchRightCellsValue * cellsPercent; while (touchRightCellsValue < currLeftCellsValue + reserveCount) { touchRightCellsValue++; if (touchRightCellsValue > maxValue - minValue) break; percent = touchRightCellsValue * cellsPercent; } } else { if (x > lineRight) { percent = 1; } else { percent = (x - lineLeft) * 1f / (lineWidth); } if (percent < leftSB.currPercent + reservePercent) { percent = leftSB.currPercent + reservePercent; } } rightSB.slide(percent); rightSB.isShowingHint = true; } if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], true); } invalidate(); //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_CANCEL: if (mSeekBarMode == 2) { rightSB.isShowingHint = false; } leftSB.isShowingHint = false; if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], false); } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: if (mSeekBarMode == 2) { rightSB.isShowingHint = false; } leftSB.isShowingHint = false; currTouch.materialRestore(); if (callback != null) { float[] result = getCurrentRange(); callback.onRangeChanged(this, result[0], result[1], false); } //Intercept parent TouchEvent if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } break; } return super.onTouchEvent(event); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.minValue = minValue - offsetValue; ss.maxValue = maxValue - offsetValue; ss.reserveValue = reserveValue; ss.cellsCount = cellsCount; float[] results = getCurrentRange(); ss.currSelectedMin = results[0]; ss.currSelectedMax = results[1]; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); float min = ss.minValue; float max = ss.maxValue; float reserve = ss.reserveValue; int cells = ss.cellsCount; setRules(min, max, reserve, cells); float currSelectedMin = ss.currSelectedMin; float currSelectedMax = ss.currSelectedMax; setValue(currSelectedMin, currSelectedMax); } private class SavedState extends BaseSavedState { private float minValue; private float maxValue; private float reserveValue; private int cellsCount; private float currSelectedMin; private float currSelectedMax; SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); minValue = in.readFloat(); maxValue = in.readFloat(); reserveValue = in.readFloat(); cellsCount = in.readInt(); currSelectedMin = in.readFloat(); currSelectedMax = in.readFloat(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeFloat(minValue); out.writeFloat(maxValue); out.writeFloat(reserveValue); out.writeInt(cellsCount); out.writeFloat(currSelectedMin); out.writeFloat(currSelectedMax); } } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ private int dp2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * Compare the size of two floating point numbers * * @param a * @param b * @return 1 is a > b * -1 is a < b * 0 is a == b */ private int compareFloat(float a, float b) { int ta = Math.round(a * 1000); int tb = Math.round(b * 1000); if (ta > tb) { return 1; } else if (ta < tb) { return -1; } else { return 0; } } }
取拖动条的最大值代码块:
//从缓存中拿值 float maxValue = SharePreferencesUtils.getFloat(context, "maxValue", 0); if (0 == maxValue) { mMax = t.getFloat(R.styleable.RangeSeekBar_max, 100);//最大取值 } else { mMax = maxValue; }
这边的maxValue 值是从缓存中取出来的,如果在缓存中这个key(maxValue)为空的话,默认值为0,为0的时候从本地的配置文件arrays.xml中拿取默认的最大值(100),该文件放置在values目录下面,代码如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="markArray"> <item>0</item> <item>10</item> <item>20</item> <item>30</item> <item>50</item> <item>100</item> </string-array> </resources>
还有一段就是获取刻度的array。
//从缓存中拿值 CharSequence tempArray[] = SharePreferencesUtils.getStringSet(context, "textArray", null); if (tempArray == null || tempArray.length == 0) { mTextArray = t.getTextArray(R.styleable.RangeSeekBar_markTextArray); } else { mTextArray = tempArray; }
一样的 存储方式,放在SharePreferences内存当中,默认为null,默认获取的就是上面的arrays.xml文件。不为空则在本内存中获取,那么这边可能有人会问,SharePreferences怎么存储和获取数组元素,我这边贴出来一下:
package com.ds.platform.utils; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import com.ds.platform.bean.Lot; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * 覆盖模式的SharePreference */ public class SharePreferencesUtils { private final static String SP_NAME = "sp_cache"; private static SharedPreferences mPreferences; // SharedPreferences的实例 private static final String TAG = LogUtils.LogName; private static SharedPreferences getSp(Context context) { if (mPreferences == null) { mPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);//覆盖 } return mPreferences; } /** * 通过SP获得boolean类型的数据,没有默认为false * * @param context : 上下文 * @param key : 存储的key * @return */ public static boolean getBoolean(Context context, String key) { SharedPreferences sp = getSp(context); return sp.getBoolean(key, false); } /** * 通过SP获得boolean类型的数据,没有默认为false * * @param context : 上下文 * @param key : 存储的key * @param defValue : 默认值 * @return */ public static boolean getBoolean(Context context, String key, boolean defValue) { SharedPreferences sp = getSp(context); return sp.getBoolean(key, defValue); } /** * 设置int的缓存数据 * * @param context * @param key :缓存对应的key * @param value :缓存对应的值 */ public static void setBoolean(Context context, String key, boolean value) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putBoolean(key, value); edit.commit(); } /** * 缓存float * * @param context * @param key * @param defValue * @return */ public static void setFloat(Context context, String key, float defValue) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putFloat(key, defValue); edit.commit(); } /** * 获取int型数据 * @param context * @param key * @param defValue * @return */ public static int getInt(Context context, String key, int defValue) { SharedPreferences sp = getSp(context); return sp.getInt(key, defValue); } /** * 获取string值 * @param context * @param key * @param defValue * @return */ public static String getString(Context context, String key, String defValue) { SharedPreferences sp = getSp(context); return sp.getString(key, defValue); } public static float getFloat(Context context, String key, float defValue) { SharedPreferences sp = getSp(context); return sp.getFloat(key, defValue); } public static long getLong(Context context, String key, long defValue) { SharedPreferences sp = getSp(context); return sp.getLong(key, 0); } /** * 储存数组 */ public static void setArrayData(Context context, String key, CharSequence text[]) { if (text == null || text.length == 0) { return; } SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 Set set = new HashSet(); for (int i = 0; i < text.length; i++) { set.add(text[i]); } edit.putStringSet(key, set); edit.commit(); } /** * 获取数组 */ public static CharSequence[] getStringSet(Context context, String key, Set<String> defValue) { CharSequence sequence[] = new CharSequence[6]; SharedPreferences sp = getSp(context); Set set = sp.getStringSet(key, defValue); if (set != null && set.size() > 0) { Iterator<Object> it = set.iterator(); for (int i = 0; i < set.size(); i++) { sequence[i] = (CharSequence) it.next(); } Arrays.sort(sequence); } return sequence; } /** * 保存list数据 */ public static void setArrayList(Context context, String key, ArrayList<Lot> list) { if (list == null || list.size() == 0) { return; } Log.d(TAG, list.size() + ""); SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 int size = list.size(); setInt(context, "listSize", size);//保存list的大小 for (int i = 0; i < list.size(); i++) { edit.putString(key + i, list.get(i).getId()); edit.putString(key + i + "n", list.get(i).getName()); } edit.commit(); } /** * 获取list数据 */ public static ArrayList<Lot> getArrayList(Context context, String key) { ArrayList<Lot> list = new ArrayList<>(); int size = getInt(context, "listSize", 0); for (int i = 0; i < size; i++) { Lot lot = new Lot(); lot.setId(getString(context, key + i, null)); lot.setName(getString(context, key + i + "n", null)); list.add(lot); } return list; } //删除list public static void deleteList(Context context, String key) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 int size = getInt(context, "listSize", 0); for (int i = 0; i < size; i++) { edit.remove(key + i); edit.remove(key + i + "n"); edit.commit(); } deleteData(context, "listSize");//删除列表玩法的缓存数据 } /** * 设置int的缓存数据 * * @param context * @param key :缓存对应的key * @param value :缓存对应的值 */ public static void setInt(Context context, String key, int value) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putInt(key, value); edit.commit(); } public static void setString(Context context, String key, String value) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putString(key, value); edit.commit(); } public static void setLong(Context context, String key, long value) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putLong(key, value); edit.commit(); } public static void setInt(Context context, String key, String value) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.putString(key, value); edit.commit(); } /** * 删除指定key的value * * @param context * @param key */ public static void deleteData(Context context, String key) { SharedPreferences sp = getSp(context); SharedPreferences.Editor edit = sp.edit();// 获取编辑器 edit.remove(key); edit.commit(); } /** * 清空缓存中的全部数据 * * @param context */ public static void clearData(Context context) { SharedPreferences sp = getSp(context); SharedPreferences.Editor editor = sp.edit(); editor.clear().commit(); } }
上述是我项目中用到的,需要的可以看一下,另外分享一个小技巧,如果你想知道app中存入在Sp内存中的数据怎么看呢?可以在android studio中去查看,工具导航栏Tools–>Android–>Android Device Monitor ,打开之后点击右侧导航菜单的File Explorer ,找到data–>data–>自己的项目包名–>shared_prefs–>自己定义的sp名字,这边定义的是sp_cache,然后该目录下就存在一个sp_cache.xml的文件,点击右上角的图标
第一个导出电脑就可以查看了。好了,原归正传:
我怎么动态设置数组呢?先看下效果图:
不知你们有没有发现一些细节,每次拖动条上面的刻度文字跟跳转另一个activity输入框的数字是顺序大小一样,还有动态去设置刻度值得时候我没有从0开始,而是大于0的数字开始,我也做了启用停用的开关,停用之后不能使用拖动条功能,这个状态也是保存在SP内存里面的。
接下来看一下拖动条的对话框,我使用fragment做的,这边也一起说一下吧!
代码如下:
package com.ds.platform.dialog; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import com.ds.platform.R; import com.ds.platform.activity.OrderActivity; import com.ds.platform.utils.LogUtils; import com.ds.platform.view.RangeSeekBar; /** * @author: Allen. * @date: 2017/4/14 * @description: 拖动条对话框 */ public class MyDialogFragment extends android.support.v4.app.DialogFragment { private RangeSeekBar seekbar1; private String progressData = "0";//初始第一个数值 private static final String TAG = LogUtils.LogName; private OrderActivity orderActivity; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.dialog_balance_seekbar, null); orderActivity = (OrderActivity) getActivity(); initView(view);//初始控件 //去除标题 getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); setCancelable(true); return view; } /** * 获取控件 * * @param view */ private void initView(View view) { seekbar1 = (RangeSeekBar) view.findViewById(R.id.seekbar1); seekbar1.setValue(0);//设置默认值 //监听控件去设置列表订单的item金额 seekbar1.setOnRangeChangedListener(new RangeSeekBar.OnRangeChangedListener() { @Override public void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser) { seekbar1.setProgressDescription((int) min + ""); progressData = ((int) min) + ""; orderActivity.setMoney(Integer.parseInt(progressData)); } }); } }
那怎么使用这个fragment呢?首先不用思考的,其父类必须继承FragmentActivity,然后在父类的layout中必须有
<FrameLayout android:id="@+id/fragment" android:layout_width="match_parent" android:layout_height="match_parent" />
然后可以通过view来触发这个fragment。
FragmentTransaction tran = getSupportFragmentManager().beginTransaction(); MyDialogFragment dialogFragment = new MyDialogFragment(); dialogFragment.show(tran, "myDialog");
需要注意的是要用V4的getSupportFragmentManager。
MyDialogFragment 的layout中需要使用自己定义的seekbar,如下:
<com.dsn.platform.view.RangeSeekBar android:id="@+id/seekbar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@+id/view1" app:cellMode="number" app:lineColorEdge="@color/silery" app:lineColorSelected="@color/main_top" app:markTextArray="@array/markArray" app:seekBarMode="single" app:textPadding="15dp" app:textSize="@dimen/text14" app:textColor="@color/login_text" app:seekBarResId="@drawable/seekbar_thumb" /> </RelativeLayout>
该控件上的属性封装在attrs.xml中:
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- ******************** 参数解释 ******************** --> <!--最大值--> <!--最小值--> <!--两个按钮的最小间距--> <!--cells 等于0为普通模式,大于1时切换为刻度模式--> <!--是否关闭进度提示--> <!--拖动后的Seekbar颜色--> <!--默认的Seekbar颜色--> <!--进度为最小值或最大值时按钮的颜色,默认此属性不调用--> <!--进度不为最小值或最大值时按钮的颜色,默认此属性不调用--> <!--刻度文字,不设置的时候默认隐藏--> <!--按钮的背景资源,不设置的时候默认为圆形按钮--> <!--进度提示背景资源,必须使用 9 path文件--> <!--刻度文字与进度条之间的距离--> <!--刻度文字和进度提示文字的大小--> <!--进度提示背景的高度,不设置时根据文字尺寸自适应--> <!--进度提示背景的宽度,不设置时根据文字尺寸自适应--> <!--进度提示背景和进度条之间的距离--> <!--进度条的高度--> <!--按钮的尺寸--> <!--刻度模式 number 根据刻度的实际所占比例分配位置(markTextArray中必须都为数字) other 平分当前布局(markTextArray可以是任何字符) --> <!--单向、双向模式 single 单向模式,只有一个按钮 range 双向模式,有两个按钮 --> <!-- ******************** 参数解释 ******************** --> <declare-styleable name="RangeSeekBar"> <attr name="max" format="float"/> <attr name="min" format="float"/> <attr name="reserve" format="float"/> <attr name="cells" format="integer"/> <attr name="hideProgressHint" format="boolean"/> <attr name="lineColorSelected" format="color"/> <attr name="lineColorEdge" format="color"/> <attr name="thumbPrimaryColor" format="color"/> <attr name="thumbSecondaryColor" format="color"/> <attr name="markTextArray" format="reference"/> <attr name="seekBarResId" format="reference"/> <!-- must use 9 path !!!--> <attr name="progressHintResId" format="reference"/> <attr name="textPadding" format="dimension" /> <attr name="textSize" format="dimension" /> <attr name="textColor" format="color" /> <attr name="hintBGHeight" format="dimension" /> <attr name="hintBGWith" format="dimension" /> <attr name="hintBGPadding" format="dimension" /> <attr name="seekBarHeight" format="dimension"/> <attr name="thumbSize" format="dimension"/> <attr name="cellMode" format="enum"> <enum name="number" value="0"/> <enum name="other" value="1"/> </attr> <attr name="seekBarMode" format="enum"> <enum name="single" value="1"/> <enum name="range" value="2"/> </attr> </declare-styleable> </resources>
这边要注意progressHintResId属性,设置刻度提示背景必须使用.9的图片。
另一个跳转的activity页面我就不贴代码了,逻辑很简单,把输入的数字封装成数组,存入SP中,然后你每次弹出拖动条对话框,它都会去判断缓存中是否有值。
好了,今天先分享这个,感谢!
相关文章推荐
- Android——自定义带刻度的SeekBar单向拖动条 原创 2017年04月21日 11:29:53 标签:android /listview /app /seekbar 1302 时间过得真快,
- android自定义Seekbar你还将你的拖动数值显示在旁边固定的TextView上么?
- Android自定义可长按 可点击不可拖动的SeekBar
- Android界面开发之拖动条及自定义SeekBar
- Android 可拖动进度条:SeekBar之自定义进度条
- Android界面开发之拖动条及自定义SeekBar
- android自定义进度值可拖动的seekbar
- android自定义Seekbar你还将你的拖动数值显示在旁边固定的TextView上么?
- 自定义漂亮的Android SeekBar(拖动条)样式
- Android 可拖动的seekbar自定义进度值
- Android自定义垂直拖动seekbar进度条
- Android界面开发之拖动条及自定义SeekBar
- Android 可拖动进度条:SeekBar之自定义进度条
- Android 自定义带刻度的seekbar
- Android自定义Seekbar拖动条式样
- android自定义Seekbar你还将你的拖动数值显示在旁边固定的TextView上么?
- android 自定义SeekBar(拖动条)
- 我的Android进阶之旅------>Android之拖动条(SeekBar和RatingBar)的功能和用法
- Android中自定义SeekBar的背景颜色,进度条颜色,以及滑块的图片
- android之控件自定义(seekBar)