您的位置:首页 > 移动开发 > Android开发

深度定制双按钮调节的SeekBar控件

2013-04-18 15:38 447 查看
1.首先在工程values文件夹下定义attrs.xml文件,用来定义控件所需要的属性,并在其中定义属性如下:

<declare-styleable name="SeekBarPressure">
<attr name="minValue" format="float" />
<attr name="maxValue" format="float" />
<attr name="duration" format="float" />
<attr name="width" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
<attr name="height" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
</declare-styleable>
这些设置的属性就可以如Android原生的android:layout_width等属性直接在layout的xml文件中设置
2.在用到此控件的layout的xml文件中需要引入当前自定义控件所在的命名空间,然后在xml中就可以直接用上面定义的属性。比如我的工程包名是com.android.anndy,那么命名空间定义以及控件使用方法如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tyre="http://schemas.android.com/apk/res/com.android.anndy"
android:layout_width="fill_parent"
android:layout_height="wrap_content">

<com.android.anndy.widget.SeekBarPressure
android:id="@+id/SBP_ValueSet_Pressure"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tyre:minValue="1.0"
tyre:maxValue="4.0"
tyre:duration="0.5"
tyre:width="175dip"
tyre:height="12dip">
</com.android.anndy.widget.SeekBarPressure>

</LinearLayout>注意,命名控件的名字(本例是tyre)和属性设置中的一定要一致(如tyre:minValue)
3.在java文件中定义此控件,完成onDraw的挥之,以及接口的调用

public class SeekBarPressure extends View {
private static final String TAG = "SeekBarPressure";

private static final int CLICK_ON_LOW = 1;

private static final int CLICK_ON_HIGH = 2;

private static final int CLICK_IN_LOW_AREA = 3;

private static final int CLICK_IN_HIGH_AREA = 4;

private static final int CLICK_OUT_AREA = 5;

private static final int CLICK_INVAILD = 0;

/*
* private static final int[] PRESSED_STATE_SET = {
* android.R.attr.state_focused, android.R.attr.state_pressed,
* android.R.attr.state_selected, android.R.attr.state_window_focused, };
*/

private static final int[] STATE_NORMAL = {};

private static final int[] STATE_PRESSED = {
android.R.attr.state_pressed, android.R.attr.state_window_focused,
};

private Drawable mScrollBarBgNormal;

private Drawable mScrollBarProgress;

private Drawable mThumbLow;

private Drawable mThumbHigh;

private int mScollBarWidth;

private int mScollBarHeight;

private int mThumbWidth;

private int mThumbHeight;

private int mHalfScollBarHeight;

private int mHalfThumbHeight;

private int mOffsetLow = 0;

private int mOffsetHigh = 0;

private int mDistance = 0;

private float mMin = 0;

private float mMax = 0;

private int mDuration = 0;

private int mFlag = CLICK_INVAILD;

private OnSeekBarChangeListener mBarChangeListener;

public SeekBarPressure(Context context) {
this(context, null);
}

public SeekBarPressure(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public SeekBarPressure(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

init(context);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SeekBarPressure, defStyle,
0);

mMin = a.getFloat(R.styleable.SeekBarPressure_minValue, mMin);
mMax = a.getFloat(R.styleable.SeekBarPressure_maxValue, mMax);
mScollBarWidth = a.getLayoutDimension(R.styleable.SeekBarPressure_width, "layout_width");
mScollBarHeight = a.getLayoutDimension(R.styleable.SeekBarPressure_height, "layout_height");

mDistance = mScollBarWidth - mThumbWidth;

mDuration = (int)Math.rint(a.getFloat(R.styleable.SeekBarPressure_duration, mDuration)
* mDistance / (mMax - mMin));

if (mMin == 0) {
throw new RuntimeException(a.getPositionDescription()
+ ": You must supply a minValue attribute.");
}
if (mMax == 0) {
throw new RuntimeException(a.getPositionDescription()
+ ": You must supply a maxValue attribute.");
}
if (mMin > mMax) {
throw new RuntimeException(a.getPositionDescription()
+ ": The minValue attribute must be smaller than the maxValue attribute.");
}
if (mDuration == 0) {
throw new RuntimeException(a.getPositionDescription()
+ ": You must supply a duration attribute.");
}

if (mScollBarWidth == 0 || mScollBarWidth == -1 || mScollBarWidth == -2) {
throw new RuntimeException(a.getPositionDescription()
+ ": You must supply a width attribute.");
}
if (mScollBarHeight == 0 || mScollBarHeight == -1 || mScollBarHeight == -2) {
throw new RuntimeException(a.getPositionDescription()
+ ": You must supply a height attribute.");
}

a.recycle();

TyreLog.d(TAG, "Constructor-->mMinDuration: " + mDuration + " mScollBarWidth: "
+ mScollBarWidth + " mScollBarHeight: " + mScollBarHeight + " mDistance: "
+ mDistance + " mMax: " + mMax);

// mOffsetHigh = mOffsetLow + mDuration;
// TyreLog.d(TAG, "constructor-->mOffsetHigh: " + mOffsetHigh +
// " mOffsetLow: " + mOffsetLow
// + " mDuration: " + mDuration);
}

public static double formatDouble(double pDouble) {
BigDecimal bd = new BigDecimal(pDouble);
BigDecimal bd1 = bd.setScale(3, BigDecimal.ROUND_HALF_UP);
pDouble = bd1.doubleValue();
return pDouble;
}

public void init(Context context) {
Resources resources = getResources();

mScrollBarBgNormal = resources.getDrawable(R.drawable.seekbarpressure_bg_normal);
mScrollBarProgress = resources.getDrawable(R.drawable.seekbarpressure_bg_progress);
mThumbLow = resources.getDrawable(R.drawable.seekbarpressure_thumb);
mThumbHigh = resources.getDrawable(R.drawable.seekbarpressure_thumb);

// setDrawableState(mThumbLow);
// setDrawableState(mThumbHigh);
mThumbLow.setState(STATE_NORMAL);
mThumbHigh.setState(STATE_NORMAL);

// mScollBarWidth = mScrollBarBgNormal.getIntrinsicWidth();
// mScollBarHeight = mScrollBarBgNormal.getIntrinsicHeight();

mThumbWidth = mThumbLow.getIntrinsicWidth();
mThumbHeight = mThumbLow.getIntrinsicHeight();
TyreLog.d(TAG, "init-->mThumbWidth: " + mThumbWidth + " mThumbHeight: " + mThumbHeight);
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*
* TyreLog.d(TAG, "widthMeasureSpec: " + getDefaultSize(mScollBarWidth,
* widthMeasureSpec) + " heightMeasureSpec" +
* getDefaultSize(mScollBarHeight, heightMeasureSpec));
*/
int width = mScollBarWidth;
int height = mScollBarHeight;
setMeasuredDimension(width, height);
}

protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
TyreLog.d(TAG, "changed: " + changed + "l:" + l + "t:" + t + "r:" + r + "b:" + b);
// mScollBarWidth = r - l;
// mScollBarHeight = b - t;
}

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

mHalfScollBarHeight = mScollBarHeight >> 1;
mHalfThumbHeight = mThumbHeight >> 1;

mScrollBarBgNormal.setBounds(0, 0, mScollBarWidth, mScollBarHeight);
mScrollBarBgNormal.draw(canvas);

mThumbLow.setBounds(mOffsetLow, mHalfScollBarHeight - mHalfThumbHeight, mOffsetLow
+ mThumbWidth, mHalfScollBarHeight + mHalfThumbHeight);
mThumbLow.draw(canvas);

mScrollBarProgress.setBounds(mOffsetLow + mThumbWidth - 2, 0, mOffsetHigh + 3,
mScollBarHeight);
mScrollBarProgress.draw(canvas);

mThumbHigh.setBounds(mOffsetHigh, mHalfScollBarHeight - mHalfThumbHeight, mOffsetHigh
+ mThumbWidth, mHalfScollBarHeight + mHalfThumbHeight);
mThumbHigh.draw(canvas);

if (mBarChangeListener != null) {

double progressLow = formatDouble(mOffsetLow * (mMax - mMin) / mDistance + mMin);
double progressHigh = formatDouble(mOffsetHigh * (mMax - mMin) / mDistance + mMin);
TyreLog.d(TAG, "onDraw-->mOffsetLow: " + mOffsetLow + " mOffsetHigh: " + mOffsetHigh
+ " progressLow: " + progressLow + " progressHigh: " + progressHigh);
mBarChangeListener.onProgressChanged(this, progressLow, progressHigh, mMax, mMin);
}
}

@Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
mFlag = getAreaFlag(e);
Ty
c4bc
reLog.d(TAG, "e.getX: " + e.getX() + "mFlag: " + mFlag);
if (mFlag == CLICK_ON_LOW) {
mThumbLow.setState(STATE_PRESSED);
} else if (mFlag == CLICK_ON_HIGH) {
mThumbHigh.setState(STATE_PRESSED);
} else if (mFlag == CLICK_IN_LOW_AREA) {
mThumbLow.setState(STATE_PRESSED);
if (e.getX() < 0 || e.getX() <= mThumbWidth) {
mOffsetLow = 0;
} else if (e.getX() > mScollBarWidth - mThumbWidth) {
mOffsetLow = mDistance - mDuration;
} else {
mOffsetLow = formatInt(e.getX() - (double)mThumbWidth / 2);
if (mOffsetHigh - mDuration <= mOffsetLow) {
mOffsetHigh = (mOffsetLow + mDuration <= mDistance) ? (mOffsetLow + mDuration)
: mDistance;
mOffsetLow = mOffsetHigh - mDuration;
}
}
} else if (mFlag == CLICK_IN_HIGH_AREA) {
mThumbHigh.setState(STATE_PRESSED);
if (e.getX() < mDuration) {
mOffsetHigh = mDuration;
mOffsetLow = mOffsetHigh - mDuration;
} else if (e.getX() >= mScollBarWidth - mThumbWidth) {
mOffsetHigh = mDistance;
} else {
mOffsetHigh = formatInt(e.getX() - (double)mThumbWidth / 2);
if (mOffsetHigh - mDuration <= mOffsetLow) {
mOffsetLow = (mOffsetHigh - mDuration >= 0) ? (mOffsetHigh - mDuration) : 0;
mOffsetHigh = mOffsetLow + mDuration;
}
}
}
} else if (e.getAction() == MotionEvent.ACTION_MOVE) {
if (mFlag == CLICK_ON_LOW) {
if (e.getX() < 0 || e.getX() <= mThumbWidth) {
mOffsetLow = 0;
} else if (e.getX() > mScollBarWidth - mThumbWidth - mDuration) {
mOffsetLow = mDistance - mDuration;
mOffsetHigh = mOffsetLow + mDuration;
} else {
mOffsetLow = formatInt(e.getX() - (double)mThumbWidth / 2);
if (mOffsetHigh - mOffsetLow <= mDuration) {
mOffsetHigh = (mOffsetLow + mDuration <= mDistance) ? (mOffsetLow + mDuration)
: mDistance;
}
}
} else if (mFlag == CLICK_ON_HIGH) {
if (e.getX() < mDuration + mThumbWidth) {
mOffsetHigh = mDuration;
mOffsetLow = 0;
} else if (e.getX() > mScollBarWidth - mThumbWidth) {
mOffsetHigh = mDistance;
} else {
mOffsetHigh = formatInt(e.getX() - (double)mThumbWidth / 2);
if (mOffsetHigh - mOffsetLow <= mDuration) {
mOffsetLow = (mOffsetHigh - mDuration >= 0) ? (mOffsetHigh - mDuration) : 0;
}
}
}
} else if (e.getAction() == MotionEvent.ACTION_UP) {
mThumbLow.setState(STATE_NORMAL);
mThumbHigh.setState(STATE_NORMAL);
}

setProgressLow(formatDouble(mOffsetLow * (mMax - mMin) / mDistance + mMin));
setProgressHigh(formatDouble(mOffsetHigh * (mMax - mMin) / mDistance + mMin));

return true;
}

public int getAreaFlag(MotionEvent e) {
int top = mHalfScollBarHeight - mHalfThumbHeight;
int bottom = mHalfScollBarHeight + mHalfThumbHeight;

if (e.getY() >= top && e.getY() <= bottom && e.getX() >= mOffsetLow
&& e.getX() <= mOffsetLow + mThumbWidth) {
return CLICK_ON_LOW;
} else if (e.getY() >= top && e.getY() <= bottom && e.getX() >= mOffsetHigh
&& e.getX() <= mOffsetHigh + mThumbWidth) {
return CLICK_ON_HIGH;
} else if (e.getY() >= top
&& e.getY() <= bottom
&& ((e.getX() >= 0 && e.getX() < mOffsetLow) || ((e.getX() > mOffsetLow
+ mThumbWidth) && e.getX() <= ((double)mOffsetHigh + mOffsetLow + mThumbWidth) / 2))) {
return CLICK_IN_LOW_AREA;
} else if (e.getY() >= top
&& e.getY() <= bottom
&& (((e.getX() > ((double)mOffsetHigh + mOffsetLow + mThumbWidth) / 2) && e.getX() < mOffsetHigh) || (e
.getX() > mOffsetHigh + mThumbWidth && e.getX() <= mScollBarWidth))) {
return CLICK_IN_HIGH_AREA;
} else if (!(e.getX() >= 0 && e.getX() <= mScollBarWidth && e.getY() >= top && e.getY() <= bottom)) {
return CLICK_OUT_AREA;
} else {
return CLICK_INVAILD;
}
}

public void setMax(double max) {
this.mMax = (float)max;
}

public double getMax() {
return mMax;
}

public void setMin(double min) {
this.mMin = (float)min;
}

public double getMin() {
return mMin;
}

public void setProgressLow(double progressLow) {
mOffsetLow = formatInt((progressLow - mMin) / (mMax - mMin) * mDistance);
invalidate();
}

public void setProgressHigh(double progressHigh) {
mOffsetHigh = formatInt((progressHigh - mMin) / (mMax - mMin) * mDistance);
invalidate();
}

public double getProgressLow() {
return formatDouble(mOffsetLow * (mMax - mMin) / mDistance + mMin);
}

public double getProgressHigh() {
return formatDouble(mOffsetHigh * (mMax - mMin) / mDistance + mMin);
}

public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener) {
this.mBarChangeListener = mListener;
}

public interface OnSeekBarChangeListener {
public void onProgressChanged(SeekBarPressure seekBar, double progressLow,
double progressHigh, double max, double min);
}

/*
* public void setDrawableState(Drawable dr) {
* dr.setState(PRESSED_STATE_SET); invalidate(); }
* @Override protected void drawableStateChanged() {
* super.drawableStateChanged(); TyreLog.d(TAG, "drawableStateChanged");
* mThumbLow.setState(PRESSED_STATE_SET);
* mThumbHigh.setState(PRESSED_STATE_SET); invalidate(); }
*/

private int formatInt(double value) {
BigDecimal bd = new BigDecimal(value);
BigDecimal bd1 = bd.setScale(0, BigDecimal.ROUND_HALF_UP);
return bd1.intValue();
}
}

好了,到此控件的定制已完成,可以像使用Android原生SeekBar那样在Java文件中使用,或者如上面我说的那样在xml中直接布局此控件。
其中使用到的资源文件如下:









内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息