效果图
View代码
第一步:自定义属性
在values文件夹下的attrs.xml文件下添加代码
<declare-styleable name="DragHoriView">
<!--横向节点数量-->
<attr name="hor_number" format="integer"/>
<!--节点文字,用中文状态下的“,”分隔-->
<attr name="nodestext" format="string"/>
<!--文字大小-->
<attr name="text_size" format="float"/>
<!--文字颜色-->
<attr name="text_color" format="color"/>
<!--可拖动图片-->
<attr name="drag_icon" format="reference"/>
<!--标题底下的图标-->
<attr name="type_icon" format="reference"/>
<!--节点颜色-->
<attr name="circle_color" format="color"/>
</declare-styleable>
第二步:自定义圆形
public class CircleView extends View {
/**
* 默认颜色
*/
private final int DEFAULT_COLOR = Color.LTGRAY;
/**
* 默认半径dp
*/
private final float DEFAULT_RADIUS = 32;
private int mColor;
private Paint mCirclePaint;
private float mRadius;
private float mCenterX;
private float mCenterY;
public CircleView(Context context)
{
this(context, null);
}
public CircleView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DragView);
mColor = array.getColor(R.styleable.DragHoriView_circle_color,DEFAULT_COLOR);
mCirclePaint = new Paint();
mCirclePaint.setColor(mColor);
mCenterY = mCenterX = mRadius = getMeasuredWidth() == 0?DEFAULT_RADIUS:getMeasuredWidth()/2;
array.recycle();
}
public float getRadius(){
return mRadius;
}
public int getColor()
{
return mColor;
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawCircle(mCenterX,mCenterY,mRadius,mCirclePaint);
}
}
第三步:自定义View
public class DragHoriView extends RelativeLayout implements View.OnTouchListener {
private final String TAG = "DragHoriView";
/**
* 默认文字大小
*/
private final float DEFAULT_TEXT_SIZE = 16;
/**
* 默认字体颜色
*/
private final int DEFAULT_TEXT_COLOR = Color.GRAY;
/**
* 横向节点数量
*/
private int mHorNum;
/**
* 字体大小
*/
private float mTextSize;
/**
* 字体颜色
*/
private int mTextColor;
/**
* 每个节点的文字
*/
private String[] mTexts;
/**
* 当前X坐标
*/
private float mCurX;
/**
* 图片
*/
private Drawable mIcon;
/**
* title底部的图标
*/
private Drawable mTypeIcon;
private float mStartX;
/**
* 可移动的最小值
*/
private float mMinX;
/**
* 可移动的最大值
*/
private float mMaxX;
/**
* 横线的宽度
*/
private int mHorWidth;
/**
* 横线高度
*/
private int mHorHeight;
/**
* 圆的半径
*/
private int mCRadius;
/**
* 拖动控件的宽度(此处为直径)
*/
private int mDragWidth;
/**
* 拖动控件的高度
*/
private int mDragHeight;
/**
* 文字的高度
*/
private int mTextHeight;
/**
* 文字的宽度
*/
private int mTextWidth;
/**
* 控件本身的宽度
*/
private int mWidth;
/**
* 文字距离顶部的距离
*/
private int mTextTop;
/**
* 文字和拖动View的间距
*/
private int mCenterSpace;
private int mSelectPosition;
private int[] mRanges;
private ImageView mDragView;
public DragHoriView(Context context) {
this(context, null);
}
public DragHoriView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DragHoriView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DragHoriView);
mTextColor = array.getColor(R.styleable.DragHoriView_text_color, DEFAULT_TEXT_COLOR);
mTextSize = array.getFloat(R.styleable.DragHoriView_text_size, DEFAULT_TEXT_SIZE);
mIcon = array.getDrawable(R.styleable.DragHoriView_drag_icon);
mTypeIcon = array.getDrawable(R.styleable.DragHoriView_type_icon);
String text = array.getString(R.styleable.DragHoriView_nodestext);
if (text != null && !text.equals("")) {
mTexts = text.split(",");
mHorNum = mTexts.length;
}
array.recycle();
initView(attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
int hegith = MeasureSpec.getSize(heightMeasureSpec);
int count = getChildCount();
//边距
mCenterSpace = dp2px(getContext(), 8);
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
LayoutParams rParams;
if (view instanceof CircleView) {
if (mCRadius == 0) {
mCRadius = (int) ((CircleView) view).getRadius();
}
} else if (view instanceof TextView) {
if (mTextHeight == 0) {
mTextHeight = spTopx(getContext(), mTextSize + 2);
String text = ((TextView) view).getText().toString();
mTextWidth =spTopx(getContext(), mTextSize * text.length());
}
} else if (view instanceof ImageView) {
if (mDragHeight == 0) {
mDragHeight = mDragWidth = dp2px(getContext(), 50);
rParams = new LayoutParams(mDragWidth, mDragWidth);
view.setLayoutParams(rParams);
}
} else {
if (mHorWidth == 0) {
//横线
mHorWidth = mWidth * 3 / 8;
mHorHeight = mCRadius * 2 / 3;
rParams = new LayoutParams(mHorWidth, mHorHeight);
view.setLayoutParams(rParams);
}
}
}
mTextTop = (hegith - (mTextHeight + mCenterSpace + mDragHeight)) / 2;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int count = getChildCount();
int width = r - l;
int space = mHorWidth / (mHorNum - 1);
int dTop = mTextTop + mTextHeight + mCenterSpace;
int cTop = dTop + mDragHeight / 2 - mCRadius;
int hTop = dTop + mDragHeight / 2 - mHorHeight / 2;
int hLeft = (mWidth - mHorWidth) / 2;
int cLeft = hLeft - mCRadius;
int tLeft = hLeft - mTextWidth / 2;
int dLeft = hLeft - mDragWidth / 2;
if (mRanges == null) {
mRanges = new int[mHorNum];
}
int j = 0;
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
if (view instanceof CircleView) {
cLeft = cLeft + space * j;
int cRight = cLeft + mCRadius * 2;
int cBottom = cTop + mCRadius * 2;
view.layout(cLeft, cTop, cRight, cBottom);
//赋值最小值为拖动空间本身的左边
mMinX = dLeft;
Log.v(TAG, "mMinX = " + mMinX);
mRanges[j] = (cLeft + cRight) / 2;
j++;
} else if (view instanceof TextView) {
String text = ((TextView) view).getText().toString();
mTextWidth = spTopx(getContext(), mTextSize * text.length());
int tBottom = mTextTop + mTextHeight;
tLeft = tLeft + space * (j - 1);
int tRight = tLeft + mTextWidth;
view.layout(tLeft, mTextTop, tRight, tBottom);
} else if (view instanceof ImageView) {
view.layout(dLeft, dTop, dLeft + mDragWidth, dTop + mDragHeight);
} else {
//横线
view.layout(hLeft, hTop, hLeft + mHorWidth, hTop + mHorHeight);
//赋值最大值为横线的右边加上拖动控件的宽度的一半
mMaxX = width - hLeft - mDragWidth / 2;
Log.v(TAG, "mMaxX = " + mMaxX);
}
}
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof ImageView) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = (int) event.getRawX();
mCurX = v.getTranslationX();
Log.v(TAG, "mStartX = " + mStartX);
v.setPressed(true);
break;
case MotionEvent.ACTION_MOVE:
float x = mCurX + event.getRawX() - mStartX;
if (x >= 0 && x <= mMaxX - mMinX) {
v.setTranslationX(mCurX + event.getRawX() - mStartX);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int max;
int min;
int length = mRanges.length;
float curX = v.getTranslationX();
for (int i = 0; i < length; i++) {
if (i > 0 && mRanges[i] - mMinX > curX) {
max = mRanges[i];
min = mRanges[i - 1];
float center = (max + min) / 2 - mMinX - mDragWidth / 2;
if (curX >= center) {
//超过一半
setAnim(max - mMinX - mDragWidth / 2, i);
} else if (curX < center) {
setAnim(min - mMinX - mDragWidth / 2, i - 1);
} else {
//刚好拖到节点
if (mOnNodeSelect != null) {
if (mSelectPosition != i) {
mOnNodeSelect.onNodeSelect(mSelectPosition);
}
}
}
break;
}
}
v.setPressed(false);
break;
}
} else {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
int length = mRanges.length;
int nodeX = (v.getLeft() + v.getRight()) / 2;
for (int i = 0; i < length; i++) {
if (nodeX == mRanges[i]) {
setAnim(mRanges[i] - mMinX - mDragWidth / 2, i);
break;
}
}
break;
}
}
return true;
}
private void setAnim(float moveX, final int scrollPosition) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mDragView, "translationX", mDragView.getTranslationX(), moveX);
animator.setDuration(300);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mSelectPosition != scrollPosition) {
mSelectPosition = scrollPosition;
if (mOnNodeSelect != null) {
mOnNodeSelect.onNodeSelect(mSelectPosition);
}
}
}
});
animator.start();
}
private void initView(AttributeSet attrs) {
CircleView circleView = null;
for (int i = 0; i < mHorNum; i++) {
TextView textView = insTextView();
textView.setText(mTexts[i]);
//小圆点
circleView = new CircleView(getContext(), attrs);
addView(circleView);
addView(textView);
textView.setOnTouchListener(this);
circleView.setOnTouchListener(this);
}
//横线
View view = new View(getContext());
view.setBackgroundColor(circleView.getColor());
view.setOnTouchListener(this);
addView(view);
//可拖动view
mDragView = new ImageView(getContext());
mDragView.setImageDrawable(mIcon);
mDragView.setOnTouchListener(this);
addView(mDragView);
}
private TextView insTextView() {
TextView textView = new TextView(getContext());
textView.setTextSize(mTextSize);
textView.setTextColor(mTextColor);
return textView;
}
private OnNodeSelect mOnNodeSelect;
public void setNodeSelectListener(OnNodeSelect onNodeSelect) {
mOnNodeSelect = onNodeSelect;
}
public interface OnNodeSelect {
void onNodeSelect(int position);
}
/**
* 根据手机的分辨率从dp 的单位 转成为px(像素)
*/
public int dp2px(Context context, float dpValue)
{
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 将sp值转换为px值,保证文字大小不变
*/
public int spTopx(Context context, float spValue)
{
final float scale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * scale + 0.5f);
}
}
使用方法
<com.pengkv.apple.weight.DragHoriView
android:id="@+id/dhv_select_type"
android:layout_width="match_parent"
android:layout_height="84dp"
android:background="#f5f5f5"
app:nodestext="简单,困难"
app:text_size="14"
android:layout_margin="10dp"
app:text_color="#333333"
app:drag_icon="@drawable/ic_drag"
app:circle_color="#D7D7D7"/>