Jonathan,
I would do as Erick suggests, but I would be careful to make sure the X and Y speed components remain interlocked, otherwise the ball's overall speed will change simply from a collision with the paddle, which is unrealistic physics.
So I would first compute the ball's overall speed from .speedX() and .speedY() (using the Pythagorean theorem), then compute the new X speed from the position of the ball relative to the paddle, and then set the Y speed dependent on a function of the new X speed and the original overall speed, so that the ball simply changes direction (in an infinite number of possible angles) but always maintains the same overall speed.
To put that more concretely, I would do it like this:
if ((m_ball.rect()).intersects(m_paddle.rect())) {
double ballWidth = m_ball.rect().getWidth();
double ballCenterX = m_ball.rect().getX() + ballWidth/2;
double paddleWidth = m_paddle.rect().getWidth();
double paddleCenterX = m_paddle.rect().getX() + paddleWidth/2;
double speedX = m_ball.speedX();
double speedY = m_ball.speedY();
// Applying the Pythagorean theorem, calculate the ball's overall
// speed from its X and Y components. This will always be a
// positive value.
double speedXY = Math.sqrt(speedX*speedX + speedY*speedY);
// Calculate the position of the ball relative to the center of
// the paddle, and express this as a number between -1 and +1.
// (Note: collisions at the ends of the paddle may exceed this
// range, but that is fine.)
double posX = (ballCenterX - paddleCenterX) / (paddleWidth/2);
// Define an empirical value (tweak as needed) for controlling
// the amount of influence the ball's position against the paddle
// has on the X speed. This number must be between 0 and 1.
final double influenceX = 0.75;
// Let the new X speed be proportional to the ball position on
// the paddle. Also make it relative to the original speed and
// limit it by the influence factor defined above.
speedX = speedXY * posX * influenceX;
m_ball.setSpeedX(speedX);
// Finally, based on the new X speed, calculate the new Y speed
// such that the new overall speed is the same as the old. This
// is another application of the Pythagorean theorem. The new
// Y speed will always be nonzero as long as the X speed is less
// than the original overall speed.
speedY = Math.sqrt(speedXY*speedXY - speedX*speedX) *
(speedY > 0? -1 : 1);
m_ball.setSpeedY(speedY);
}
Note: As time goes on, rounding errors will cause the overall speed to drift slowly from its original value. The desired overall speed might be something you want to add as member data to m_ball (rather than calculating it each time here), or it might be something you want to allow to speed up and slow down according to other gameplay factors.