Slidery zależne od siebie

0

Potrzebuję zrobić 3 slidery, w których suma wartości ma być zawsze taka sama, np 100. Przykład takiego czegoś jest na stronie humble bundle. Chciałbym wiedzieć jak uzupełnić funkcję stateChanged, aby to uzyskać.

0

Musisz sprawdzić, który właśnie suwak się zmienił w listenerze, sprawdzić o ile się zmienił, jeśli np. o 40 się zwiększył, to pozostałe suwaki zmniejszasz o 20.

0

To nie jest takie proste. Jeśli suma wartości ma być stała, to musisz przesuwać programowo pozostałe suwaki w trakcie przesuwania ręcznego. Ale te suwaki przesuwane ręcznie też mają listenery.
Druga sprawa, zmniejszenie obu o 20 moze być niemozliwe, bo jeden z nich ma wartość 0 5 większą od jego wartości minimalnej.

0

Co za problem stworzyć metodę uruchamianą przez listenery, przekazujemy jej eventArg, który zawiera co i o ile się przesunęło. Metoda wtedy za pomocą ifów etc. sprawdzi wcześniej stan innych suwaków i na tej podstawie, odpowiednio zmniejszy/zwiększy ich wartość. Jeśli suwak ma już minimalną wartość jakiś to wcześniej to sprawdzamy i zmniejszamy o całą wartość ten trzeci.

Ważne żeby nie gubić części ułamkowych po dzieleniu.

0

Ale te przesuwane programowo suwaki też wygenerują zdarzenie.

0

Po to listenery mają fajną możliwość ich podłączania i odłączania, by z tego skorzystać w odpowiedniej chwili, kiedy nie chcemy reagować na zmiany.

0

Mam takie coś:

 
slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				
				JSlider source = (JSlider)e.getSource();
				
				slider_1.setValue(100 - source.getValue() - slider_2.getValue());
				slider_2.setValue(100 - source.getValue() - slider_1.getValue());
			}
		});

i analogicznie w pozostałych dwóch sliderach. Niby działa, ale na przesuwany ręcznie slider reaguje tylko 1 i to średnio wygląda. Ten ostatni zmienia dopiero wartość jeśli przekroczona miała by być ta stała suma dla wszystkich, czyli 100, tzn kiedy ten poprzedni jest już ustawiony na 0 lub 100.

0

No to działa dokładnie tak jak zaprogramowałeś. Jeśli chcesz by oba naraz się przesuwały to:

  1. Najpierw oblicz różnicę sumy, tzn difference = 100 - (slider1.value() + slider2.value() + slider3.value());
  2. Następnie podziel tą wartość na dwa składniki (niekoniecznie różne), np difference1 = difference / 3; difference2 = difference - difference1, przy czym musisz zadbać o to by końcowe wartości nie wyleciały poza zakres [0, 100].
  3. Nastepnie dodaj difference1 do slider1 i difference2 do slider2.
0

Heh, wszystko fajnie przy pierwszym sliderze, ale gdy już edytuję pozostałe, to albo wariują z wartościami, albo się blokują. Problem nie jest jakiś nietypowy, ale nie mogę znaleźć jakiegoś przykładu.

0

@m4tius Może nie odłączasz listenerów przed wywoływaniem set, o czym pisaliśmy wyżej?

0

Mam użyć removeChangeListener?

0

@m4tius Tak, użyj remowe na suwakach, dla których robisz set, później przyłącz listenery z powrotem. Najlepiej jakbyś nie robił anonimowych klas w tym wypadku, ale jak lubisz.

2

Ewentualnie możesz przekazać do wszystkich listenerów jeden i ten sam AtomicBoolean i zrobić metodę stateChanged w tym stylu:

if (atomicBoolean.compareAndSet(false, true)) {
  // ustawiamy setValue, w tym czasie odpalają się metody changeEvent w innych listenerach, ale tam mamy takiego samego ifa
  .....

  atomicBoolean.set(false);
}

\

Uwaga: we wszystkich trzech listenerach do tych trzech sliderów ma być użyta ta sama instancja AtomicBoolean, tzn operację new AtomicBoolean(false) masz robić tylko raz.

0

^ Dzięki, zrobiłem tym sposobem i działa. Jeszcze muszę tylko ogarnąć jeden problem. Z chwilą kiedy zaczynam przesuwać dowolny slider, oba pozostałe się wyrównują, ale cały czas utrzymując stałą wartość.

Tak to wygląda w tej chwili:

				slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				
				JSlider source = (JSlider)e.getSource();
				
				int dif = (100 - slider.getValue());
				
				if (lol.compareAndSet(false, true)) {
					slider_1.setValue(dif/2);
					slider_2.setValue(dif/2);
					 
					  lol.set(false);
					}

			}
		});
0

Bo nie zrobiłeś tak jak napisałem. A napisałem tak:

Jeśli chcesz by oba naraz się przesuwały to:

  1. Najpierw oblicz różnicę sumy, tzn difference = 100 - (slider1.value() + slider2.value() + slider3.value());
  2. Następnie podziel tą wartość na dwa składniki (niekoniecznie różne), np difference1 = difference / 3; difference2 = difference - difference1, przy czym musisz zadbać o to by końcowe wartości nie wyleciały poza zakres [0, 100].
  3. Nastepnie dodaj difference1 do slider1 i difference2 do slider2.

A więc zamiast :

 int dif = (100 - slider.getValue());
 slider_1.setValue(dif/2);
 slider_2.setValue(dif/2);

Powinieneś zrobić coś mniej więcej takiego:

int dif = 100 - slider.getValue() - slider1.getVaule() - slider2.getValue(); // liczenie dif też rób w ifie !!
int dif1 = dif * jakaśStałaMiędzy0a1; // np 0.5
int toSlide;
if (dif < 0) {
  toSlide = Math.max(-slider1.getValue(), dif1);
} else if (dif > 0) {
  toSlide = Math.min(100 - slider1.getValue(), dif1);
}
slider1.setValue(slider1.getValue() + toSlide);
slider2.setValue(slider2.getValue() + dif - toSlide);

Całość powyższego kodu ma być w ifie z atomicBooleanem. Kod 100% nietestowany.

0
 
slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				
				if (lol.compareAndSet(false, true)) {
					int dif = 100 - slider.getValue() - slider_1.getValue() - slider_2.getValue();
					double dif1 = dif*0.5; //* 0.5; // np 0.5
					int toSlide = 0;
					
					if (dif < 0) 
					{
					  toSlide = (int) Math.max(-slider.getValue(), dif1);
					} 
					else if (dif > 0) 
					{
					  toSlide = (int) Math.min((100 - slider.getValue()), dif1);
					}
					slider_1.setValue((slider_1.getValue() + toSlide));
					slider_2.setValue((slider_2.getValue() + dif - toSlide));
						
					String aString = Integer.toString(toSlide);
					lblNewLabel.setText(aString);
					
					lol.set(false);
					}

			}
		});

Działa dosyć dobrze, dzięki. Będę musiał potem jeszcze nad tym posiedzieć, bo czasami suma lekko przekracza 100.

0

W złym miejscu robisz zaokrąglanie. Zamiast:
double dif1 = dif*0.5; //* 0.5; // np 0.5
Zrób:
int dif1 = (int) (dif * 0.5)

W sumie nasunęła mi się teraz jeszcze jedna myśl, tzn ten ChangeEvent będzie raczej leciał bardzo często i np dif może mieć bardzo małe wartości względne, co może psuć wrażenie przy przesuwaniu. Proponuję więc kolejną zmianę, tzn kod do liczenia dif1 zamienić na:
int dif1 = (int)(dif * jakaśStałaMiędzy0a1 + Math.random() - 0.5);
Może będzie działać lepiej.

0

Dla zabawy napisałem sobie taki programik. Zmienna dif ma praktycznie zawsze wartość 1. Co implikuje, że przesuwany suwak wymusza ruch tylko jednego suwaka. Pomoże rozwiązanie zaproponowane przez @Wibowita. Ja zastosowałem gorsze (też działające): mam suwaki w tablicy, co drugie wywołanie metody stateChanged zmieniam zwrot wędrowania po tablicy.

0

Po poprawieniu tego zaokrąglania cały czas miałem problemy z wykraczaniem wartości ponad 100, więc dopisałem warunek:

slider.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				
				if (lol.compareAndSet(false, true)) {
					int dif = 100 - slider.getValue() - slider_1.getValue() - slider_2.getValue();
					int dif1 = (int)(dif * 0.5 + Math.random() - 0.5);

					int toSlide = 0;
					
					if (dif < 0) 
					{
					  toSlide = (int) Math.max(-slider.getValue(), dif1);
					} 
					else if (dif > 0) 
					{
					  toSlide = (int) Math.min((100 - slider.getValue()), dif1);
					}
					slider_1.setValue((slider_1.getValue() + toSlide));
					slider_2.setValue((slider_2.getValue() + dif - toSlide));
					
					if ((slider.getValue() + slider_1.getValue() + slider_2.getValue()) > 100)
					{
						toSlide = (slider.getValue() + slider_1.getValue() + slider_2.getValue()) - 100;

						if (slider_1.getValue() > slider_2.getValue())
							slider_1.setValue(slider_1.getValue() - toSlide);
						else
							slider_2.setValue(slider_2.getValue() - toSlide);
					}
					
					String s1 = Integer.toString(slider.getValue());
					String s2 = Integer.toString(slider_1.getValue());
					String s3 = Integer.toString(slider_2.getValue());
					
					String s4 = Integer.toString(slider.getValue() + slider_1.getValue() + slider_2.getValue());
					
					lbl1.setText(s1);
					lbl2.setText(s2);
					lbl3.setText(s3);
					lbl4.setText(s4);
			 
					lol.set(false);
					}

			}
		}); 
0

Nie działa identycznie jak na tej stronie, ale ten stan rzeczy mnie satysfakcjonuje póki co. Dziwi tylko trochę fakt, że to jak szybko poruszam sliderem wpływa na to jak procentowo rozkłada się zmiana na innych.

Ten kod który podałem rozkłada różnicę w stałym podziale pomiędzy pozostałe slidery. Sprawdziłem jak slidery działają na Humble Bundle - tam rozkład nie jest stały. Na Humble Bundle rozkład różnicy na pozostałe slidery jest taki sam jak rozkład wartości na pozostałych sliderach.

Jeśli chcesz mieć zachowanie jak na Humble Bundle to musisz zmienić linijkę:
int dif1 = (int)(dif * 0.5 + Math.random() - 0.5);
Na:

double factor;
if (slider1.value() + slider2.value() == 0) { // tzn oba są zero
  factor = 0.5;
} else {
  factor = slider1.value() / (slider1.value() + slider2.value());
}
int dif1 = (int)(dif * factor + Math.random() - 0.5);

Ps:

                                        if (dif < 0) 
                                        {
                                          toSlide = (int) Math.max(-slider.getValue(), dif1);
                                        } 
                                        else if (dif > 0) 
                                        {
                                          toSlide = (int) Math.min((100 - slider.getValue()), dif1);
                                        }

Po co rzutujesz inta na inta?

1 użytkowników online, w tym zalogowanych: 0, gości: 1