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

来源:互联网 发布:mac webstorm使用 编辑:程序博客网 时间:2024/06/05 03:45

项目中遇见一个奇怪的需求 就是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地址

0 0
原创粉丝点击