Kurs Java Podstawy - rozszerzony

Pobieranie i wyświetlanie obrazów

Rysowanie takich kształtów jak prostokąty, koła, czy proste nie zawsze będzie nas zadowalało, zapewne większość z nas wolałaby wyświetlać coś bardziej użytecznego, na przykład obrazki. Tego właśnie nauczymy się w tej lekcji - pokażemy jak w najprostszy sposób wyświetlać obrazki w różnych formatach zarówno z naszego dysku, jak i bezpośrednio z internetu.

Java oferuje nam w tej kwestii kilka możliwości, na przykład wykorzystując:

  • klasę ImageIO
  • obiekt klasy Toolkit

My będziemy korzystać z tej pierwszej, przede wszystkim dlatego, że jest bardziej uniwersalna. Statyczna metoda ImageIO.read() daje w wyniku obiekt klasy BufferedImage, który z kolei jest podtypem dla Image. Nie ma w tym przypadku problemu z przekazaniem takiego obiektu do metod wymagających któregokolwiek z tych typów. Metoda getImage() klasy Toolkit zwraca z kolei obiekt Image, którego nie można przekazać bezpośrednio do metod przyjmujących jako parametr obiekt BufferedImage, a co za tym idzie używać niezbyt wygodnego rzutowania.

Wszystkie metody w klasie ImageIO są zdefiniowane jako statyczne, a klasa oznaczona jest jako finalna co oznacza, że nie może być przez nas rozszerzana. Nie można także utworzyć instancji klasy ImageIO, ponieważ nie udostępnia ona publicznego konstruktora (dosyć logiczne biorąc pod uwagę wspomniane statyczne metody). Zaglądając do dokumentacji tejże klasy możemy zobaczyć, że jej przeznaczenie, to czytanie i zapisywania plików graficznych. Jak się za chwilę przekonamy jest to wręcz bajecznie proste.

Utwórzmy w eclipse projekt o dowolnej nazwie, ja swój nazwę Obrazek. następnie dodajmy trzy klasy, jedna rozszerzająca JPanel, druga JFrame (tak jak robiliśmy to do tej pory, mogą znajdować się w domyślnym pakiecie), oraz oczywiście nasza magiczna klasa testowa. Przykładowe nazywają się odpowiednio ObrazPanel, ObrazFrame i Test. Przejdźmy do listingu i omówienia:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ObrazPanel extends JPanel {

	private BufferedImage image;

	public ObrazPanel() {
		super();

		File imageFile = new File("java.jpg");
		try {
			image = ImageIO.read(imageFile);
		} catch (IOException e) {
			System.err.println("Blad odczytu obrazka");
			e.printStackTrace();
		}

		Dimension dimension = new Dimension(image.getWidth(), image.getHeight());
		setPreferredSize(dimension);
	}

	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		g2d.drawImage(image, 0, 0, this);
	}
}

W klasie ObrazPanel widzimy kilka nowych rzeczy (oprócz obrzydliwego nagłówka z importami klas). Po pierwsze obiekt klasy BufferedImage - reprezentuje on obraz w formie tablicy atrybutów, które odpowiadają poszczególnym pikselom obrazka. Możemy z niego oczytać poszczególne składowe koloru w dowolnym punkcie i wiele innych - ale każdy zainteresowany może zerknąć w tym celu do dokumentacji. Przed utworzeniem naszego obiektu image, musimy utworzyć obiekt reprezentujący plik obrazka - w naszym wypadku imageFile znanej nam już klasy File. Cała magia polegająca na zapisie obrazu jest równie prosta, wystarczy, że wywołamy jedną z przeciążonych wersji metody read() klasy ImageIO. W naszym przypadku wykorzystujemy tę wersję, która jako argument przyjmuje wcześniej utworzony obiekt klasy File. Może ona generować wyjątek związany z odczytem - plik może na przykład nie istnieć. Dalej ustawiamy rozmiar panelu równy rozmiarom obrazkom - które także można pobrać z obiektu BufferedImage.

Nie pozostaje nam nic innego jak wyświetlenie obrazka w przesłoniętej metodzie paintComponent za pomocą metody drawImage obiektu Graphics, lub Graphics2D.

import javax.swing.JFrame;
import javax.swing.JPanel;

public class ObrazFrame extends JFrame {

	public ObrazFrame() {
		super("Program obrazkowy");

		JPanel obrazPanel = new ObrazPanel();
		add(obrazPanel);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		pack();
		setVisible(true);
	}
}

W klasie ramki tworzymy obiekt reprezentujący nasz panel i go dodajemy. Jak wiemy dzięki wywołaniu metody pack() jej rozmiar dopasuje się automatycznie.

import java.awt.EventQueue;

public class Test {
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				new ObrazFrame();
			}
		});
	}
}

No i klasa testowa.

W wyniku działania programu powinniśmy zobaczyć ramkę z obrazkiem - obrazek możesz wybrać dowolny, nie zapominając o zmianie nazwy w klasie ObrazekPanel. To co ważne powinieneś go umieścić bezpośrednio w katalogu projektu, czyli np C:\Users\Sławek\workspace\Obrazek. Jak się później dowiemy, używanie Stringa z nazwą pliku nie jest najlepszym rozwiązaniem, ponieważ będziemy mieli problem z przenośnością pliku, który spakujemy jako jar, ale na razie nie zawracajmy sobie tym głowy.

Co ciekawe równie dobrze możemy w niemal identyczny sposób utworzyć obraz bezpośrednio pobrany z podanego adresu URL. W tym celu należy kolejno:

  • Utworzyć obiekt klasy URL - w konstruktorze przekazać ścieżkę url do obrazu (uwaga może generować wyjątek MalformedURLException np. gdy podany adres nie zostanie odnaleziony).
  • Utworzyć obraz BufferedImage przypisując mu wynik metody ImageIO.read(), której jako parametr przekazać utworzony obiekt URL.

Zadanie do samodzielnego wykonania:

4.1 Stwórz na podstawie powyższego schematu program, który wyświetli obok siebie dwa obrazy, jeden zapisany na Twoim dysku twardym, drugi pobrany z dowolnego adresu URL.

Rozwiązanie.

<- Poprzednia LekcjaNastępna Lekcja ->

Komentarze

greg

niestety nie ma rozwiązania do podejrzenia. A mimo zapowiedzianej prostoty:)) nie udaje mi się wyświetlić ściągniętego obrazka:(

Slawek

wieczorem dodam rozwiązanie.

Przemko

czekamy na rozwiązanie! :) Dobra stronka. Gratuluje i życzę niewygasającego zapału.

Marta

Witam
A jak zaimportować obrazek żeby nie było problemu z jar-em? Próbowałam projekt z samym obrazkiem jpg (bez obrazka z sieci) spakowac do jar-a ale cały czas wyskakuje mi warning przy jego tworzeniu. Kiedy klikam podwójnie ikonę jara ten się nie otwiera. Kiedy odpalam przez konsolę pisze "cannot read imput file".
Z góry dziękuję za pomoc.

garay

Jak zrobić tło z pliku w JWindow takie żeby można na nim dodawać inne komponenty tak aby było je widać?


public class Okno extends JWindow{
private BufferedImage image;
Okno(){
setSize(1024,768);
setVisible(true);
setLayout(null);
File imageFile = new File("src/tlo.jpg");
try {
image = ImageIO.read(imageFile);
} catch (IOException e) {
System.err.println("Blad odczytu obrazka");
e.printStackTrace();
}

Dimension dimension = new Dimension(image.getWidth(), image.getHeight());
setPreferredSize(dimension);
}


public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, 0, 0, this);
}


void stworzOkno(){
getContentPane().setBackground(Color.red);
}

}




public class Gra {


public static void main (String args[]){

Okno okno=new Okno();
okno.stworzOkno();
}


}

Maciek W.

Według dokumentacji raczej nie da się czegoś takiego zrobić.

garay

więc jak to zrobić?

znik

błąd opisu, "Cała magia polegająca na zapisie obrazu jest równie prosta" ma być "Cała magia polegająca na wczytaniu obrazu jest równie prosta" , bo potem jest przecież używana metoda read.

TNTeener

Chce zrobić program w którym będzie się wyświetlało zdjęcie jak sie przycisk kliknie.
Pomoże ktoś?

Anonim

Mam pytanie.
Jak usunąć obrazek z ekranu???
Chodzi mi o to żeby po np. kliknieciu myszy obraz znikal i program szedl dalej.
Potrzebuje jedynie komendy z obrazkiem reakcje na klikniecie umiem

Qbee

paintComponent wykonuje się 2 razy. Wie ktoś może dlaczego i jak temu zaradzić? (wystarczy dodać wewnątrz linijkę System.out.println("A"); żeby zobaczyć dwie litery "A" w konsoli)

mały

Mam problem. Potrzebyję prosty obraz graficzny, zawierający kilka kształtów geometrycznych zapisać w pliku. Nie znalazłem takiego przykładu. Dziękuję za pomoc.

Zoe

Może mi ktoś powiedzieć co z tym zrobić?? :D
kod przepisałam kropka w kropkę i nawet potem się wkurzyłam i przekopiowałam... błąd zostaje. Domyślam się że muszę jakoś obrazek umieścić w odpowiednim miejscu.... ale mogę poprosić o ŁOPATOLOGICZNE wskazówki co z tym zrobić? :)

Blad odczytu obrazka
javax.imageio.IIOException: Can't read input file!

Sławek Ludwiczak

Problem leży w samym pliku, lub jego złym umiejscowieniem, a nie w samym kodzie.

Zoe

..? czyli co powinnam zrobić by mi to zadziałało?

Zoe

bo z tym zadaniem "do domu" też mi to samo wyskakuje...

Marcin

Bardzo ciekawy temat, ale szkoda że tylko z obrazkami :)
A czy jest jakaś możliwość pobierania plików z Internetu i zapisywania ich na dysku (plików nieobrazkowych,
np. zip czy exe)

Anka

Ale dużo miejsca zajęłam... no w końcu udało mi się. Po prostu plik trzeba zapisać jako java a nie java.jpg :> 4 dni nad tym myślałam... chyba jednak wrócę do blond włosów....
Pozdrawiam,
Stronka super, oprócz tej małej wpadki wszystko zrozumiałe i przystępne

Ola

W jakim folderze trzeba umieścić obraz, by kod zadziałał?

lolo

W folderze z projektem.

Ola

Dziękuję

Zatherz

Niestety, zawiodłem się na tym kursie. Zapowiadał się bardzo dobry, ale te opisy robione "na szybkiego", zadania z rzeczami o których nawet autor nie wspominał... ah, a było tak fajnie... :(

eta

Potwierdzam, sporo rzeczy o których autor wcześniej nie wspominał, tak jakby zrobił to na zadanie domowe i przedstawiał to nauczycielowi, który ma całkowite pojęcie o tym co mówi uczeń

MonteChristo_

Przy wyświetlaniu dużych obrazków(oba po ustawieniu obok siebie są większe od rozdzielczości ekranu) ucina mi ten drugi obrazek i okno programu nie chce się rozciągnać na szerokość większą niż rozdzielczość. W jaki sposób mogę utworzyć okno by zmieścić w nim 2 duże obrazki i żebym mógł rozciągać okienko na dowolną szerokość?

Adalberton

Nie mam bladego pojęcia jak to zrealizować:
" - Utworzyć obiekt klasy URL – w konstruktorze przekazać ścieżkę url do obrazu (uwaga może generować wyjątek MalformedURLException np. gdy podany adres nie zostanie odnaleziony).
- Utworzyć obraz BufferedImage przypisując mu wynik metody ImageIO.read(), której jako parametr przekazać utworzony obiekt URL."

Na tej lekcji nie zostało to wyjaśnione krok po kroku (chyba że ja coś przeoczyłem). Przydałby się osobny przykład w postaci kodu.

Sławek Ludwiczak

Hej:
Klasa URL: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html
Obiekt tworzysz przez:
URL url = new URL("http://coscos.com/obrazek.jpg");

Później:
BufferedImage image = ImageIO.read(url);

Całość opakuj tak jak jest wspomniane w try...catch, żeby złapać potencjalny wyjątek (błędny URL obrazka).
Polecam też:
http://docs.oracle.com/javase/tutorial/2d/images/loadimage.html
:)

Jack

Witam, program kompiluję się, ale przy próbie wywołania za pomocą komendy "java ..." pojawia się następujący komunikat: Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError i dalej cała lista... co zrobić? Proszę o pomoc.

Janosch

A czy poprawnie zaimportowałeś w swoim programie bibliotekę "java.awt.EventQueue" w klasie testowej?

Jack

Tak, poprawnie to znaczy przy użyciu "import" i dalej tak jak napisałeś. Nie rozumiem o co chodzi, wczoraj w końcu udało mi się poprawić ten program, a teraz ponownie nie działa, mimo, że nie zmieniałem nic od tego czasu kiedy chodził.

Anna

Nie zauważyłam w komentarzach, żeby ktoś miał podobny problem do mojego a mianowicie program kompiluje się bez błędu -> uruchamia się (wymiary okienka dostosowują się do wymiarów obrazka) ale sam obrazek się nie wyświetla? Co jest przyczyną takiego zachowania?

lolo

Niestety wczoraj rozbiła mi się magiczna kula a nową przyjdzie dopiero po weekendzie więc albo musisz zaczekać albo dać kod.

Janosch

Aniu, moim zdaniem prawdopodobnie zrobiłaś gdzieś błąd w tej konstrukcji:
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, 0, 0, this);
}
Przeanalizuj i sprawdź czy nie masz jakieś literówki.

Anna

Bardzo dziękuję za pomoc. Faktycznie zrobiłam głupią literówkę z paintComponent na paintComponents. Na przyszłość będę też pamiętać o zamieszczaniu kodu.

Osin

A jak dodać obsługe zdarzen do takiego obrazu?

Piotrek

Co można użyć zamiast Stringa w nazwie pliku??

Lolo

Obiektów dziedziczących po Stringu

Lolo

Miało być Obiektów klas dziedziczących po Stringu

Piotrek

Mógł bym prosić o konkret.

Lolo

Wybacz wprowadziłem Cię w błąd. Zapomniałem, że nie można dziedziczyć po Stringu. W takim razie można tylko Stringa użyć.

Piotrek

Przecież Sławek napisał "Jak się później dowiemy, używanie Stringa z nazwą pliku nie jest najlepszym rozwiązaniem, ponieważ będziemy mieli problem z przenośnością pliku, który spakujemy jako jar".Czyli można użyć czegoś oprócz Stringa.

Lolo

W takim razie nie wiem co miał na myśli z tego co się orientuję konstruktor klasy File przyjmuje jako parametr obiekty String, no jest jeszcze URI ale to raczej jakbyś chciał wywołać jakiś zdalny plik.

Piotrek

A da się wywołać obraz innym konstruktorem.

Lolo

Nie do końca rozumiem co masz na myśli. Mógłbyś rozwinąć pytanie?

Piotrek

Jak robię plik jar z mojego projektu i go uruchamiam nie wyświetla mi się obraz.
I chce jakoś to naprawić.

Lolo

A plik z obrazem znajduje się w pliku jar lub w tym samym folderze co plik jar?
Mógłbyś spróbować dać ścieżkę bezwzględną do pliku z obrazkiem.
Za bardzo nie bawiłem się swingiem więc średnio się w nim orientuje.

Piotrek

Ok już wiem dziękuje za pomoc.

Piotrek

Chciałbym zrobić animacje piłki na tle innego obrazu.
I tu pojawia się problem, bo mam wyświetlani piłki i tła w jednej metodzie.
Próbowałem rozdzielić je na 2 metody ale się nie da.
I tu pojawia się pytanie: Czy da się zatrzymać wykonywanie części metody a nie całej.

Piotrek

*?