Android SeekBar:仅通过拇指移动

发布于 2025-01-10 08:04:51 字数 7162 浏览 1 评论 0原文

我知道这个问题之前被问过并且有所回答,但是到目前为止我发现的解决方案都没有真正适合我:我有一个自定义的seekBar,其中有一个用于拇指的大方形块,我即时绘制,我想要仅当拖动拇指时,seekBar 进度才会改变。 大拇指搜索栏

我环顾四周并尝试了我能找到的所有建议的解决方案,还有一些比其他人工作得更好,但它们都有一个大问题(至少对我来说):如果我检测到拇指的边界以便开始拖动拇指,onTouch ACTION_DOWN 事件会使拇指跳转到指针所在的任何位置屏幕内,当然是拇指的边界。

如果拇指尺寸很小,这几乎不是问题,但对于大拇指来说,这种行为真的很烦人。

更糟糕的是,如果我声明拇指偏移量为 0 以将拇指保持在搜索栏内,则跳跃行为会根据拇指在搜索栏上的位置而变化:在搜索栏中间不确定的跳跃方向,或者它跳跃如果拇指位于搜索栏的左半部分,则向右;如果拇指位于搜索栏的右半部分,则向左。

这是我的起始代码,我删除了所有失败的尝试,以使seekBar的拇指仅在拖动开始时移动:

activity_main.xml

   <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/progressText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mySeekbar"
        app:layout_constraintVertical_bias="0.154" />

    <com.example.thumbonlyseekbar.MySeekBar
        android:id="@+id/mySeekbar"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="150dp"
        android:layout_marginEnd="20dp"
        android:indeterminate="false"
        android:max="99"
        android:progress="0"
        android:progressDrawable="@drawable/my_seekbar"
        android:thumb="@drawable/thumb"
        android:thumbOffset="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

MySeekbar.java

package com.example.thumbonlyseekbar;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;


public class MySeekBar extends androidx.appcompat.widget.AppCompatSeekBar {

    Drawable mThumb;

    public MySeekBar(@NonNull Context context) {
        super(context);
    }

    public MySeekBar(@NonNull Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    public void setThumb(Drawable thumb) {
        super.setThumb(thumb);
        mThumb = thumb;
    }
    public Drawable getSeekBarThumb() {
        return mThumb;
    }

}

MainActivity.java

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

thumb.xml

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

my_seekbar.xml

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

如果您想查看我所说的无拇指跳转的seekBar 的示例,请看一下在 Google Play 商店中的 AMPLIFi Remote 应用程序中(它可能仅适用于较旧的设备,它适用于我的 OREO 8.1,如果您不这样做,您可能需要在允许从 Google Play 下载的模拟器中尝试它)没有旧设备)。拇指的移动非常顺畅,绝对没有跳动的情况。编写该应用程序的人做得非常出色!

非常非常感谢,非常感谢任何建议!

I know this question was asked and somewhat answered before, but none of the solutions I found so far really worked out for me: I have a custom seekBar with a large square block for the thumb, which I draw on the fly, and I want the seekBar progress to change only when the thumb is being dragged.
Large thumb seekBar

I looked around and tried out all of the proposed solutions I could find, and some worked better then others, but they all have a big issue (for me at least): if I detect the boundaries of the thumb in order to start dragging the thumb, the onTouch ACTION_DOWN event makes the thumb jump to whatever position the pointer is on the screen, within the thumb's boundaries, of course.

If the thumb size is small, this is almost a non issue, but on a large thumb, this behavior is really annoying.

To make matters worse, if I declare a thumb offset of 0 to keep the thumb inside the seekBar, the jumping behavior changes with respect to where on the seekbar the thumb is: indetermined jump direction right in the middle of the seekbar, or it jumps to the right if the thumb is on the left half of the seekbar, or to the left if the thumb is on the right half of the seekbar.

Here's my starting code, I removed all of my failed attempts to make the seekBar's thumb move only when a drag starts:

activity_main.xml

   <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/progressText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mySeekbar"
        app:layout_constraintVertical_bias="0.154" />

    <com.example.thumbonlyseekbar.MySeekBar
        android:id="@+id/mySeekbar"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="150dp"
        android:layout_marginEnd="20dp"
        android:indeterminate="false"
        android:max="99"
        android:progress="0"
        android:progressDrawable="@drawable/my_seekbar"
        android:thumb="@drawable/thumb"
        android:thumbOffset="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

MySeekbar.java

package com.example.thumbonlyseekbar;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;


public class MySeekBar extends androidx.appcompat.widget.AppCompatSeekBar {

    Drawable mThumb;

    public MySeekBar(@NonNull Context context) {
        super(context);
    }

    public MySeekBar(@NonNull Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    public void setThumb(Drawable thumb) {
        super.setThumb(thumb);
        mThumb = thumb;
    }
    public Drawable getSeekBarThumb() {
        return mThumb;
    }

}

MainActivity.java

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

thumb.xml

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

my_seekbar.xml

package com.example.thumbonlyseekbar;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    MySeekBar customSeekBar;
    TextView progressValueText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        customSeekBar = findViewById(R.id.mySeekbar);

        progressValueText = findViewById(R.id.progressText);
        progressValueText.setText(String.valueOf(customSeekBar.getProgress()));


        customSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressValueText.setText(String.valueOf(progress));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }
}

If you would like to see an example of what I mean by a no-thumb-jump seekBar, please take a look at the AMPLIFi Remote app in Google Play Store (it may only work on older devices, it works on my OREO 8.1, you may need to try it in an emulator that allows downloads from Google Play if you don't have an older device). The thumb's progress is unbelievably smooth, and there's absolutely no jumping around. Whoever wrote that app did an awesome job!

Many, many thanks, any suggestion is much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文