自定义 View 不难:带刻度线的 SeekBar(垂直方向)

11 篇文章 0 订阅

1e97da0737325a6483ccfebbe0ed9c36da93a5137961c-1j0DuG_fw658.png

需求

  • 实现垂直摆放
  • 根据容器大小均匀等分刻度
  • 实现长按监听

思路

  1. 将原本水平的 SeekBar 垂直摆放
  2. 添加长按接口
  3. 根据等分的份数计算每份的长度
  4. 使用 Paint 逐个画线

实现

![Screenshot_2018-06-04-16-42-04.png

Screenshot_2018-06-04-16-42-16.png

  • 因为有份数是变动的,我们需要自定义属性
  • 自定义 View
1. 自定义属性

我们将份数定义为 tick_mark_count ,整形

<?xml version="1.0" encoding="utf-8"?>

<resources>


    <declare-styleable name="tick_count">
        <attr name="tick_mark_count" format="integer" />
    </declare-styleable>


</resources>
2.支持长按的 VerticalSeekBar

原生的 SeekBar 有坑,即使调用 setOnLongClickListener 也无效,可以自己动手实验一下。So,我们需要自己定义长按接口

package com.feng.launcher.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;

/**
 * Created by Administrator on 2018/5/28.
 */

// 支持长按事件垂直摆放的 SeekBar
// VerticalSeekBar have longclick behavior
public class VerticalSeekBar extends SeekBar {

    private Drawable mThumb;
    private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
    private LongClickListener longClickListener;
    private long downTime;
    private boolean hasLongTouch = false;//是否已经执行长按操作
    private boolean possibleLongTouch = true;//可能是长按
    private float lastY;
    private float lastX;


    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1 && !hasLongTouch) {
                hasLongTouch = true;
                if (longClickListener != null) longClickListener.onLongClick();
            }
        }
    };


    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener l) {
        mOnSeekBarChangeListener = l;
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);

        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    void onProgressRefresh(float scale, boolean fromUser) {
        Drawable thumb = mThumb;
        if (thumb != null) {
            setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
            invalidate();
        }
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
        }
    }

    private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
        int available = w - getPaddingLeft() - getPaddingRight();

        int thumbWidth = thumb.getIntrinsicWidth();
        int thumbHeight = thumb.getIntrinsicHeight();

        int thumbPos = (int) (scale * available + 0.5f);


        int topBound, bottomBound;
        if (gap == Integer.MIN_VALUE) {
            Rect oldBounds = thumb.getBounds();
            topBound = oldBounds.top;
            bottomBound = oldBounds.bottom;
        } else {
            topBound = gap;
            bottomBound = gap + thumbHeight;
        }
        thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
    }

    public void setThumb(Drawable thumb) {
        mThumb = thumb;
        super.setThumb(thumb);
    }

    void onStartTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStartTrackingTouch(this);
        }
    }

    void onStopTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStopTrackingTouch(this);
        }
    }

    private void attemptClaimDrag() {
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float y = event.getY();
        float x = event.getX();

        if (!isEnabled()) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setPressed(true);
                onStartTrackingTouch();
                hasLongTouch = false;// 是否已经执行过长按事件
                possibleLongTouch = true;//是否可能为长按模式
                lastY = y;
                lastX = x;
                downTime = System.currentTimeMillis();//记录按下的时间
                if (!handler.hasMessages(1)) {//如果消息队列中已有消息 则不在重新发送
                    handler.sendEmptyMessageDelayed(1, 800);
                }
                break;

            case MotionEvent.ACTION_MOVE:
                attemptClaimDrag();
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));

                onStopTrackingTouch();
                //移动时如果x|y滑动了一段距离 则不可能为长按事件 即将 possibleLongTouch置为false
                if (lastX != 0 && lastY != 0 && (Math.abs(y - lastY) > 5 || Math.abs(x - lastX) > 5)) {
                    possibleLongTouch = false;
                    handler.removeMessages(1);
                }
                lastY = y;
                lastX = x;
                break;
            case MotionEvent.ACTION_UP:
                onStopTrackingTouch();
                setPressed(false);
                lastX = 0;
                lastY = 0;
                handler.removeMessages(1);
                if (System.currentTimeMillis() - downTime > 800 && possibleLongTouch) {
                    if (!hasLongTouch) {//如果已经执行过长按操作  则不需要再次执行
                        hasLongTouch = true;
                        if (longClickListener != null) {
                            longClickListener.onLongClick();
                        }
                    }
                    return true;
                }
                break;

            case MotionEvent.ACTION_CANCEL:
                onStopTrackingTouch();
                setPressed(false);
                handler.removeMessages(1);
                lastX = 0;
                lastY = 0;
                break;
        }
        return true;
    }

    // 定义长按接口
    // define longclick interface
    public interface LongClickListener {
        void onLongClick();
    }

    public void setLongClickListener(LongClickListener longClickListener) {
        this.longClickListener = longClickListener;
    }
}

3.带刻度的SeekBar (只支持垂直,如需水平可以修改 onDraw 方法)

TickMarkSeekBar 继承于 VerticalSeekbar,是为了实现垂直的带刻度 SeekBar


/**
 * Created by Administrator on 2018/6/4.
 * 带刻度尺的 SeekBar
 */

public class TickMarkSeekBar extends VerticalSeekBar {

    private int mTickMarkCount;
    private int  mColor;
    public TickMarkSeekBar(Context context) {
        super(context);
    }

    public TickMarkSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray =context.obtainStyledAttributes(attrs, R.styleable.TickMarkSeekBar);
        try {
            mTickMarkCount = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_count,12);
            mColor = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_color,Color.GRAY);

        }finally {
            typedArray.recycle();
        }

    }

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

    @Override
    protected void onDraw(Canvas c) {
        super.onDraw(c);
        float startX ;
        float startY ;
        float stopX ;
        float stopY ;

        Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setColor(mColor);

        float mTickDiliver = getMeasuredHeight() / mTickMarkCount;
        // 画刻度线
        for (int i = 0; i< mTickMarkCount; i++) {
            stopX = getPaddingLeft()+(i*mTickDiliver);
            startX = stopX;

            c.drawLine(startX,getMeasuredHeight(),stopX,0,linePaint);

        }

    }
}
4.使用控件
   <com.feng.launcher.view.TickMarkSeekBar
        android:id="@+id/flashlight_seekbar"
        android:layout_width="124dp"
        android:layout_height="382dp"
        android:maxHeight="200dp"
        android:progressDrawable="@drawable/seekbar"
        android:thumb="@null"
        app:tick_mark_count="5"
        android:thumbOffset="0dp"

        />

如有错误,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值