*/ ?>

Jednoduchý Drag & Drop

Přetahování elemetů mezi dvěma seznamy patří většinou do příjemných rozšíření aplikací a přináší zpětnou vazbu a jednoduchost do jinak těžce realizovatelných funkcí.

Začněme definováním jednoduchého grafického seznamu elementů. Nejdříve nadefinuji třídu Container, která se bude starat o seznam elementů, zobrazí k nim pozadí a bude je udržovat zarovnané pod sebou.

class Container extends CustomNode {
	var list: Group = Group {}
	var nodes: Node[] on replace {
		var t = 0;
		for (node in nodes) {
			node.translateY = t;
			t += node.layoutBounds.height + 10;
		}
		list.content = nodes;
	}
	override function create() {
		Group {
			content: [
				Rectangle {
					x: bind list.boundsInParent.minX - 5
					y: bind list.boundsInParent.minY - 5
					width: bind list.boundsInParent.width + 10
					height: bind list.boundsInParent.height + 10
					fill: Color.YELLOW
				},
				list
			]
		}
	}
}

Jako elementy pro jednoduchost použiju obyčejné obdélníky vyplněné náhodnými barvami, kterými následně vyplním Container.

class Draggable extends CustomNode {
	var container : Container;
	var rect = Rectangle {
			width: 80
			height: 30
			fill: randomColor()
	}
	override function create() {
		rect
	}
	function randomColor() : Color {
		Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255))
	}
}

var container1 = Container {
	translateX: 10
	translateY: 50
}
container1.nodes = [
	Draggable { container: container1 },
	Draggable { container: container1 },
	Draggable { container: container1 },
	Draggable { container: container1 }
];

Stejným způsobem pak vytvořím container2 a oba vložím spolu s jednoduchým nadpisem do scény.

Stage {
	title: TITLE
	scene: Scene {
		width: 240
		height: 320
		content: [
			header,
			container1,
			container2
		]
	}
}

Pro funkčnost drag&drop je třeba, aby každý z vytvořených obdélníků reagoval na onMouseDragged.

Rectangle {
	width: 80
	height: 30
	fill: randomColor()
	onMouseDragged: function(e) {
		this.translateX = e.dragX;
		this.translateY = e.dragY;
	}
}

Tato úprava sice způsobí, že se element stane táhnuteným, bohužel ale rozbije všechno ostatní. Při táhnutí je potřeba obdélník ze seznamu vyjmout a vložit do scény samostatně. Nejdříve přidám proměnou pro aktuálně tažený elment a zajistím aby se zobrazil ve scéně.

// aktuálně tažený element
var dragged: Draggable;
Stage {
	scene: Scene {
		content: [
			header,
			container1,
			container2,
			Group {
				// zobrazuje aktuálně tažený element
				content: bind [dragged]
			}
		]
	}
}

Následně vložím kód zpracovávající reakci na drag a release. Funkce přidávám přímo do třídy Draggable pro větší přehlednost:

class Draggable extends CustomNode {
	override var onMouseDragged = function(e) {
		if (not drag) {
			drag = true;
			delete this from container.nodes;
			dragged = this;
			// napozicovat element "pod myš"
			rect.x = e.sceneX - e.x;
			rect.y = e.sceneY - e.y;
		}
		// posunout element o relativní posun myši
		translateX = e.dragX;
		translateY = e.dragY;
	}
	override var onMouseReleased = function (e) {
		if (drag) {
			drag = false;
			// všechno se vrátí do původního stavu
			translateX = 0;
			translateY = 0;
			rect.x = 0;
			rect.y = 0;
			dragged = null;
			insert this into container.nodes;
		}
	}
}

Teď už se táhnutelné elemety chovají tak jak mají – pohybují se spolu s myší a při upuštění se vrátí zpět do původního seznamu. Aby se element při upuštění zařadil do správného seznamu, musí každý Container reagovat na onMouseEntered a onMouseExited. Je tedy třeba říct táhnutému elementu, kam má upadnout. A dále je potřeba zajistit, aby se element upuštěný jenom tak v prostoru vrátil na původní místo.

class Draggable extends CustomNode {
	var startingContainer: Container;
	var container : Container on replace {
		if (container == null and startingContainer != null) {
			container = startingContainer;
		}
	};
}

class Container extends CustomNode {
	override var onMouseEntered = function(e) {
		// já jsem teď tvůj nový container
		dragged.container = this;
	}
	override var onMouseExited = function(e) {
		// zapomeň na mě, vrať se odkud si přišel
		dragged.container = null;
	}
}

Teď už mám plně funkční kód a zbývá jenom přidat trochu vizuální odezvy. Přidáním jedné řádky se táhnuté elementy stanou lehce průhlednými a aktivní seznamy se rozsvítí po najetí myši.

class Draggable extends CustomNode {
	override var opacity = bind if (drag) 0.6 else 1
}

class Container extends CustomNode {
	override function create() {
		Group {
			content: [
				Rectangle {
					fill: bind if (hover and dragged != null) Color.GREEN else Color.YELLOW
				},
				list
			]
		}
	}
}

Jak to celé funguje si můžeš prohlédnout tu:

Zdrojové kódy jsou k dispozici na githubu: simple-drag-drop

  1. Přizpůsobivé pozadí
  2. document.getPageSize()
  3. Políčka s hintom
  4. Dynamicky generovaný iframe
  5. Zrovnanie výšok elementov

Zverejnené 25.2.2009
v kategórii JavaFX.

Nálepky:
,

Autor článku

Honza Štěrba, http://honzasterba.cz

Su z Moravy. Pracuju v Sun Microsystems. Programování mě baví.

Vyjadri sa

Tvůj komentář se zobrazí, až ho některý z adminů schválí. Zveřejňovat budeme pouze hodnotné komentáře, které se přímo týkají tématu.


O projekte

Tento projekt vznikol, pretože všetky odborné weby sajú a my sme tým pádom nemali kde publikovať svoje články.