效果圖
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"/>