Android自定义可长按 可点击不可拖动的SeekBar

Posted IyangcLove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义可长按 可点击不可拖动的SeekBar相关的知识,希望对你有一定的参考价值。

项目中遇见一个奇怪的需求 就是SeekBar不可点击 ,可长按 和拉着拖拽点拖动。一开始想着很简单嘛。屏蔽吊点击事件,SeekBar自带长按事件,差不多就搞定了,然而是我太天真啊,SeekBar自带的长按监听是无效的。我通过网上查寻资料 有一些简单的方法实现不可单击的 有实现长按的,然后我根据这些方式自己写了一个 满足需求的自定义控件。现在依次说下吧。
1. 不可点击的SeekBar
这个可以直接通过seekBar的监听 修改SeekBar的进度 让其达到显示的效果。
可以重写SeekBar 设置其setOnSeekBarChangeListener监听方法
此方式点击SeekBar是触发了 3个方法的 只是进度条没让他跳过去而已
可以看看打印的Log

 /**
 * 不可点击 长按无效的seekbar
 */
public class MySeekBar extends SeekBar  {
    private  int oldsign;

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

    public MySeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init() {
        setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                if(progress>oldsign+3||progress<oldsign-3){
                    seekBar.setProgress(oldsign);
                    return;
                }
                seekBar.setProgress(progress);
                oldsign = progress;
            }
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                seekBar.setProgress(oldsign);
            }
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }

        });
    }

}

也可以直接在Activity中用普通的SeekBar进行设置

        //sb为原生的SeekBar
        oldsign = sb.getProgress();
        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
//              Toast.makeText(MainActivity.this, "onProgressChanged"+progress, 0).show();
                Log.e("Chuck", "onProgressChanged");
                if(progress>oldsign+3||progress<oldsign-3){
                    seekBar.setProgress(oldsign);
                    return;
                }
                seekBar.setProgress(progress);
                oldsign = progress;
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                Log.e("Chuck", "onStartTrackingTouch");
                    seekBar.setProgress(oldsign);
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                Log.e("Chuck", "onStopTrackingTouch");
            }

        });

这样都是可以达到 不可点击 可拖拽的效果的,接下来看看 可长按的SeekBar。
长按监听的话 是自己通过onTouch事件监听判断的然后写了一个接口把长按事件传递出来,详细代码如下:

public class SeekBarLongClick extends SeekBar implements OnTouchListener {

    private onLong longClick;
    private  int oldsign;//获取上次更改后的点击状态
    /**
     * 长按接口
     */
    public interface onLong {
        public boolean onLongClick(SeekBarLongClick seekBar);
    }

    private onChange SeekBarChange;

    /**
     * 进度改变接口
     */
    public interface onChange {
        public void onStopTrackingTouch(SeekBarLongClick seekBar);

        public void onStartTrackingTouch(SeekBarLongClick seekBar);

        public void onProgressChanged(SeekBarLongClick seekBar, int progress,
                                      boolean fromUser);
    }


    private Handler hand;
    private Runnable runable;
    private Thread th;
    public static int i = 0;
    private boolean isStop = false;
    public static int pp = 0;
    public int index = 0;

    public SeekBarLongClick(Context context) {
        this(context, null);
        // TODO Auto-generated constructor stub
    }

    public SeekBarLongClick(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setOnTouchListener(this);
        this.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
                if (SeekBarChange != null) {
                    SeekBarChange.onStopTrackingTouch(SeekBarLongClick.this);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
                seekBar.setProgress(oldsign);
                if (SeekBarChange != null) {
                    SeekBarChange.onStartTrackingTouch(SeekBarLongClick.this);
                }
            }

            @Override
            public void onProgressChanged(final SeekBar seekBar,
                    final int progress, boolean fromUser) {

                if(progress>oldsign+3||progress<oldsign-3){
                    seekBar.setProgress(oldsign);
                }else{
                seekBar.setProgress(progress);
                oldsign = progress;
                }
                if (SeekBarChange != null) {
                    SeekBarChange.onProgressChanged(SeekBarLongClick.this, oldsign,
                            fromUser);
                }
                hand = getHandler(1, SeekBarLongClick.this, oldsign);
            }
        });
        /**
         * 为runable 赋值
         */
        runable = new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                do {
                    i++;
                    try {
                        Thread.sleep(400);
                        Message msg = hand.obtainMessage();
                        msg.arg1 = i;
                        msg.sendToTarget();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } while (isStop);
            }
        };
    }

    /**
     * 获取一个handler 对象
     * @param  j 0代表onTouch 1代表onChange
     * @param  v 视图对象
     * @param progress 进度
     * @return 返回一个handler对象
     */
    public Handler getHandler(final int j, final View v, final int progress) {
        Handler h = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (j) {
                case 0:
                    if (msg.arg1 == 3) {
                        if (longClick != null) {
                            longClick.onLongClick(SeekBarLongClick.this);
                            isStop = false;
                        }
                    }
                    break;
                case 1:
                    if (msg.arg1 == 1) {
                        pp = progress;
                    }
                    if (msg.arg1 == 2) {
                        if (pp != progress) {
                            i = 0;
                        }
                    }
                    if (msg.arg1 == 3) {
                        i = 0;
                        if (pp == progress) {
                            if (longClick != null) {
                                longClick.onLongClick(SeekBarLongClick.this);
                                isStop = false;
                            }
                        }
                    }
                    break;
                }
                super.handleMessage(msg);
            }
        };
        return h;
    }
    /**
     * 设置长按事件
     * @param longClick
     */
    public void setOnLongSeekBarClick(onLong longClick) {
        this.longClick = longClick;
    }

    /**
     * 设置进度改变事件
     * @param change
     */
    public void setOnSeekBarChange(onChange change) {
        this.SeekBarChange = change;
    }

    @Override
    public boolean onTouch(final View v, MotionEvent event) {
        // TODO Auto-generated method stub
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            float x2 = event.getX();
            float y2 = event.getY();

            isStop = true;
            th = new Thread(runable);
            th.start();
            i = 0;
            hand = getHandler(0, v, 0);
            break;
        case MotionEvent.ACTION_UP:
            isStop = false;
            break;
        }
        return false;
    }
}

接下来就是可长按 可拖动 不可点击,而且点击不会触发SeekBar监听的自定义SeekBar了。控件继承FrameLayout,在SeekBar上面盖上了一层透明的ImageView,通过ImageView的onTouch事件 相应的对SeekBar进行操作,控件中还添加了一个添加关键点的功能,主要用于 用于点击关键点时才能跳到对应的进度,不添加关键点 看起来 用起来就和普通的seekbar一模一样。拖动进度 是根据SeekBar控件的宽度来的,所以要计算一些坐标 和控件宽高,使用时在Activity的onWindowFocusChanged()方法中设置。如果是固定宽高 可直接写死。详细代码如下:

public class SeekBarView extends FrameLayout {

    private onLong longClick; // 长按监听
    private MotionEvent motionEvent;// 移动监听的对象
    private float scale; // 当前比例
    private Context context;
    boolean isMove = false; // 当前是否是移动动作

    float xDown, yDown, xUp;
    private SeekBar seekBar;
    private ImageView image;
    private RelativeLayout rl_container;
    private int marginLeft;
    private int seekBarWidth;
    private int moveWidth;
    private boolean longClicked;
    /**
     * 长按接口
     * 
     * @author terry
     * 
     */
    public interface onLong {
        public boolean onLongClick(SeekBarView seekBar);
    }

    private onChange SeekBarChange;// SeekBar进度监听

    /**
     * 进度改变接口,仿SeekBar
     * 
     * @author terry
     * 
     */
    public interface onChange {
        public void onStopTrackingTouch(SeekBarView seekBar);

        public void onStartTrackingTouch(SeekBarView seekBar);

        /**
         * @param seekBar
         *            SeekBarView对象
         * @param progress
         *            进度条
         * @param fromUser
         *            只有拖动 为true,长按 不触发此方法,点击关键点为false.
         */
        public void onProgressChanged(SeekBarView seekBar, int progress,
                boolean fromUser);
    }

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

    public SeekBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        View view = LayoutInflater.from(context).inflate(R.layout.view_seekbar,
                null);
//      initView(view);
        addView(view);

    }

    public SeekBarView(Context context, AttributeSet attrs, int defStyle) {
        this(context, attrs);
    }

    public void initView(View view) {
        seekBarWidth = getWidth();
        int [] ints=new int[2];
        getLocationOnScreen(ints); 
        marginLeft = ints[0];

        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay()
                .getMetrics(displayMetrics);
        moveWidth = seekBarWidth - dip2px(context, 20);
        // 播放时间为1000秒,
        seekBar = (SeekBar) view.findViewById(R.id.seekbar);
        scale = (float) seekBar.getMax() / (float) moveWidth;
        rl_container = (RelativeLayout) view.findViewById(R.id.rl_container);
        image = (ImageView) view.findViewById(R.id.image);
        //motionEvent获取到的是Activity 的 坐标······ 与Seekbar的坐标 x 有margin距离
        image.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                int x = (int) motionEvent.getX();
                if (Math.abs(x - xDown - marginLeft) < 10) {
                    seekBar.setProgress((int) ((int) (x - marginLeft) * scale));
                    isMove = false;
                    if (longClick != null) {
                        longClick.onLongClick(SeekBarView.this);
                        longClicked=true;
                    }
                }
                return false;
            }
        });
        image.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (motionEvent == null) {
                    motionEvent = event;
                }
                // 当按下时处理
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    xDown = event.getX();
                    yDown = event.getY();

                }// 松开处理
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    isMove = false;
                    xUp = event.getX();
                    if (SeekBarChange != null&&!longClicked) {
                        SeekBarChange.onStopTrackingTouch(SeekBarView.this);
                    }
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {

                    float x = event.getX();
                    final int progress = seekBar.getProgress();
                    // 如果点击位置是当前进度位置对应坐标的20px以内就认为点击到了当前位置 ,拖动可调整进度条
                    if (!isMove && Math.abs(progress - scale * xDown) <= 20) {
                        isMove = true;
                    }
                    if (isMove) {
                        seekBar.setProgress((int) (scale * x));
                        if (SeekBarChange != null) {
                            SeekBarChange.onProgressChanged(SeekBarView.this,
                                    (int) (scale * x), true);
                        }
                    } else {
                        // 其他模式
                    }
                }
                return false;
            }
        });
    }
    public void setMax(int max) {
        seekBar.setMax(max);
        scale= (float) seekBar.getMax() / (float) moveWidth ;
    }

    public int getMax() {
        return seekBar.getMax();
    }

    public void setProgress(int progress) {
        seekBar.setProgress(progress);
    }

    public int getProgress() {
        return seekBar.getProgress();
    }

    public void addKeyPoint(final int[] pointIndexs) {
        for (int i = 0; i < pointIndexs.length; i++) {
            final int tempI = i;
            ImageView imageView = new ImageView(context);
            int imgSize = dip2px(context, 20);
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(imgSize, imgSize);
            // 距离左边的位置···因为图标本身长度为20dp 所以这里需要减去10dp
            int marginLeft = (int) (pointIndexs[i] / scale);
            layoutParams.leftMargin = marginLeft;
            imageView.setLayoutParams(layoutParams);
            imageView.setImageResource(R.drawable.main_homework_up);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            rl_container.addView(imageView);
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    seekBar.setProgress(pointIndexs[tempI]);
                    if (SeekBarChange != null) {
                        SeekBarChange.onProgressChanged(SeekBarView.this,
                                pointIndexs[tempI], false);
                    }
                }
            });
        }
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    private int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    private int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
    /**
     * 设置长按事件
     * 
     * @param longClick
     */
    public void setOnLongSeekBarClick(onLong longClick) {
        this.longClick = longClick;
    }

    /**
     * 设置进度改变事件
     * 
     * @param change
     */
    public void setOnSeekBarChange(onChange change) {
        this.SeekBarChange = change;
    }
}
//对应的xml布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
   <SeekBar
       android:progressDrawable="@drawable/seekbar_img"
        android:thumb="@drawable/public_play_volume_dragpoint"
        android:id="@+id/seekbar"
        android:background="#00ff0000"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxHeight="8dp"
        android:minHeight="8dp"
         android:layout_gravity="center"
        />
    <ImageView
        android:id="@+id/image"
         android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#0000ff00"
  />
     <RelativeLayout
        android:background="#000000ff"
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:gravity="center_vertical"/>


</FrameLayout>
//在Activity中调用
    /**
     * @param hasFocus
     *
     * 第一次界面渲染完成时 对自定义的SeekBarView进行初始化 和 测量。
     */
    boolean isOne=false;
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        if(isOne){
            isOne=false;
            barView.initView(barView);
            barView.setMax(1000);
            //添加关键点。不添加即无效果
            barView.addKeyPoint(new int[]{100,300,620,530,1000,700});

            barView.setOnLongSeekBarClick(new SeekBarView.onLong() {
                @Override
                public boolean onLongClick(SeekBarView seekBar) {
                    Log.e("Chuck", "onLongClick() 方法触发  progress = " +seekBar. getProgress());
                    return false;
                }
            });
            barView.setOnSeekBarChange(new SeekBarView.onChange() {

                //手指离开屏幕,可能长按,可能拖动,判断按下屏幕之前 计时器的状态 进行相应的操作
                @Override
                public void onStopTrackingTouch(SeekBarView seekBar) {
                    Log.e("Chuck", "onStopTrackingTouch()方法触发    progress = " + seekBar.getProgress()); 
                    if(!playerTimer.isStarted()){
                           playerTimer.start();
                        }
                }
                @Override
                public void onStartTrackingTouch(SeekBarView seekBar) {
                }


                //fromUser 只有拖动的时候为true,长按不触发此方法,点击关键点时为false.
                @Override
                public void onProgressChanged(SeekBarView seekBar, int progress,
                        boolean fromUser) {
                    if(fromUser){
                        if(playerTimer.isStarted()){
                           playerTimer.stop();
                        }
                        tv.setText(progress+"----------------"+barView.getMax());
                    }

                    Log.e("Chuck", "onProgressChanged()方法触发  progress=" + progress + "  "+"fromUser="+fromUser );
                }
            });
            playerTimer.start();
        }
        super.onWindowFocusChanged(hasFocus);
    }

最终的效果
这里写图片描述

Demo下载地址(AndroidStudio写的比较大额)
GitHub地址
CSDN地址

以上是关于Android自定义可长按 可点击不可拖动的SeekBar的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义可长按 可点击不可拖动的SeekBar

Android自定义万能Canvas画布

android开发中获取选中文字以及弹出框

在Android中单击后使标记可拖动

iOS 手势操作:拖动捏合旋转点按长按轻扫自定义

如何正确实现可拖动的“抽屉”自定义 UIView