自定义View-5-拖动选择按钮
效果图
View代码
第一步:自定义属性
 <declare-styleable name="DragView">
        <attr name="icon_drag" format="reference"/>
        <attr name="color_circle" format="color"/>
        <attr name="dot_num" format="integer"/>
 </declare-styleable>
- 1
 - 2
 - 3
 - 4
 - 5
 
- 1
 - 2
 - 3
 - 4
 - 5
 
第二步:自定义圆形
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.DragView_color_circle,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);
    }
}
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 - 51
 - 52
 - 53
 
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 - 51
 - 52
 - 53
 
第三步:自定义拖动按钮
public class DragView extends RelativeLayout implements View.OnTouchListener {
    private Drawable mDragIcon;//拖动图标
    private ImageView mDragView;//拖动图标ImageView
    private int mCircleColor;//圆的颜色
    private Context mContext;//上下文
    private int mDotNum;//节点数量
    private int mWidth;//组件的宽度
    private int mHight;//组件的高度
    private int mCircleRadius;//园的半径
    private int mDragWidth;//拖动图标的宽度
    private int mLineWidth,mLineHeight;//中间线的长宽
    private float mStartX;//开始拖动的屏幕坐标
    private float mCurX;//开始拖动的触点坐标
    private float mMinX;//拖动范围的最小值
    private float mMaxX;//拖动范围的最大值
    private OnNodeSelect mOnNodeSelect;//回调接口
    public DragView(Context context) {
       this(context,null);
    }
    public DragView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.DragView);
        mDragIcon=array.getDrawable(R.styleable.DragView_icon_drag);
        mCircleColor=array.getColor(R.styleable.DragView_color_circle,Color.WHITE);
        mDotNum=array.getInt(R.styleable.DragView_dot_num,2);
        array.recycle();
        mContext=context;
        initView();
    }
    /**
     *视图初始化,这个方法的作用是把所有的视图添加进来,并做一些初始化的配置
     * 其实可以用LayoutInflater把xml的布局文件加载进来,这样比较简便
     */
    public void initView(){
        for (int i=0;i<mDotNum;i++){
            CircleView circleView=new CircleView(mContext);
            circleView.setOnTouchListener(this);
            addView(circleView);
        }
        View view=new View(mContext);
        view.setBackgroundColor(mCircleColor);
        addView(view);
        // TODO: 2016/4/21 top not right
        mDragView=new ImageView(mContext);
        mDragView.setImageDrawable(mDragIcon);
        mDragView.setOnTouchListener(this);
        addView(mDragView);
    }
    /**
     *这一步主要是为了测量、配置每个view的大小,以便于后面触摸事件的处理
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth=getMeasuredWidth();
        mHight=getMeasuredHeight();
        mCircleRadius=dp2px(mContext,32);
        mDragWidth=dp2px(mContext,50);
        mLineWidth=mWidth-mDragWidth;
        mLineHeight=mCircleRadius/2;
        int count=getChildCount();
        for (int i=0;i<count;i++){
            View view=getChildAt(i);
            LinearLayout.LayoutParams params;
            if (view instanceof CircleView){
                params=new LinearLayout.LayoutParams(mCircleRadius,mCircleRadius);
                view.setLayoutParams(params);
            }else if (view instanceof ImageView){
                params=new LinearLayout.LayoutParams(mDragWidth,mDragWidth);
                view.setLayoutParams(params);
            }else {
                params=new LinearLayout.LayoutParams(mLineWidth,mLineHeight);
                view.setLayoutParams(params);
            }
        }
    }
    /**
     *这一步主要是为了放置每个view的位置,如果用LayoutInflater加载布局文件,那这一步就可以省略了
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//        super.onLayout(changed, l, t, r, b);
        if (changed){
            int count =getChildCount();
            int dLeft=0;
            int dTop=mHight/2-mDragWidth/2;
            int cLeft=mDragWidth/2-mCircleRadius/2;
            int cTop=dTop+mDragWidth/2-mCircleRadius/2;
            int lLeft=mDragWidth/2;
            int lTop=dTop+mDragWidth/2-mLineHeight/2;
            mMinX=dLeft;
            mMaxX=dLeft+mLineWidth;
            int cIndex=0;
            int cSpace=0;
            for (int i=0;i<count;i++){
                View view=getChildAt(i);
                if (view instanceof CircleView){
                    cLeft=cLeft+cSpace;
                    view.layout(cLeft,cTop,cLeft+mCircleRadius,cTop+mCircleRadius);
                    cIndex++;
                    cSpace=cIndex*mLineWidth;
                }else if (view instanceof ImageView){
                    view.layout(dLeft,dTop,dLeft+mDragWidth,dTop+mDragWidth);
                }else {
                    view.layout(lLeft,lTop,lLeft+mLineWidth,lTop+mLineHeight);
                }
            }
        }
    }
    /**
     *触碰事件的处理,这是自定义view比较重要的地方
     */
    @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();//获取view的偏移量
                    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 distance=(int) (event.getRawX()-mStartX);
                    if (distance>0){
                        if (Math.abs(distance)>mLineWidth/2){
                            v.setTranslationX(mCurX + event.getRawX() - mStartX);
                            setAnim(mLineWidth,1);
                        }else {
                            v.setTranslationX(mCurX + event.getRawX() - mStartX);
                            setAnim(0,0);
                        }
                    }else if (distance<0){
                        if (Math.abs(distance)>mLineWidth/2){
                            v.setTranslationX(mCurX + event.getRawX() - mStartX);
                            setAnim(0,0);
                        }else {
                            v.setTranslationX(mCurX + event.getRawX() - mStartX);
                            setAnim(mLineWidth,1);
                        }
                    }
                    break;
            }
        }else if (v instanceof CircleView){
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    if (event.getRawX()>getDisplayWidth()/2){
                        setAnim(mLineWidth,1);
                    }else {
                        setAnim(0,0);
                    }
                    break;
            }
        }
        return true;
    }
    //设置平移动画
    private void setAnim(float moveX, final int scrollPosition) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(mDragView, "translationX", mDragView.getTranslationX(), moveX);
        animator.setDuration(300);
        animator.start();
        if (mOnNodeSelect != null) {
            mOnNodeSelect.onNodeSelect(scrollPosition);
        }
    }
    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);
    }
    //获取屏幕宽度
    public int getDisplayWidth(){
        DisplayMetrics metrics=new DisplayMetrics();
        ((Activity)mContext).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        return metrics.widthPixels;
    }
}
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 - 51
 - 52
 - 53
 - 54
 - 55
 - 56
 - 57
 - 58
 - 59
 - 60
 - 61
 - 62
 - 63
 - 64
 - 65
 - 66
 - 67
 - 68
 - 69
 - 70
 - 71
 - 72
 - 73
 - 74
 - 75
 - 76
 - 77
 - 78
 - 79
 - 80
 - 81
 - 82
 - 83
 - 84
 - 85
 - 86
 - 87
 - 88
 - 89
 - 90
 - 91
 - 92
 - 93
 - 94
 - 95
 - 96
 - 97
 - 98
 - 99
 - 100
 - 101
 - 102
 - 103
 - 104
 - 105
 - 106
 - 107
 - 108
 - 109
 - 110
 - 111
 - 112
 - 113
 - 114
 - 115
 - 116
 - 117
 - 118
 - 119
 - 120
 - 121
 - 122
 - 123
 - 124
 - 125
 - 126
 - 127
 - 128
 - 129
 - 130
 - 131
 - 132
 - 133
 - 134
 - 135
 - 136
 - 137
 - 138
 - 139
 - 140
 - 141
 - 142
 - 143
 - 144
 - 145
 - 146
 - 147
 - 148
 - 149
 - 150
 - 151
 - 152
 - 153
 - 154
 - 155
 - 156
 - 157
 - 158
 - 159
 - 160
 - 161
 - 162
 - 163
 - 164
 - 165
 - 166
 - 167
 - 168
 - 169
 - 170
 - 171
 - 172
 - 173
 - 174
 - 175
 - 176
 - 177
 - 178
 - 179
 - 180
 - 181
 - 182
 - 183
 - 184
 - 185
 - 186
 - 187
 - 188
 - 189
 - 190
 - 191
 - 192
 - 193
 - 194
 - 195
 - 196
 - 197
 - 198
 - 199
 - 200
 - 201
 - 202
 - 203
 - 204
 - 205
 - 206
 - 207
 - 208
 - 209
 - 210
 - 211
 - 212
 - 213
 - 214
 - 215
 - 216
 - 217
 - 218
 - 219
 - 220
 - 221
 - 222
 - 223
 - 224
 - 225
 - 226
 - 227
 - 228
 - 229
 - 230
 - 231
 - 232
 
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 - 15
 - 16
 - 17
 - 18
 - 19
 - 20
 - 21
 - 22
 - 23
 - 24
 - 25
 - 26
 - 27
 - 28
 - 29
 - 30
 - 31
 - 32
 - 33
 - 34
 - 35
 - 36
 - 37
 - 38
 - 39
 - 40
 - 41
 - 42
 - 43
 - 44
 - 45
 - 46
 - 47
 - 48
 - 49
 - 50
 - 51
 - 52
 - 53
 - 54
 - 55
 - 56
 - 57
 - 58
 - 59
 - 60
 - 61
 - 62
 - 63
 - 64
 - 65
 - 66
 - 67
 - 68
 - 69
 - 70
 - 71
 - 72
 - 73
 - 74
 - 75
 - 76
 - 77
 - 78
 - 79
 - 80
 - 81
 - 82
 - 83
 - 84
 - 85
 - 86
 - 87
 - 88
 - 89
 - 90
 - 91
 - 92
 - 93
 - 94
 - 95
 - 96
 - 97
 - 98
 - 99
 - 100
 - 101
 - 102
 - 103
 - 104
 - 105
 - 106
 - 107
 - 108
 - 109
 - 110
 - 111
 - 112
 - 113
 - 114
 - 115
 - 116
 - 117
 - 118
 - 119
 - 120
 - 121
 - 122
 - 123
 - 124
 - 125
 - 126
 - 127
 - 128
 - 129
 - 130
 - 131
 - 132
 - 133
 - 134
 - 135
 - 136
 - 137
 - 138
 - 139
 - 140
 - 141
 - 142
 - 143
 - 144
 - 145
 - 146
 - 147
 - 148
 - 149
 - 150
 - 151
 - 152
 - 153
 - 154
 - 155
 - 156
 - 157
 - 158
 - 159
 - 160
 - 161
 - 162
 - 163
 - 164
 - 165
 - 166
 - 167
 - 168
 - 169
 - 170
 - 171
 - 172
 - 173
 - 174
 - 175
 - 176
 - 177
 - 178
 - 179
 - 180
 - 181
 - 182
 - 183
 - 184
 - 185
 - 186
 - 187
 - 188
 - 189
 - 190
 - 191
 - 192
 - 193
 - 194
 - 195
 - 196
 - 197
 - 198
 - 199
 - 200
 - 201
 - 202
 - 203
 - 204
 - 205
 - 206
 - 207
 - 208
 - 209
 - 210
 - 211
 - 212
 - 213
 - 214
 - 215
 - 216
 - 217
 - 218
 - 219
 - 220
 - 221
 - 222
 - 223
 - 224
 - 225
 - 226
 - 227
 - 228
 - 229
 - 230
 - 231
 - 232
 
使用方法
布局文件:
 <com.pengkv.apple.weight.DragView
        android:id="@+id/view_drag"
        android:layout_width="200dp"
        app:icon_drag="@drawable/ic_drag"
        app:color_circle="#CCCCCC"
        app:dot_num="2"
        android:layout_height="60dp"/>
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 
页面代码:
 dragView=(DragView)view.findViewById(R.id.view_drag);
 dragView.setNodeSelectListener(new DragView.OnNodeSelect() {
     @Override
     public void onNodeSelect(int position) {
         Toast.makeText(getActivity(),"我选择了"+position,Toast.LENGTH_SHORT).show();
     }
 });
                    
                
                
            
        
浙公网安备 33010602011771号