Wie erstelle ich eine vertikale SeekBar in Android?
74
Kann ein SeekBarvertikal sein? Ich bin nicht sehr gut im UI-Design. Wie kann ich das SeekBarschöner machen ? Bitte geben Sie mir einige Vorlagen und Beispiele.
Sie da. Ich habe den Code ausprobiert, den Sie als .zip gepostet haben, und er funktioniert hervorragend. Bis auf ein Problem: Die SeekBar zeichnet ihren Fortschritt immer noch von links nach rechts und nicht von unten nach oben. Ich habe nicht gefunden, wo es codiert ist. Hast du irgendwelche Vorschläge für mich? thx
Droidman
2
Hallo! Liebe deine vertikaleScrollbar Umgekehrt habe ich versucht, eine zu erstellen, hatte aber große Schwierigkeiten, die richtige Rotation und Übersetzung zu finden. Es gibt einen kleinen Fehler, den ich sowieso gefunden habe: Wenn MAX nicht 100 ist, wird der Fortschritt nicht korrekt berechnet. Um dies zu beheben, müssen Sie (100-i) in (getMax () - i) ändern. Auf diese Weise funktioniert es perfekt für jeden Bereich.
Rupps
anscheinend kann ich auch hier negativ und größer als max sein, also habe ich es geändert inMath.max(0, Math.min(getMax(), i)
Matthias
1
@AndroSelva seekbar.setprogress (Position) bewegt den vertikalen Suchleisten-Daumen nicht in die gewünschte Position. .... warum so?
Animesh Mangla
THUMB FUNKTIONIERT NICHT
user25
74
Für API 11 und höher können die XML-Attribute von seekbar (android: rotation = "270") für vertikale Effekte verwendet werden.
@ Nikhil , Selvas Antwort ist die, die der Fragesteller oben akzeptiert hat. Ich denke, Selvas eigenes Gerät ist einfach und gut!
Saturn
18
Lösung 1 - gedrehte SeekBar funktioniert nicht richtig. Der Daumen ist weggezogen, Größe und Position können nicht richtig eingestellt werden (zumindest innerhalb von RelativeLayout).
Zeiger Null
2
Es funktioniert jedoch, Sie müssen bedenken, dass die Abmessungen ausgetauscht werden, sodass aus der Breite die Höhe und so weiter wird. Wenn Sie also die Höhe erhöhen, dehnen Sie sie tatsächlich, weshalb der Daumen weggezogen wird, und dies geschieht sogar mit einer normalen Suchleiste, wenn Sie die Breite gedehnt haben. Das Drehen ist ärgerlich, daher finde ich es einfacher, wenn Sie die Suchleiste in einem Layout platzieren und die richtige Größe festlegen. Drehen Sie dann das Layout und passen Sie die Größe an Ihr Hauptlayout an.
Fahad Alduraibi
4
Ah, ich habe dich zu schnell gestimmt. Das funktioniert kaum. Der Schieberegler verwendet intern die Breite / Höhe vor dem Drehen, sodass alles ungünstig positioniert und dimensioniert ist.
b005t3r
24
Arbeitsbeispiel
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
publicclassVerticalSeekBarextendsSeekBar{
publicVerticalSeekBar(Context context){
super(context);
}
publicVerticalSeekBar(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
publicVerticalSeekBar(Context context, AttributeSet attrs){
super(context, attrs);
}
protectedvoidonSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(h, w, oldh, oldw);
}
@OverridepublicsynchronizedvoidsetProgress(int progress)// it is necessary for calling setProgress on click of a button{
super.setProgress(progress);
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
@OverrideprotectedsynchronizedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protectedvoidonDraw(Canvas c){
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
@OverridepublicbooleanonTouchEvent(MotionEvent event){
if (!isEnabled()) {
returnfalse;
}
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;
}
returntrue;
}
}
Fügen Sie dort den Code ein und speichern Sie ihn. Verwenden Sie es jetzt in Ihrem XML-Layout:
Dies gibt keinen Daumen, sondern nur den Schieberegler
Fahid Nadeem
versuche deinen eigenen daumen mit android zu setzen: thumb = "@ drawable / thumb_image". Aber ich stehe dem Problem nicht gegenüber. stackoverflow.com/a/18559778/3496570
AndroidGeek
Ich habe auch ein Problem mit dem Daumen mit dieser Lösung. Es wird angezeigt, aber nicht animiert (seine Größe nimmt nicht zu), wenn es wie gewohnt bewegt wird.
Tim Autin
@XarEAhmer, wissen Sie, wie ich bidirektionale Sprachen unterstützen kann, Arabicda diese auf den Vertical SeekBarKopf gestellt wurden und der Schieberegler eine falsche Fortschrittsrichtung angibt?
@ Mahmoud Durch Hinzufügen von Android: Rotation = "270"
Ali AlNoaimi
6
Durch Einstellen der Drehung wird der Schieberegler gedreht, nicht jedoch das Layout. layout_width steuert weiterhin die Länge des Schiebereglers. Daher denken die Layouts immer noch, dass es sich um Breite und nicht um Höhe handelt. So macht es Spaß, richtig in ein Design einzufügen. Ich habe immer noch nicht herausgefunden, wie man eine gedrehte Suchleiste in ein GridLayout einfügt.
JT.
10
Ich habe Selvas Lösung verwendet, hatte aber zwei Arten von Problemen:
OnSeekbarChangeListener funktionierte nicht richtig
Das programmgesteuerte Einstellen des Fortschritts funktionierte nicht richtig.
Ich habe diese beiden Probleme behoben. Die Lösung (in meinem eigenen Projektpaket) finden Sie unter
Funktioniert gut, danke. Sie sollten auch eine Nullprüfung für die Variable mOnSeekBarChangeListener hinzufügen.
Jekatt
@ JörgEisfeld Daumen setzen und FortschrittDrawab le für mich fehlgeschlagen
noobEinstien
@ JörgEisfeld, Weißt du, wie ich bidirektionale Sprachen unterstützen kann, Arabicda diese auf den Vertical SeekBarKopf gestellt wurden und der Schieberegler eine falsche Fortschrittsrichtung angibt?
Blueware
10
Wir haben eine vertikale SeekBar erstellt mit android:rotation="270":
Der Trick besteht darin, die Höhe einzustellen. Andernfalls verwendet das Framelayout die Breite des Suchers vor der Rotation
ShellDude
6
Beachten Sie, dass sich die Daumenbreite beim Ändern der Breite nicht richtig ändert. Ich habe mir nicht die Zeit genommen, es richtig zu reparieren, ich habe es nur für meinen Fall repariert. Hier ist was ich getan habe. Konnte nicht herausfinden, wie man den ursprünglichen Schöpfer kontaktiert.
publicvoidsetThumb(Drawable thumb) {
if (thumb != null) {
thumb.setCallback(this);
// Assuming the thumb drawable is symmetric, set the thumb offset// such that the thumb will hang halfway off either edge of the// progress bar.//This was orginally divided by 2, seems you have to adjust here when you adjust width.
mThumbOffset = (int)thumb.getIntrinsicHeight();
}
<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes --><com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapperandroid:layout_width="wrap_content"android:layout_height="150dp"><com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarandroid: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>
HINWEIS: android:splitTrack="false"ist für Android N + erforderlich.
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
/**
* Implementation of an easy vertical SeekBar, based on the normal SeekBar.
*/publicclassVerticalSeekBarextendsSeekBar{
/**
* The angle by which the SeekBar view should be rotated.
*/privatestaticfinalint ROTATION_ANGLE = -90;
/**
* A change listener registrating start and stop of tracking. Need an own listener because the listener in SeekBar
* is private.
*/private OnSeekBarChangeListener mOnSeekBarChangeListener;
/**
* Standard constructor to be implemented for all views.
*
* @param context The Context the view is running in, through which it can access the current theme, resources, etc.
* @see android.view.View#View(Context)
*/publicVerticalSeekBar(final Context context){
super(context);
}
/**
* Standard constructor to be implemented for all views.
*
* @param context The Context the view is running in, through which it can access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @see android.view.View#View(Context, AttributeSet)
*/publicVerticalSeekBar(final Context context, final AttributeSet attrs){
super(context, attrs);
}
/**
* Standard constructor to be implemented for all views.
*
* @param context The Context the view is running in, through which it can access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyle An attribute in the current theme that contains a reference to a style resource that supplies default
* values for the view. Can be 0 to not look for defaults.
* @see android.view.View#View(Context, AttributeSet, int)
*/publicVerticalSeekBar(final Context context, final AttributeSet attrs, finalint defStyle){
super(context, attrs, defStyle);
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverrideprotectedfinalvoidonSizeChanged(finalint width, finalint height, finalint oldWidth, finalint oldHeight){
super.onSizeChanged(height, width, oldHeight, oldWidth);
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverrideprotectedfinalsynchronizedvoidonMeasure(finalint widthMeasureSpec, finalint heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverrideprotectedfinalvoidonDraw(@NonNullfinal Canvas c){
c.rotate(ROTATION_ANGLE);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverridepublicfinalvoidsetOnSeekBarChangeListener(final OnSeekBarChangeListener listener){
// Do not use super for the listener, as this would not set the fromUser flag properly
mOnSeekBarChangeListener = listener;
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverridepublicfinalbooleanonTouchEvent(@NonNullfinal MotionEvent event){
if (!isEnabled()) {
returnfalse;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
break;
case MotionEvent.ACTION_MOVE:
setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
break;
case MotionEvent.ACTION_UP:
setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
break;
case MotionEvent.ACTION_CANCEL:
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
break;
default:
break;
}
returntrue;
}
/**
* Set the progress by the user. (Unfortunately, Seekbar.setProgressInternally(int, boolean) is not accessible.)
*
* @param progress the progress.
* @param fromUser flag indicating if the change was done by the user.
*/publicfinalvoidsetProgressInternally(finalint progress, finalboolean fromUser){
if (progress != getProgress()) {
super.setProgress(progress);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
}
}
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
/*
* (non-Javadoc) ${see_to_overridden}
*/@OverridepublicfinalvoidsetProgress(finalint progress){
setProgressInternally(progress, false);
}
}
Dieser Ansatz weist einen Fehler auf. Der gemeldete progressWert kann kleiner als der Min- oder Maximalwert werden, wenn der Benutzer den Daumen weiterhin außerhalb der Grenzen der Ansicht zieht.
Roman Samoilenko
1
Ich habe es auf viele verschiedene Arten versucht, aber die, die für mich funktioniert hat, war. Verwenden Sie Seekbar in FrameLayout
Rückruf vor dem Zeichnen in der Suchleiste einrichten, wo Sie die Breite und Höhe der Suchleiste ändern können Ich habe diesen Teil in c # ausgeführt, also war der von mir verwendete Code
var volumeSlider = view.FindViewById<SeekBar>(Resource.Id.home_link_volume);
var volumeFrameLayout = view.FindViewById<FrameLayout>(Resource.Id.linkVolumeFrameLayout);
voidOnPreDrawVolume(object sender, ViewTreeObserver.PreDrawEventArgs e)
{
volumeSlider.ViewTreeObserver.PreDraw -= OnPreDrawVolume;
var h = volumeFrameLayout.Height;
volumeSlider.Rotation = 270.0f;
volumeSlider.LayoutParameters.Width = h;
volumeSlider.RequestLayout();
}
volumeSlider.ViewTreeObserver.PreDraw += OnPreDrawVolume;
Hier füge ich Listener zum PreDraw-Ereignis hinzu und wenn es ausgelöst wird, entferne ich das PreDraw, damit es nicht in die Endlosschleife geht.
Wenn Pre Draw ausgeführt wird, rufe ich die Höhe von FrameLayout ab und weise sie Seekbar zu. Und setzen Sie die Drehung der Suchleiste auf 270. Da sich meine Suchleiste innerhalb des Rahmenlayouts befindet und ihre Schwerkraft als Mitte festgelegt ist. Ich brauche mir keine Sorgen um die Übersetzung zu machen. Da Seekbar immer in der Mitte des Rahmenlayouts bleibt.
Der Grund, warum ich EventHandler entferne, ist, dass seekbar.RequestLayout (); Lässt dieses Ereignis erneut ausgeführt werden.
Antworten:
Hier ist eine sehr gute Implementierung der vertikalen Suchleiste. Guck mal.
http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip
Und hier ist meine eigene Implementierung für Vertikale und umgekehrt SeekBar basierend auf dieser
https://github.com/AndroSelva/Vertical-SeekBar-Android
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: int i=0; i=getMax() - (int) (getMax() * event.getY() / getHeight()); setProgress(i); Log.i("Progress",getProgress()+""); onSizeChanged(getWidth(), getHeight(), 0, 0); break; case MotionEvent.ACTION_CANCEL: break; } return true; }
quelle
Math.max(0, Math.min(getMax(), i)
Für API 11 und höher können die XML-Attribute von seekbar (android: rotation = "270") für vertikale Effekte verwendet werden.
<SeekBar android:id="@+id/seekBar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:rotation="270"/>
Verwenden Sie für ältere API-Level (ex API10) nur die Antwort von Selva:
https://github.com/AndroSelva/Vertical-SeekBar-Android
quelle
Arbeitsbeispiel
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 public synchronized void setProgress(int progress) // it is necessary for calling setProgress on click of a button { super.setProgress(progress); onSizeChanged(getWidth(), getHeight(), 0, 0); } @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; } }
Fügen Sie dort den Code ein und speichern Sie ihn. Verwenden Sie es jetzt in Ihrem XML-Layout:
<android.widget.VerticalSeekBar android:id="@+id/seekBar1" android:layout_width="wrap_content" android:layout_height="200dp" />
Stellen Sie sicher, dass Sie ein Paket
android.widget
erstellen undVerticalSeekBar.java
unter diesem Paket erstellenquelle
Arabic
da diese auf denVertical SeekBar
Kopf gestellt wurden und der Schieberegler eine falsche Fortschrittsrichtung angibt?Versuchen:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <SeekBar android:id="@+id/seekBar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:rotation="270" /> </RelativeLayout>
quelle
Ich habe Selvas Lösung verwendet, hatte aber zwei Arten von Problemen:
Ich habe diese beiden Probleme behoben. Die Lösung (in meinem eigenen Projektpaket) finden Sie unter
https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/components/VerticalSeekBar.java
quelle
Arabic
da diese auf denVertical SeekBar
Kopf gestellt wurden und der Schieberegler eine falsche Fortschrittsrichtung angibt?Wir haben eine vertikale SeekBar erstellt mit
android:rotation="270"
:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <SurfaceView android:id="@+id/camera_sv_preview" android:layout_width="match_parent" android:layout_height="match_parent"/> <LinearLayout android:id="@+id/camera_lv_expose" android:layout_width="32dp" android:layout_height="200dp" android:layout_centerVertical="true" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:orientation="vertical"> <TextView android:id="@+id/camera_tv_expose" android:layout_width="32dp" android:layout_height="20dp" android:textColor="#FFFFFF" android:textSize="15sp" android:gravity="center"/> <FrameLayout android:layout_width="32dp" android:layout_height="180dp" android:orientation="vertical"> <SeekBar android:id="@+id/camera_sb_expose" android:layout_width="180dp" android:layout_height="32dp" android:layout_gravity="center" android:rotation="270"/> </FrameLayout> </LinearLayout> <TextView android:id="@+id/camera_tv_help" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:layout_marginBottom="20dp" android:text="@string/camera_tv" android:textColor="#FFFFFF" /> </RelativeLayout>
Screenshot zur Kompensation der Kamera:
quelle
Das hat bei mir funktioniert, fügen Sie es einfach in ein beliebiges Layout ein.
<FrameLayout android:layout_width="32dp" android:layout_height="192dp"> <SeekBar android:layout_width="192dp" android:layout_height="32dp" android:layout_gravity="center" android:rotation="270" /> </FrameLayout>
quelle
Beachten Sie, dass sich die Daumenbreite beim Ändern der Breite nicht richtig ändert. Ich habe mir nicht die Zeit genommen, es richtig zu reparieren, ich habe es nur für meinen Fall repariert. Hier ist was ich getan habe. Konnte nicht herausfinden, wie man den ursprünglichen Schöpfer kontaktiert.
public void setThumb(Drawable thumb) { if (thumb != null) { thumb.setCallback(this); // Assuming the thumb drawable is symmetric, set the thumb offset // such that the thumb will hang halfway off either edge of the // progress bar. //This was orginally divided by 2, seems you have to adjust here when you adjust width. mThumbOffset = (int)thumb.getIntrinsicHeight(); }
quelle
Wickeln Sie es in ein FrameLayout, damit es keine Größenprobleme gibt.
<FrameLayout android:layout_width="@dimen/_20dp" android:layout_marginStart="@dimen/_15dp" android:layout_marginEnd="@dimen/_15dp" android:layout_height="match_parent" android:orientation="vertical"> <SeekBar android:layout_width="150dp" android:layout_height="30dp" android:layout_gravity="center" android:rotation="270" /> </FrameLayout>
quelle
Wenn Sie den Daumen mit einem EditText bewegen, funktioniert die vertikale Suchleiste setProgress möglicherweise nicht. Der folgende Code kann helfen:
@Override public synchronized void setProgress(int progress) { super.setProgress(progress); updateThumb(); } private void updateThumb() { onSizeChanged(getWidth(), getHeight(), 0, 0); }
Diesen Snippet-Code finden Sie hier: https://stackoverflow.com/a/33064140/2447726
quelle
Beginnen
Fügen Sie diese Zeilen zu build.gradle hinzu.
dependencies { compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2' }
Verwendung
Java-Code
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(); } }); } }
Layout 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>
HINWEIS:
android:splitTrack="false"
ist für Android N + erforderlich.quelle
Versuche dies
import android.content.Context; import android.graphics.Canvas; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.SeekBar; /** * Implementation of an easy vertical SeekBar, based on the normal SeekBar. */ public class VerticalSeekBar extends SeekBar { /** * The angle by which the SeekBar view should be rotated. */ private static final int ROTATION_ANGLE = -90; /** * A change listener registrating start and stop of tracking. Need an own listener because the listener in SeekBar * is private. */ private OnSeekBarChangeListener mOnSeekBarChangeListener; /** * Standard constructor to be implemented for all views. * * @param context The Context the view is running in, through which it can access the current theme, resources, etc. * @see android.view.View#View(Context) */ public VerticalSeekBar(final Context context) { super(context); } /** * Standard constructor to be implemented for all views. * * @param context The Context the view is running in, through which it can access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @see android.view.View#View(Context, AttributeSet) */ public VerticalSeekBar(final Context context, final AttributeSet attrs) { super(context, attrs); } /** * Standard constructor to be implemented for all views. * * @param context The Context the view is running in, through which it can access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @param defStyle An attribute in the current theme that contains a reference to a style resource that supplies default * values for the view. Can be 0 to not look for defaults. * @see android.view.View#View(Context, AttributeSet, int) */ public VerticalSeekBar(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); } /* * (non-Javadoc) ${see_to_overridden} */ @Override protected final void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) { super.onSizeChanged(height, width, oldHeight, oldWidth); } /* * (non-Javadoc) ${see_to_overridden} */ @Override protected final synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(heightMeasureSpec, widthMeasureSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); } /* * (non-Javadoc) ${see_to_overridden} */ @Override protected final void onDraw(@NonNull final Canvas c) { c.rotate(ROTATION_ANGLE); c.translate(-getHeight(), 0); super.onDraw(c); } /* * (non-Javadoc) ${see_to_overridden} */ @Override public final void setOnSeekBarChangeListener(final OnSeekBarChangeListener listener) { // Do not use super for the listener, as this would not set the fromUser flag properly mOnSeekBarChangeListener = listener; } /* * (non-Javadoc) ${see_to_overridden} */ @Override public final boolean onTouchEvent(@NonNull final MotionEvent event) { if (!isEnabled()) { return false; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true); if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onStartTrackingTouch(this); } break; case MotionEvent.ACTION_MOVE: setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true); break; case MotionEvent.ACTION_UP: setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true); if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onStopTrackingTouch(this); } break; case MotionEvent.ACTION_CANCEL: if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onStopTrackingTouch(this); } break; default: break; } return true; } /** * Set the progress by the user. (Unfortunately, Seekbar.setProgressInternally(int, boolean) is not accessible.) * * @param progress the progress. * @param fromUser flag indicating if the change was done by the user. */ public final void setProgressInternally(final int progress, final boolean fromUser) { if (progress != getProgress()) { super.setProgress(progress); if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser); } } onSizeChanged(getWidth(), getHeight(), 0, 0); } /* * (non-Javadoc) ${see_to_overridden} */ @Override public final void setProgress(final int progress) { setProgressInternally(progress, false); } }
quelle
progress
Wert kann kleiner als der Min- oder Maximalwert werden, wenn der Benutzer den Daumen weiterhin außerhalb der Grenzen der Ansicht zieht.Ich habe es auf viele verschiedene Arten versucht, aber die, die für mich funktioniert hat, war. Verwenden Sie Seekbar in FrameLayout
<FrameLayout android:id="@+id/VolumeLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/MuteButton" android:layout_below="@id/volumeText" android:layout_centerInParent="true"> <SeekBar android:id="@+id/volume" android:layout_width="500dp" android:layout_height="60dp" android:layout_gravity="center" android:progress="50" android:secondaryProgress="40" android:progressDrawable="@drawable/seekbar_volume" android:secondaryProgressTint="@color/tint_neutral" android:thumbTint="@color/tint_neutral" />
Und im Code.
Rückruf vor dem Zeichnen in der Suchleiste einrichten, wo Sie die Breite und Höhe der Suchleiste ändern können Ich habe diesen Teil in c # ausgeführt, also war der von mir verwendete Code
var volumeSlider = view.FindViewById<SeekBar>(Resource.Id.home_link_volume); var volumeFrameLayout = view.FindViewById<FrameLayout>(Resource.Id.linkVolumeFrameLayout); void OnPreDrawVolume(object sender, ViewTreeObserver.PreDrawEventArgs e) { volumeSlider.ViewTreeObserver.PreDraw -= OnPreDrawVolume; var h = volumeFrameLayout.Height; volumeSlider.Rotation = 270.0f; volumeSlider.LayoutParameters.Width = h; volumeSlider.RequestLayout(); } volumeSlider.ViewTreeObserver.PreDraw += OnPreDrawVolume;
Hier füge ich Listener zum PreDraw-Ereignis hinzu und wenn es ausgelöst wird, entferne ich das PreDraw, damit es nicht in die Endlosschleife geht.
Wenn Pre Draw ausgeführt wird, rufe ich die Höhe von FrameLayout ab und weise sie Seekbar zu. Und setzen Sie die Drehung der Suchleiste auf 270. Da sich meine Suchleiste innerhalb des Rahmenlayouts befindet und ihre Schwerkraft als Mitte festgelegt ist. Ich brauche mir keine Sorgen um die Übersetzung zu machen. Da Seekbar immer in der Mitte des Rahmenlayouts bleibt.
Der Grund, warum ich EventHandler entferne, ist, dass seekbar.RequestLayout (); Lässt dieses Ereignis erneut ausgeführt werden.
quelle
Sie können es selbst tun - es ist jetzt so schwierig. Hier ist ein Beispiel aus meinem Projekt: https://github.com/AlShevelev/WizardCamera
Beginnen wir mit den Einstellungen (attrs.xml).
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ExpositionBar"> <attr name="button_icon" format="reference" /> <attr name="button_icon_size" format="dimension" /> <attr name="stroke_width" format="dimension" /> <attr name="stroke_color" format="color" /> <attr name="button_color" format="color" /> <attr name="button_color_pressed" format="color" /> <attr name="min_value" format="float" /> <attr name="max_value" format="float" /> </declare-styleable> </resources>
Hier sind einige Dienstprogrammfunktionen:
fun <T: Comparable<T>>T.fitInRange(range: Range<T>): T = when { this < range.lower -> range.lower this > range.upper -> range.upper else -> this } fun Float.reduceToRange(rangeFrom: Range<Float>, rangeTo: Range<Float>): Float = when { this == rangeFrom.lower -> rangeTo.lower this == rangeFrom.upper -> rangeTo.upper else -> { val placeInRange = (this - rangeFrom.lower) / (rangeFrom.upper - rangeFrom.lower) ((rangeTo.upper - rangeTo.lower) * placeInRange) + rangeTo.lower } }
Und zu guter Letzt - eine Klasse für vertikale Suchleisten:
class ExpositionBar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val drawingRect = RectF(0f, 0f, 0f, 0f) private val drawingPaint = Paint(Paint.ANTI_ALIAS_FLAG) private val strokeWidth: Float @ColorInt private val strokeColor: Int @ColorInt private val buttonFillColor: Int @ColorInt private val buttonFillColorPressed: Int private val icon: VectorDrawable private val valuesRange: Range<Float> private var centerX = 0f private var minY = 0f private var maxY = 0f private var buttonCenterY = 0f private var buttonRadiusExt = 0f private var buttonRadiusInt = 0f private var buttonMinY = 0f private var buttonMaxY = 0f private var buttonCenterBoundsRange = Range(0f, 0f) private var iconTranslationX = 0f private var iconTranslationY = 0f private var isInDragMode = false private var onValueChangeListener: ((Float) -> Unit)? = null private var oldOutputValue = Float.MIN_VALUE init { val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpositionBar) icon = typedArray.getDrawable(R.styleable.ExpositionBar_button_icon) as VectorDrawable val iconSize = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_button_icon_size, 0) icon.setBounds(0, 0, iconSize, iconSize) strokeWidth = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_stroke_width, 0).toFloat() drawingPaint.strokeWidth = strokeWidth strokeColor = typedArray.getColor(R.styleable.ExpositionBar_stroke_color, Color.WHITE) buttonFillColor = typedArray.getColor(R.styleable.ExpositionBar_button_color, Color.BLACK) buttonFillColorPressed = typedArray.getColor(R.styleable.ExpositionBar_button_color_pressed, Color.BLUE) val minValue = typedArray.getFloat(R.styleable.ExpositionBar_min_value, 0f) val maxValue = typedArray.getFloat(R.styleable.ExpositionBar_max_value, 0f) valuesRange = Range(minValue, maxValue) typedArray.recycle() } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) drawingRect.right = width.toFloat() drawingRect.bottom = height.toFloat() buttonCenterY = drawingRect.centerY() recalculateDrawingValues() } override fun onDraw(canvas: Canvas) { drawingPaint.color = strokeColor drawingPaint.style = Paint.Style.STROKE // Draw the center line canvas.drawLine(centerX, minY, centerX, buttonMinY, drawingPaint) canvas.drawLine(centerX, buttonMaxY, centerX, maxY, drawingPaint) // Draw the button canvas.drawCircle(centerX, buttonCenterY, buttonRadiusExt, drawingPaint) drawingPaint.style = Paint.Style.FILL drawingPaint.color = if(isInDragMode) buttonFillColorPressed else buttonFillColor canvas.drawCircle(centerX, buttonCenterY, buttonRadiusInt, drawingPaint) // Draw button icon canvas.translate(iconTranslationX, iconTranslationY) icon.draw(canvas) canvas.translate(-iconTranslationX, -iconTranslationY) } @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean { if(!isEnabled) { return false } when(event.actionMasked) { MotionEvent.ACTION_DOWN -> { if(isButtonHit(event.y)){ isInDragMode = true invalidate() } } MotionEvent.ACTION_MOVE -> { if(isInDragMode) { buttonCenterY = event.y.fitInRange(buttonCenterBoundsRange) recalculateDrawingValues() invalidate() val outputValue = buttonCenterY.reduceToRange(buttonCenterBoundsRange, valuesRange) if (outputValue != oldOutputValue) { onValueChangeListener?.invoke(outputValue) oldOutputValue = outputValue } } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { isInDragMode = false invalidate() } } return true } fun setOnValueChangeListener(listener: ((Float) -> Unit)?) { onValueChangeListener = listener } private fun recalculateDrawingValues() { centerX = drawingRect.left + drawingRect.width()/2 minY = drawingRect.top maxY = drawingRect.bottom buttonRadiusExt = drawingRect.width() / 2 - strokeWidth / 2 buttonRadiusInt = buttonRadiusExt - strokeWidth / 2 buttonMinY = buttonCenterY - buttonRadiusExt buttonMaxY = buttonCenterY + buttonRadiusExt val buttonCenterMinY = minY + buttonRadiusExt + strokeWidth / 2 val buttonCenterMaxY = maxY - buttonRadiusExt - strokeWidth / 2 buttonCenterBoundsRange = Range(buttonCenterMinY, buttonCenterMaxY) iconTranslationX = centerX - icon.bounds.width() / 2 iconTranslationY = buttonCenterY - icon.bounds.height() / 2 } private fun isButtonHit(y: Float): Boolean { return y >= buttonMinY && y <= buttonMaxY } }
Sie können es wie hier gezeigt verwenden:
<com.shevelev.wizard_camera.main_activity.view.widgets.ExpositionBar android:id="@+id/expositionBar" android:layout_width="@dimen/mainButtonSize" android:layout_height="300dp" android:layout_gravity="end|center_vertical" android:layout_marginEnd="@dimen/marginNormal" android:layout_marginBottom="26dp" app:button_icon = "@drawable/ic_brightness" app:button_icon_size = "@dimen/toolButtonIconSize" app:stroke_width = "@dimen/strokeWidthNormal" app:stroke_color = "@color/mainButtonsForeground" app:button_color = "@color/mainButtonsBackground" app:button_color_pressed = "@color/mainButtonsBackgroundPressed" app:min_value="-100" app:max_value="100" />
Voila!
quelle
In meinem Fall habe ich eine normale Suchleiste verwendet und das Layout einfach ausgeklappt.
seekbark_layout.xml - mein Layout, das die Suchleiste enthält, die wir vertikal machen müssen.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rootView" android:layout_width="match_parent" android:layout_height="match_parent"> <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true"/> </RelativeLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.vgfit.seekbarexample.MainActivity"> <View android:id="@+id/headerView" android:layout_width="match_parent" android:layout_height="100dp" android:background="@color/colorAccent"/> <View android:id="@+id/bottomView" android:layout_width="match_parent" android:layout_height="100dp" android:layout_alignParentBottom="true" android:background="@color/colorAccent"/> <include layout="@layout/seekbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/bottomView" android:layout_below="@id/headerView"/> </RelativeLayout>
Und in MainActivity drehe ich seekbar_layout:
import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.RelativeLayout import kotlinx.android.synthetic.main.seekbar_layout.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) rootView.post { val w = rootView.width val h = rootView.height rootView.rotation = 270.0f rootView.translationX = ((w - h) / 2).toFloat() rootView.translationY = ((h - w) / 2).toFloat() val lp = rootView.layoutParams as RelativeLayout.LayoutParams lp.height = w lp.width = h rootView.requestLayout() } } }
Als Ergebnis haben wir die notwendige vertikale Suchleiste:
quelle