自定义按钮ClickListener咨询:PressedButton类的实现与点击处理问题
Hey there! Let's get your PressedButton working correctly. Your current code has a solid foundation, but we need to fix the pressed state management and properly integrate the click listener to make it behave like a standard Android button (with custom state support).
1. Fix the Core Pressed State Logic
First, your setPressed method is incomplete, and you're not properly integrating Android's view state system. Android uses onCreateDrawableState to combine custom states with the default view states, so we need to override that method. Here's how to fix it:
Update the setPressed Method
Your current setPressed doesn't trigger a state refresh. Let's complete it and ensure the view updates its appearance:
@Override public void setPressed(boolean isPressed) { // Update our custom flag mIsPressed = isPressed; // Reset the unpressed flag (since a button can't be both pressed and unpressed) mIsUnpressed = !isPressed; // Tell the view to refresh its drawable state refreshDrawableState(); // Don't forget to call the super method to maintain default button behavior super.setPressed(isPressed); }
Override onCreateDrawableState to Merge Custom States
This method lets us combine our custom state_pressed/state_unpressed with Android's default button states (like focused, enabled):
@Override protected int[] onCreateDrawableState(int extraSpace) { // Get the default state array from the super class int[] drawableState = super.onCreateDrawableState(extraSpace + 2); // Add our custom pressed state if applicable if (mIsPressed) { mergeDrawableStates(drawableState, STATE_PRESSED); } // Add our custom unpressed state if applicable if (mIsUnpressed) { mergeDrawableStates(drawableState, STATE_UNPRESSED); } return drawableState; }
2. Properly Implement OnClickListener
Good news: you don't need to re-invent the click listener wheel! The parent Button class already handles OnClickListener out of the box. But if you want to add custom logic when the button is clicked (alongside external listeners), you can override performClick():
Option 1: Use Default Click Listener (Simple)
Just let external code set the listener like a normal button:
// In your Activity/Fragment PressedButton myButton = findViewById(R.id.my_pressed_button); myButton.setOnClickListener(v -> { // Your click logic here Toast.makeText(this, "Custom button clicked!", Toast.LENGTH_SHORT).show(); });
Option 2: Add Custom Internal Click Logic
If you want to run some code every time the button is clicked (before external listeners), override performClick():
@Override public boolean performClick() { // Add your custom pre-click logic here (e.g., log the click, update a variable) Log.d("PressedButton", "Button was clicked!"); // Call the super method to trigger the external OnClickListener return super.performClick(); }
3. Full Working PressedButton Code
Here's the complete class with all fixes:
public class PressedButton extends Button { private static final int[] STATE_PRESSED = {R.attr.state_pressed}; private static final int[] STATE_UNPRESSED = {R.attr.state_unpressed}; private boolean mIsPressed = false; private boolean mIsUnpressed = true; // Start in unpressed state public PressedButton(Context context) { super(context); init(); } public PressedButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } public PressedButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // Optional: Set default behavior, like enabling clickable setClickable(true); } @Override public void setPressed(boolean isPressed) { mIsPressed = isPressed; mIsUnpressed = !isPressed; refreshDrawableState(); super.setPressed(isPressed); } @Override protected int[] onCreateDrawableState(int extraSpace) { int[] drawableState = super.onCreateDrawableState(extraSpace + 2); if (mIsPressed) { mergeDrawableStates(drawableState, STATE_PRESSED); } if (mIsUnpressed) { mergeDrawableStates(drawableState, STATE_UNPRESSED); } return drawableState; } @Override public boolean performClick() { // Optional custom internal logic Log.d("PressedButton", "Custom button clicked internally"); return super.performClick(); } }
4. Bonus: Handle Touch Events for Precise State Control
For even better control over when the pressed state activates (e.g., if the user presses the button and drags away), override onTouchEvent:
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: setPressed(true); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: setPressed(false); break; } // Let the super class handle the rest (like triggering performClick on ACTION_UP) return super.onTouchEvent(event); }
This ensures the pressed state only stays active while the user's finger is on the button, which matches standard Android button behavior.
内容的提问来源于stack exchange,提问作者Francisco Peters

