android自定义可长按可点击不可拖动的seekbar

IyangcLove IyangcLove     2022-11-09     412

关键词:

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

Bootstrap jquery长按可排序?

】Bootstrapjquery长按可排序?【英文标题】:Bootstrapjquerylongpresswithsortable?【发布时间】:2018-03-0223:16:03【问题描述】:我正在尝试向一组可排序的响应式引导按钮添加长按。longclick函数似乎触发的唯一方法是我将它放在#list_content... 查看详情

自定义 TableView 单元格不可点击

】自定义TableView单元格不可点击【英文标题】:CustomTableViewCellisnotClickable【发布时间】:2020-04-0904:24:51【问题描述】:我通过为它创建一个单独的类来创建一个自定义的tableview单元格。该代码工作正常,唯一的例外是如果我点击... 查看详情

长按Android自定义键盘弹出键盘

】长按Android自定义键盘弹出键盘【英文标题】:Androidcustomkeyboardpopupkeyboardonlongpress【发布时间】:2015-06-0601:31:40【问题描述】:我有自定义Android键盘:publicclassCustomKeyboardextendsKeyboard...publicclassCustomKeyboardViewextendsKeyboardView...public... 查看详情

自定义复选框在剑道网格中不可点击

】自定义复选框在剑道网格中不可点击【英文标题】:Customizedcheckboxesarenotclickableinkendogrid【发布时间】:2020-06-0703:14:39【问题描述】:我有带有复选框选择列的剑道网格,我自定义了这些复选框,但现在复选框不可点击,无法... 查看详情

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

】如何正确实现可拖动的“抽屉”自定义UIView【英文标题】:Howtoproperlyimplementadraggable"drawer"customUIView【发布时间】:2014-01-1020:25:56【问题描述】:我的视图控制器底部有一个自定义UIView,它充当“抽屉”控件。大约90像... 查看详情

TabBarController 添加自定义按钮不可点击问题[重复]

】TabBarController添加自定义按钮不可点击问题[重复]【英文标题】:TabBarControlleraddingcustombuttonnotclickableissue[duplicate]【发布时间】:2017-08-2512:41:29【问题描述】:我在tabBarController上添加了一个自定义按钮,当我在tabBarController内点... 查看详情

如何在 Android 自定义视图中拖动和对齐项目?

】如何在Android自定义视图中拖动和对齐项目?【英文标题】:HowtodragandalignaniteminAndroidcustomview?【发布时间】:2016-07-2622:57:57【问题描述】:我创建了这个自定义视图来在应用顶部添加一个图层视图中有一个可拖动的圆圈publicclas... 查看详情

前端大杂记

...1.zTree:国人开发,国语文档,更新少,主题较丑,可自定义主题2.jqTree:支持懒加载、拖动,主题样式不可自定义3.Fancytree:支持懒加载、拖动,自带多套主题样式 查看详情

自定义长按手势识别器

】自定义长按手势识别器【英文标题】:Customizelongpressgesturerecognizer【发布时间】:2017-07-2700:05:23【问题描述】:我想通过以下方式自定义我的长按手势识别器:1)当我按住一个物体0.5秒时,物体变暗,并且2)当我继续按住对象一... 查看详情

qt样式自定义001:sliderlineedit滑动输入框

CustomControllerCollection自定义控件集001SliderLineEdit滑动输入框功能:支持点击编辑和长按鼠标拖动变化;func:Aninputboxthatsupportsbothinputeditingandmouseslidingcontrol; 主要是继承QLineEdit类重新实现其鼠标事件,建议禁用输入框默认的菜单... 查看详情

使视图可拖动但不可点击

】使视图可拖动但不可点击【英文标题】:Makeviewdraggablebutnottappable【发布时间】:2018-11-2115:19:09【问题描述】:我的视图控制器中有一个标签,我在viewDidLoad中设置了这样的标签:varlabel:UILabel!overridefuncviewDidLoad()super.viewDidLoad()la... 查看详情

android手势操作中滑动和滚动的区别

...下,而且滑动操作范围有限,滚动范围比较大参考技术Aandroid手机有哪些手势操作:“点击”,也叫“单击”,即轻触屏幕一下。无论是搭载何种操作系统的触控手机,这个都是出现频率最高的动作。点击主要用来打开程序,还... 查看详情

拖动时停止 OnLongClickListener 触发

...:34【问题描述】:我有一个用户可以拖动的带有位图的自定义视图。我想这样做,所以当他们长按其中一个时,我可以弹出一个上下文菜单,其中包含重置位置等选项。在自定义视图中,我添加了我的OnLongClickListener:this.set 查看详情

移动网络 - 禁用长按/点击文本选择

...本选择的所有信息,但这些都不能解决我遇到的问题。在Android上(我猜是在iPhone上),如果您点击并按住文本,它会突出显示它并显示一些小标志来拖动和选择文本。我需要禁 查看详情

自定义数字软键盘

需求:1.数字键盘2.输入删除,光标要随之移动3.删除键长按全部清除4.点击键盘背景有颜色变换效果效果图:布局<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_p 查看详情

Android UI 测试长按拖拽

】AndroidUI测试长按拖拽【英文标题】:AndroidUITestLongClickandDrag【发布时间】:2017-12-3118:45:58【问题描述】:在我的应用程序中,我有一个功能,即用户可以通过长按创建拖动阴影来重新排序列表中的项目,然后用户可以将其拖动... 查看详情

UICollectionView 中的长按和平移手势

...2016-06-2106:40:02【问题描述】:我正在处理从GitHub下载的自定义日历。这是一个自定义视图,其中添加了UICollectionView以显示日期单元格。我正在添加一个拖动单元格以获取多个日期值的功能。为此,我添加了UILongpressgesture我已经... 查看详情

如何使半视图不可点击

...有一个按钮。按钮和整个项目/行是可点击的。我做到了android:clickable="false"用于ImageButton。正如您在 查看详情