Как реализовать кнопку с двойным коротким и непрерывным нажатием? - PullRequest
4 голосов
/ 07 мая 2011

Я создаю MP3-плеер и хочу двойную кнопку Next Song / Fast Forward.Таким образом, если эта кнопка нажата, она перейдет к следующей песне, и если она будет удерживаться, она будет быстро перемещаться по текущей песне.

Я могу заставить следующую песню работать, используя OnClickListener ...

private OnClickListener mSkipForwardListener = new OnClickListener() {
    public void onClick(View v)
    {
        mPlayerService.forwardASong();
    }
};

... но как мне получить функциональность ускоренной перемотки?Я попробовал OnLongClickListener, но он срабатывает только один раз.

private OnLongClickListener mFastForwardListener = new OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        mPlayerService.fastForward();
        return true;
    }
};

И, кажется, onTouch срабатывает только один раз на key.down и key.up.

private OnTouchListener mFastForwardListener = new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mPlayerService.fastForward();
        return true;
    }
};

Любая помощь, высоко ценится,M.

Ответы [ 2 ]

6 голосов
/ 07 мая 2011

Возвращая true из onTouch, вы потребляете событие касания, поэтому ваша кнопка даже не видит его.Причина, по которой больше не происходит сенсорных событий, заключается в том, что никакой View фактически не обработал событие down.

Так что вам нужно вернуть false из onTouch в ваш слушатель.(И надеюсь, что базовое представление продолжает возвращать true, потому что, если кнопка View - в этом случае - возвращает false из своего onTouchEvent, то больше никаких событий не будет отправлено вашему слушателю - для кнопки это нормально, но длядругие представления переопределяют onTouchEvent вместо использования слушателя для большего контроля и надежности).

Что-то вроде следующего OnTouchListener должно быть примерно правильным (это должен быть внутренний класс Activity, и donТакже не устанавливайте OnClickListener, потому что он также будет вызываться!):

private abstract class LongTouchActionListener implements OnTouchListener {

    /**
     * Implement these methods in classes that extend this
     */
    public abstract void onClick(View v);
    public abstract void onLongTouchAction(View v);

    /**
     * The time before we count the current touch as
     * a long touch
     */
    public static final long LONG_TOUCH_TIME = 500;

    /**
     * The interval before calling another action when the
     * users finger is held down
         */
    public static final long LONG_TOUCH_ACTION_INTERVAL = 100;

    /**
     * The time the user first put their finger down
     */
    private long mTouchDownTime;

    /**
     * The coordinates of the first touch
     */
    private float mTouchDownX;
    private float mTouchDownY;

    /**
     * The amount the users finger has to move in DIPs
     * before we cancel the touch event
     */
    public static final int TOUCH_MOVE_LIMIT_DP = 50;

    /**
     * TOUCH_MOVE_LIMIT_DP converted to pixels, and squared
     */
    private float mTouchMoveLimitPxSq;

    /**
     * Is the current touch event a long touch event
         */
    private boolean mIsLongTouch;

    /**
     * Is the current touch event a simple quick tap (click)
     */
    private boolean mIsClick;

    /**
     * Handlers to post UI events
     */
    private LongTouchHandler mHandler;

    /**
     * Reference to the long-touched view
     */
    private View mLongTouchView;

    /**
     * Constructor
     */

    public LongTouchActionListener(Context context) {
        final float scale = context.getResources().getDisplayMetrics().density;
        mTouchMoveLimitPxSq = scale*scale*TOUCH_MOVE_LIMIT_DP*TOUCH_MOVE_LIMIT_DP;

        mHandler = new LongTouchHandler();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        final int action = event.getAction();

        switch (action) {

        case MotionEvent.ACTION_DOWN:
            // down event
            mIsLongTouch = false;
            mIsClick = true;

            mTouchDownX = event.getX();
            mTouchDownY = event.getY();
            mTouchDownTime = event.getEventTime();

            mLongTouchView = view;

            // post a runnable
            mHandler.setEmptyMessageDelayed(LongTouchHandler.MESSAGE_LONG_TOUCH_WAIT, LONG_TOUCH_TIME);
            break;

        case MotionEvent.ACTION_MOVE:
            // check to see if the user has moved their
            // finger too far
            if (mIsClick || mIsLongTouch) {
                final float xDist = (event.getX() - mTouchDownX);
                final float yDist = (event.getY() - mTouchDownY);
                final float distanceSq = (xDist*xDist) + (yDist*yDist);

                if (distanceSq > mTouchMoveLimitSqPx) {
                    // cancel the current operation
                    mHandler.removeMessages(LongTouchHandler.MESSAGE_LONG_TOUCH_WAIT);
                    mHandler.removeMessages(LongTouchHandler.MESSAGE_LONG_TOUCH_ACTION);

                    mIsClick = false;
                    mIsLongTouch = false;
                }
            }
            break;

        case MotionEvent.ACTION_CANCEL:
            mIsClick = false;
        case MotionEvent.ACTION_UP:
            // cancel any message
            mHandler.removeMessages(LongTouchHandler.MESSAGE_LONG_TOUCH_WAIT);
            mHandler.removeMessages(LongTouchHandler.MESSAGE_LONG_TOUCH_ACTION);

            long elapsedTime = event.getEventTime() - mTouchDownTime;
            if (mIsClick && elapsedTime < LONG_TOUCH_TIME) {
                onClick(v);
            }
            break;

        }

        // we did not consume the event, pass it on
        // to the button
        return false; 
    }

    /**
     * Handler to run actions on UI thread
     */
    private class LongTouchHandler extends Handler {
        public static final int MESSAGE_LONG_TOUCH_WAIT = 1;
        public static final int MESSAGE_LONG_TOUCH_ACTION = 2;
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_LONG_TOUCH_WAIT:
                    mIsLongTouch = true;
                    mIsClick = false;

                    // flow into next case
                case MESSAGE_LONG_TOUCH_ACTION:
                    if (!mIsLongTouch) return;

                    onLongTouchAction(mLongTouchView); // call users function

                    // wait for a bit then update
                    takeNapThenUpdate(); 

                    break;
            }
        }

        private void takeNapThenUpdate() {
            sendEmptyMessageDelayed(MESSAGE_LONG_TOUCH_ACTION, LONG_TOUCH_ACTION_INTERVAL);
        }
    };
};

А вот пример реализации

private class FastForwardTouchListener extends LongTouchActionListener {
    public void onClick(View v) {
        // Next track
    }

    public void onLongTouchAction(View v) {
        // Fast forward the amount of time
        // between long touch action calls
        mPlayer.seekTo(mPlayer.getCurrentPosition() + LONG_TOUCH_ACTION_INTERVAL);
    }
}
2 голосов
/ 13 июня 2012

Вдохновленный предложенным выше кодом, вы можете заставить одну или несколько кнопок непрерывно выполнять свое действие периодически, пока она еще нажата, используя обработчик сообщений Activity, но более простым способом, чем предложено выше (без введения частных абстрактных расширенных классов) , Во время короткого нажатия также просто выполнить другое действие, чем при непрерывном нажатии, с небольшим отклонением от того, что я покажу ниже.

В инициализации кнопки реализуйте оба метода onClick и onTouch:

    myButton = (Button) findViewById(R.id.MyButton);
    myButton.setOnClickListener(
            new OnClickListener() {
                @Override
                public void onClick(View arg0) {
                    Log.i("myBtn", "Clicked ");
                    // perform click / short press action
                }
            }
    );  
    myButton.setOnTouchListener(
            new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        Log.i("myBtn", "Btn Down");
                        v.performClick(); // call the above onClick handler now if appropriate
                        // Make this a repeating button, using MessageHandler
                        Message msg = new Message();
                        msg.what = MESSAGE_CHECK_BTN_STILL_PRESSED;
                        msg.arg1 = R.id.MyButton;
                        msg.arg2 = 250; // this btn's repeat time in ms
                        v.setTag(v); // mark btn as pressed (any non-null)
                        myGuiHandler.sendMessageDelayed(msg, msg.arg2);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        v.setTag(null); // mark btn as not pressed
                        break;
                    }
                    return true; // return true to prevent calling btn onClick handler
                }
            }
    );

Обработчик сообщений действия может быть:

public static final int MESSAGE_CHECK_BTN_STILL_PRESSED = 1;

public final Handler myGuiHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) { 
        case MESSAGE_CHECK_BTN_STILL_PRESSED: 
            Button btn = (Button) findViewById(msg.arg1);
            if (btn.getTag() != null) { // button is still pressed
                Log.i("myBtn", "still pressed");
                btn.performClick(); // perform Click or different long press action
                Message msg1 = new Message(); // schedule next btn pressed check
                msg1.copyFrom(msg);
                myGuiHandler.sendMessageDelayed(msg1, msg1.arg2);
            }
            break;
        } 
    }
};
...