开发钢琴APP:如何实现触摸按钮时固定ScrollView,仅通过滚动条控制?
Hey there! Let's work through how to implement this piano app requirement where your ScrollView only scrolls when touching the scrollbar, and ignores touches on buttons, EditTexts, or other child views. Here's a step-by-step approach:
The key here is to customize the ScrollView's touch event handling to selectively intercept events: only allow scrolling when the user touches the scrollbar, and pass all other touch events to child views (so they can respond to clicks, input, etc.) without triggering ScrollView scrolling.
1. Create a Custom ScrollView Class
We'll extend the default ScrollView and override two critical touch event methods: onInterceptTouchEvent and onTouchEvent. These methods let us control when the ScrollView takes over touch events.
Java Code Example
public class PianoScrollView extends ScrollView { private int scrollbarWidth; public PianoScrollView(Context context) { super(context); init(); } public PianoScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public PianoScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // Get the width of the vertical scrollbar scrollbarWidth = getVerticalScrollbarWidth(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // Check if the touch is on a child view we want to exclude (Button/EditText) View touchedChild = findChildViewUnder(ev.getX(), ev.getY()); if (touchedChild != null && (touchedChild instanceof Button || touchedChild instanceof EditText)) { // Don't intercept the event - let the child view handle it return false; } // Check if the touch is on the scrollbar area boolean isTouchingScrollbar = ev.getX() > (getWidth() - scrollbarWidth); if (isTouchingScrollbar) { // Let the default ScrollView behavior take over for scrolling return super.onInterceptTouchEvent(ev); } // Ignore all other touch areas - no scrolling return false; } @Override public boolean onTouchEvent(MotionEvent ev) { // Only handle touch events if we're touching the scrollbar boolean isTouchingScrollbar = ev.getX() > (getWidth() - scrollbarWidth); return isTouchingScrollbar ? super.onTouchEvent(ev) : false; } }
Kotlin Code Example
class PianoScrollView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ScrollView(context, attrs, defStyleAttr) { private var scrollbarWidth = 0 init { scrollbarWidth = verticalScrollbarWidth } override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { val touchedChild = findChildViewUnder(ev.x, ev.y) if (touchedChild != null && (touchedChild is Button || touchedChild is EditText)) { return false } val isTouchingScrollbar = ev.x > (width - scrollbarWidth) return if (isTouchingScrollbar) { super.onInterceptTouchEvent(ev) } else { false } } override fun onTouchEvent(ev: MotionEvent): Boolean { val isTouchingScrollbar = ev.x > (width - scrollbarWidth) return if (isTouchingScrollbar) { super.onTouchEvent(ev) } else { false } } }
2. Use the Custom ScrollView in Your Layout
Replace the default <ScrollView> tag in your XML layout with your custom PianoScrollView:
<com.yourpackage.PianoScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Your piano keys (Buttons), EditTexts, and other child views go here --> </com.yourpackage.PianoScrollView>
3. Customize the Excluded Child Views (Optional)
If you have other custom views that should prevent scrolling when touched, just add them to the instanceof (Java) or is (Kotlin) check in onInterceptTouchEvent. For example:
// Add your custom view type if (touchedChild != null && (touchedChild instanceof Button || touchedChild instanceof EditText || touchedChild instanceof PianoKeyView)) { return false; }
- Child View Handling: When the user touches a Button or EditText, the ScrollView doesn't intercept the touch event, so the child view can respond normally (e.g., button clicks, text input).
- Scrollbar Only Scrolling: The ScrollView only processes touch events that occur within the scrollbar's bounds. All other touch areas (even empty space in the ScrollView) won't trigger scrolling.
内容的提问来源于stack exchange,提问作者Mohamad Fattahi

