web-dev-qa-db-fra.com

Comment puis-je obtenir un SeekBar vertical sous Android?

J'ai implémenté le message VerticalSeekBar communément pointé sur ici . En ce moment, la SeekBar fonctionne un peu bizarre. Voici mon onTouchEvent() légèrement adapté de cet exemple:

public boolean onTouchEvent(MotionEvent event)
    {
            xPos = event.getX();
            yPos = event.getY();
            oOffset = this.getThumbOffset();
            oProgress = this.getProgress();

            //Code from example - Not working
            //this.setThumbOffset( progress * (this.getBottom()-this.getTop()) );

            this.setProgress((int)(29*yPos/this.getBottom()));
            return true;
    }

J'ai réussi à implémenter un VerticalSeekBar dans lequel la progression est mise à jour comme prévu et est entièrement fonctionnelle, mais le pouce ne fait pas de même. Ceci est seulement un petit problème graphique, donc je le néglige pour le moment. Mais, il serait serait bien de le faire fonctionner. Cette SeekBar a max = 20.

Cependant, j'ai essayé d'implémenter un autre VerticalSeekBar avec max = 1000. Évidemment, il utilise le même code, vous assumerez donc le même comportement. Je ne peux réaliser qu'une progression de 0 à 35, même si mon doigt glisse au-delà de la barre SeekBar et éventuellement hors de l'écran. Si je tape juste près de la fin de la barre de progression (que devrait être progress ~ 900), il renvoie une progression d’environ 35 et la barre de progression jaune reflète cette valeur en restant près du haut.

Ma question est la suivante: quelqu'un a-t-il un lien vers une barre verticale de recherche en fonctionnement, ou sait-il comment adapter cet exemple particulier?

50
Snailer

Voici une implémentation de travail de VerticalSeekBar:

package Android.widget;

import Android.content.Context;
import Android.graphics.Canvas;
import Android.util.AttributeSet;
import Android.view.MotionEvent;

public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }
}

Pour l'implémenter, créez une nouvelle classe dans votre projet, en choisissant le bon package:

Là, collez le code et enregistrez-le. Maintenant, utilisez-le dans votre mise en page XML:

<Android.widget.VerticalSeekBar
  Android:id="@+id/seekBar1"
  Android:layout_width="wrap_content"
  Android:layout_height="200dp"
  />
140
Paul Tsupikoff

Le code donné dans la réponse acceptée n'interceptait pas les événements onStartTrackingTouch et onStopTrackingTouch. Je l'ai donc modifié pour mieux contrôler ces deux événements. 

Voici mon code: 

public class VerticalSeekBar extends SeekBar {

private OnSeekBarChangeListener myListener;
public VerticalSeekBar(Context context) {
    super(context);
}

public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public VerticalSeekBar(Context context, AttributeSet attrs) {
    super(context, attrs);
}

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(h, w, oldh, oldw);
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
    setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}

@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener){
    this.myListener = mListener;
}

protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(), 0);

    super.onDraw(c);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if(myListener!=null)
                myListener.onStartTrackingTouch(this);
            break;
        case MotionEvent.ACTION_MOVE:
            setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
            onSizeChanged(getWidth(), getHeight(), 0, 0);
            myListener.onProgressChanged(this, getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            break;
        case MotionEvent.ACTION_UP:
            myListener.onStopTrackingTouch(this);
            break;

        case MotionEvent.ACTION_CANCEL:
            break;
    }
    return true;
}
}
29
Fatal1ty2787

J'ai eu des problèmes en utilisant ce code avec la méthode setProgress. Pour les résoudre, je suggère de remplacer setProgress par un ajout d'appel onSizeChanged. 

12
Michał M

J'ai eu un problème lors de l'utilisation de ce code avec la méthode setProgress. Pour les résoudre, je suggère de remplacer setProgress par un ajout d’appel onSizeChanged.Ajoutez du code ici ..

   private int x,y,z,w;
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
        this.x=w;
        this.y=h;
        this.z=oldw;
        this.w=oldh;
    }
        @Override
        public synchronized void setProgress(int progress) {

            super.setProgress(progress);

                onSizeChanged(x, y, z, w);

        }

les actions de survol sélectionnées sont effectuées en ajoutant le code suivant:

1.setPressed (true); setSelected (true); // ajoute ceci dans ACTION_DOWN 

2.setPressed (false); setSelected (false); // Ajouter ceci dans ACTION_UP

Et rédigez le code pour les options de survol sélectionnées en ur xml.

<selector xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item Android:state_pressed="true"
          Android:state_window_focused="true"
          Android:drawable="@drawable/thumb_h" />
    <item Android:state_selected="true"
          Android:state_window_focused="true"
          Android:drawable="@drawable/thumb_h" />
    <item Android:drawable="@drawable/thumb" />
</selector>

Cela fonctionne pour moi...

11
Ramesh Akula

Pour API 11 and later, vous pouvez utiliser seekbar's XML attributes (Android:rotation="270") pour un effet vertical.

<SeekBar
Android:id="@+id/seekBar1"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:rotation="270"/>

Pour les anciens niveaux d’API (ex API10), utilisez: https://github.com/AndroSelva/Vertical-SeekBar-Android

7
Xar E Ahmer

Merci à Paul Tsupikoff, Fatal1ty2787 et Ramesh pour cet excellent code.

Personnellement, je voulais un curseur vertical qui est à l'envers par rapport au code donné. En d'autres termes, plus le pouce est bas, plus sa valeur augmente et non pas sa diminution. Changer quatre lignes semble avoir pris soin de cela.

D'abord, j'ai changé la méthode onDraw() telle qu'elle a été donnée à l'origine par Paul Les appels rotate() et translate() ont maintenant ces arguments:

c.rotate(90);
c.translate(0, -getWidth());

Ensuite, j'ai apporté deux modifications au cas ACTION_MOVE dans onTouchEvent() donné par Fatal1ty2787. L'appel à setProgress() ressemble maintenant à ceci:

setProgress((int) (getMax() * event.getY() / getHeight()));

Enfin, l'appel à onProgressChanged() se présente comme suit:

myListener.onProgressChanged(this, (int) (getMax() * event.getY() / getHeight()), true);

Maintenant, si seulement Google partageait notre intérêt pour cette fonctionnalité ....

6
Adam Mackler

Une autre idée pourrait être de changer les coordonnées X et Y de MotionEvent et de les transmettre à la super-implémentation:

public class VerticalSeekBar extends SeekBar {

public VerticalSeekBar(Context context) {
    super(context);
}

public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public VerticalSeekBar(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
        return false;
    }
    float x = (getHeight() - event.getY()) * getWidth() / getHeight();
    float y = event.getX();
    MotionEvent verticalEvent = MotionEvent
            .obtain(event.getDownTime(), event.getEventTime(), event.getAction(), x, y,
                    event.getPressure(), event.getSize(), event.getMetaState(),
                    event.getYPrecision(), event.getXPrecision(), event.getDeviceId(),
                    event.getEdgeFlags());
    return super.onTouchEvent(verticalEvent);
}

protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(), 0);
    super.onDraw(c);
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(heightMeasureSpec, widthMeasureSpec);
    setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(h, w, oldh, oldw);
}
}

Dans ce cas, il n'est pas nécessaire d'appeler la méthode setProgress (int) et vous pouvez donc utiliser l'indicateur booléen "fromUser" dans OnSeekBarChangeListener.onProgressChanged () pour déterminer si la recherche a été produite par une interaction utilisateur.

2
Christopher

Plateformes cibles

à partir d'Android 2.3.x (Pain d'épice) à Android 7.x (Nougat)

Commencer

Cette bibliothèque est publiée sur jCenter. Ajoutez simplement ces lignes à build.gradle.

dependencies {
    compile 'com.h6ah4i.Android.widget.verticalseekbar:verticalseekbar:0.7.2'
}

Utilisation

Mise en page XML

<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.Android.widget.verticalseekbar.VerticalSeekBarWrapper
    Android:layout_width="wrap_content"
    Android:layout_height="150dp">
    <com.h6ah4i.Android.widget.verticalseekbar.VerticalSeekBar
        Android:id="@+id/mySeekBar"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:max="100"
        Android:progress="0"
        Android:splitTrack="false"
        app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.Android.widget.verticalseekbar.VerticalSeekBarWrapper>

REMARQUE: Android:splitTrack="false" est requis pour Android N +.

Code Java

public class TestVerticalSeekbar extends AppCompatActivity {
    private SeekBar volumeControl = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_vertical_seekbar);

        volumeControl = (SeekBar) findViewById(R.id.mySeekBar);

        volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            int progressChanged = 0;

            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressChanged = progress;
            }

            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            public void onStopTrackingTouch(SeekBar seekBar) {
                Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

}
0
Phadadev