Skip to content

Instantly share code, notes, and snippets.

@Folyd
Last active November 3, 2015 10:49
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Folyd/1463b391d133ded157e1 to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.SeekBar;
/**
* This is workaround version for SeekBar which didn't detect the progress changed
* by arrow key pressed.
*
*/
public class KeySensitiveSeekBar extends SeekBar {
private OnKeyProgressChangeListener mOnKeyProgressChangeListener;
public KeySensitiveSeekBar(Context context) {
super(context);
}
public KeySensitiveSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public KeySensitiveSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (isEnabled()) {
int increment = getKeyProgressIncrement();
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
increment = -increment;
// fallthrough
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Let progress bar handle clamping values.
setProgress(getProgress() + increment);
onKeyChange();
return true;
}
}
return true;
}
/**
* A hook method for arrow key pressed.
*/
protected void onKeyChange() {
if (mOnKeyProgressChangeListener != null) {
mOnKeyProgressChangeListener.onKeyProgressChange(this);
}
}
public void setOnKeyProgressChangeListener(OnKeyProgressChangeListener listener) {
mOnKeyProgressChangeListener = listener;
}
/**
* A callback to indicate the progress changed by arrow key pressed.
*/
public interface OnKeyProgressChangeListener {
void onKeyProgressChange(SeekBar seekBar);
}
}
public class MediaController extends FrameLayout implements OnClickListener {
private ImageButton mPlayButton;
private TextView mCurrentTime;
private TextView mTotalTime;
private KeySensitiveSeekBar mSeekBar;
private IMediaPlayer mPlayer;
private SimpleDateFormat mDateFormat;
private static final int SHOW_PROGRESS = 0;
private static final int SEEK_BY_USER = 1;
private static final int PLAY_AGAIN = 2;
public MediaController(Context context) {
super(context);
initialize(context);
}
public MediaController(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public MediaController(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(context);
}
private void initialize(Context context) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
layoutInflater.inflate(R.layout.layout_media_controller, this, true);
mPlayButton = (ImageButton) findViewById(R.id.video_play_btn);
mPlayButton.setOnClickListener(this);
mCurrentTime = (TextView) findViewById(R.id.video_current_time);
mTotalTime = (TextView) findViewById(R.id.video_total_time);
mSeekBar = (KeySensitiveSeekBar) findViewById(R.id.video_seek_bar);
mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
mSeekBar.setOnKeyProgressChangeListener(mOnKeyProgressChangeListener);
mDateFormat = new SimpleDateFormat("mm:ss");
}
public void setMediaPlayer(IMediaPlayer mediaPlayer) {
mPlayer = mediaPlayer;
}
private Handler mSeekBarHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
int progress;
switch (msg.what) {
case SHOW_PROGRESS:
// show progress...
break;
case SEEK_BY_USER:
//Seek by user...
break;
case PLAY_AGAIN:
//Play again...
break;
}
return true;
}
});
private final OnSeekBarChangeListener mOnSeekBarChangeListener = new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mSeekBarHandler.sendEmptyMessage(SHOW_PROGRESS);
if (mPlayer != null) {
mPlayer.seekTo(seekBar.getProgress());
}
mSeekBarHandler.sendEmptyMessage(SEEK_BY_USER);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mSeekBarHandler.removeMessages(SHOW_PROGRESS);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mPlayer.seekTo(progress);
mSeekBarHandler.sendEmptyMessage(SEEK_BY_USER);
}
}
};
private final OnKeyProgressChangeListener mOnKeyProgressChangeListener = new OnKeyProgressChangeListener() {
@Override
public void onKeyProgressChange(SeekBar seekBar) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(seekBar, seekBar.getProgress(), true);
}
}
};
//Other methods omitted...
public interface IMediaPlayer {
void start();
void pause();
int getDuration();
int getCurrentPosition();
void seekTo(int pos);
boolean isPlaying();
void progressCallback(int progress);
}
}
@Folyd
Copy link
Author

Folyd commented Nov 3, 2015

I wanna to build a custom MediaController which can support forward and backward video progress by pressed arrow key,such as KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT,although android.widget.AbsSeekBar handle this in onKeyDown() method,(here is AbsSeekBar source code,target api 23 )

/**
 * Called when the user changes the seekbar's progress by using a key event.
 */
void onKeyChange() {
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (isEnabled()) {
        int increment = mKeyProgressIncrement;
        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_LEFT:
                increment = -increment;
                // fallthrough
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                increment = isLayoutRtl() ? -increment : increment;

                // Let progress bar handle clamping values.
                if (setProgress(getProgress() + increment, true)) {
                    onKeyChange();
                    return true;
                }
                break;
        }
    }

    return super.onKeyDown(keyCode, event);
}

SeekBar never override onKeyChange() method to notify the progress change by call OnSeekBarChangeListener.onProgressChanged().

The interesting thing is,RatingBar override the method,and call mOnRatingBarChangeListener.onRatingChanged() method to notify the progress change.

public class RatingBar extends AbsSeekBar {
@Override
void onKeyChange() {
    super.onKeyChange();
    dispatchRatingChange(true);
}

void dispatchRatingChange(boolean fromUser) {
    if (mOnRatingBarChangeListener != null) {
        mOnRatingBarChangeListener.onRatingChanged(this, getRating(),
                fromUser);
    }
}
}

@Folyd
Copy link
Author

Folyd commented Nov 3, 2015

KeySensitiveSeekBar and MediaController is my sample project.KeySensitiveSeekBar is my workaround solution to explain the problem. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment