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