Comparison criterion | Avian Classpath | Android Classpath |
Completeness and Compliance | It contains only the very minimum, although it expands. However, it is not possible to launch any serious ready project on this platform. | Contains the implementation of all core Java Core classes. Theoretically, it should support any not too confused application. In practice, there may be complications associated with the transfer of the library on Win32. Of course, all such errors will be corrected as soon as possible. |
Executable file size | An executable file that does not contain SWT classes is slightly larger than a megabyte. | The executable file is blown up to 25 megabytes. This is mainly not due to the Android classes themselves (which take up about 5MB), but because of the encoding tables, part of ICU4C, the language support library of the Android Classpath |
License | BSD (Compatible with GPL) | Apache (also open, but incompatible with the GPL) |
zetes-works
. Go into it and inside create a folder zetes
, into which we unpack the folder target-**
from the downloaded archive. Now let's go back to zetes-works
and create a folder called tinyviewer
. The application we are going to create will be called TinyViewer. It demonstrates the main features of ZetesWings. The application is a simple image viewer. It allows you to simultaneously download multiple files, showing each in a separate window. The finished version of this program is among the examples presented by me in the zetes-examples repository. For the impatient at the end of the article there is a link to the compiled release version of the sample applications. ZETES_PATH = ../zetes APPLICATION_NAME = Tiny\ Viewer BINARY_NAME = tinyview ENTRY_CLASS = tinyviewer/TinyViewerApplication include $(ZETES_PATH)/common-scripts/globals.mk include $(ZETES_WINGS_PATH)/ZetesWings.mk all: package
ZETES_PATH
variable is ZETES_PATH
, which sets the path to the Zetes
library relative to the project directory. Next comes APPLICATION_NAME
, the human-readable name of the application Then - BINARY_NAME
- the name of the executable file and ENTRY_CLASS
- the name of the class containing the entry point - the function public static void main(String... args);
. The class name is given by slashes (/), and not by periods, as is customary for Java developers. That is, in fact, the class will be called tinyviewer.TinyViewerApplication
.globals.mk
- contains general definitions, constants and rules, the ZetesWings.mk
file is specifically for ZetesWings. In the case of using ZetesHands, we would include, respectively, its makefile.all: app
.ApplicationBase
is the main class of the application that defines the global parameters of its behavior. In fact, it is created in one instance (although it is not a singleton in the strict sense). It is also advisable to put an entry point into it ( main
function)ViewWindowsManagerBase
is a class that manages windows-View. Creates windows for documents, stores links to them. Communication between the windows through it.MenuConstructorBase
- the class that controls the menu - both global (in OS X) and the window menu. Responsible for the appearance / disappearance / deactivation of menu items, for the correct definition of hot keys. Working with the main menu of the application to bypass this class is extremely undesirable. All requests for improvement are accepted. The main task is to ensure the “cultural independence” of the menu from the platformViewWindowBase
- a window showing the contents of a document. This implementation is closely related to the notion of Shell
from SWTDocument
- document. The most abstract of all the entities described. Must be able to report its title (to form the window title and menu item) with the getTitle()
function, and also forcefully clean its resources using the dispose()
functionDocument
interface) are templates that have a connection between them. And now we need to make the heir from each of them and tie these heirs together. package tinyviewer; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.widgets.Display; import zetes.wings.abstracts.Document; public class ImageDocument implements Document { private Image image; private String fileName; private static Image loadImage(InputStream stream) throws IOException { try { Display display = Display.getDefault(); ImageData data = new ImageData(stream); if (data.transparentPixel > 0) { return new Image(display, data, data.getTransparencyMask()); } return new Image(display, data); } finally { stream.close(); } } private static Image loadImage(String fileName) throws IOException { return loadImage(new FileInputStream(fileName)); } public ImageDocument(String fileName) throws IOException { this.fileName = fileName; this.image = loadImage(fileName); } public String getTitle() { return fileName; } public Image getImage() { return image; } public void dispose() { if (image != null && !image.isDisposed()) image.dispose(); } @Override protected void finalize() throws Throwable { dispose(); super.finalize(); } }
loadImage
function loadImage
responsible for loading the image and is called in the constructor. The getTitle()
function returns the file name, and dispose()
frees the image resources. Just in case, finalize()
also redefined to dispose()
to be called. package tinyviewer; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; public class ImageView extends Canvas { Image image = null; Image zoomedImage = null; double zoom = 1.0; public ImageView(Composite arg0, int arg1) { super(arg0, arg1); addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { draw(e.gc); } }); } protected void draw(GC gc) { if (image != null) { checkZoomedImage(); Point viewSize = getSize(); Point imageSize = new Point(zoomedImage.getBounds().width, zoomedImage.getBounds().height); int xpos = viewSize.x > imageSize.x ? viewSize.x / 2 - imageSize.x / 2 : 0; int ypos = viewSize.y > imageSize.y ? viewSize.y / 2 - imageSize.y / 2 : 0; gc.drawImage(zoomedImage, xpos, ypos); } } public Point desiredSize() { if (image == null) return new Point(1, 1); else return new Point((int)(image.getImageData().width * zoom), (int)(image.getImageData().height * zoom)); } protected void checkZoomedImage() { if (zoomedImage == null || zoomedImage.isDisposed()) { Rectangle bounds = image.getBounds(); zoomedImage = new Image(image.getDevice(), (int)(bounds.width * zoom), (int)(bounds.height * zoom)); GC gc = new GC(zoomedImage); gc.drawImage(image, 0, 0, bounds.width, bounds.height, 0, 0, (int)(bounds.width * zoom), (int)(bounds.height * zoom)); gc.dispose(); } } public void setImage(Image image) { this.image = image; if (zoomedImage != null) zoomedImage.dispose(); this.redraw(); } public Image getImage() { return image; } public void setZoom(double zoom) { this.zoom = zoom; if (zoomedImage != null) zoomedImage.dispose(); this.redraw(); } public double getZoom() { return zoom; } }
ImageView
class. In essence, the window will simply be a frame around this very ImageView
. package tinyviewer; import java.util.HashSet; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Shell; import org.eclipse.wb.swt.SWTResourceManager; import zetes.wings.base.ViewWindowBase; public class ImageViewWindow extends ViewWindowBase<ImageDocument> { private ScrolledComposite scrolledComposite; private DropTarget scrolledCompositeDropTarget, imageViewDropTarget; private ImageView imageView; private HashSet<DropTargetAdapter> dropTargetAdapters = new HashSet<DropTargetAdapter>(); public void addDropTargetListener(DropTargetAdapter dropTargetAdapter) { dropTargetAdapters.add(dropTargetAdapter); if (imageViewDropTarget != null && !imageViewDropTarget.isDisposed()) { scrolledCompositeDropTarget.addDropListener(dropTargetAdapter); imageViewDropTarget.addDropListener(dropTargetAdapter); } } public void removeDropTargetListener(DropTargetAdapter dropTargetAdapter) { dropTargetAdapters.remove(dropTargetAdapter); if (imageViewDropTarget != null && !imageViewDropTarget.isDisposed()) { scrolledCompositeDropTarget.removeDropListener(dropTargetAdapter); imageViewDropTarget.removeDropListener(dropTargetAdapter); } } /** * Create contents of the window. * * @wbp.parser.entryPoint */ @Override protected Shell constructShell() { Shell shell = new Shell(SWT.TITLE | SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.BORDER | SWT.DOUBLE_BUFFERED); shell.setBackground(SWTResourceManager.getColor(SWT.COLOR_BLACK)); shell.setMinimumSize(new Point(150, 200)); shell.setImages(new Image[] { SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos16.png"), // Necessary in Windows (for taskbar) SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos64.png"), // Necessary in Windows (for Alt-Tab) SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos512.png") // Necessary in OS X }); shell.setLayout(new FillLayout(SWT.HORIZONTAL)); scrolledComposite = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.NO_BACKGROUND); scrolledComposite.setExpandHorizontal(true); scrolledComposite.setExpandVertical(true); imageView = new ImageView(scrolledComposite, SWT.NONE); imageView.setBounds(0, 0, 200, 127); imageView.setVisible(false); imageView.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_BLACK)); // Drop targets scrolledCompositeDropTarget = new DropTarget(scrolledComposite, DND.DROP_MOVE); scrolledCompositeDropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() }); imageViewDropTarget = new DropTarget(imageView, DND.DROP_MOVE); imageViewDropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() }); for (DropTargetAdapter adapter : dropTargetAdapters) { scrolledCompositeDropTarget.addDropListener(adapter); imageViewDropTarget.addDropListener(adapter); } scrolledComposite.setContent(imageView); scrolledComposite.setMinSize(imageView.desiredSize()); scrolledComposite.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_BLACK)); scrolledComposite.addControlListener(new ControlListener() { @Override public void controlResized(ControlEvent arg0) { updateImageViewSize(); } @Override public void controlMoved(ControlEvent arg0) { } }); return shell; } private void updateImageViewSize() { Point desired = imageView.desiredSize(); Point clientAreaSize = new Point(scrolledComposite.getClientArea().width, scrolledComposite.getClientArea().height); int width = Math.max(clientAreaSize.x, desired.x); int height = Math.max(clientAreaSize.y, desired.y); Point newSize = new Point(width, height); Point oldSize = imageView.getSize(); if (!oldSize.equals(newSize)) { imageView.setSize(newSize); } } @Override public void setDocument(ImageDocument document) { super.setDocument(document); imageView.setImage(getDocument().getImage()); scrolledComposite.setMinSize(imageView.desiredSize()); updateImageViewSize(); imageView.setVisible(true); getShell().forceActive(); } @Override public boolean supportsFullscreen() { return true; } @Override public boolean supportsMaximizing() { return true; } }
ViewWindowBase
template, to which our document class is passed as a parameter. That is, the window is associated with a document of this type. Clearly shows the relationship between classes.setDocument
function). Then just please the user with his appearance, until he closes it. In addition, as the DropTarget
objects indicate to us, the window responds to the event of “throwing” a file of the specified type into it. In the same window, the file will not open (it would be just not interesting), it will be loaded as a new document and a separate window will be created for it. Note that this class is not responsible for opening new windows, so it brings the listener to the outside with the methods addDropTargetListener
and removeDropTargetListener
. The use of these methods will be shown below.constructShell()
is called when creating a window to form a Shell
object from SWT. This object is our window. Annotation @wbp.parser.entryPoint
above method - an instruction for the visual painter of SWF windows in Eclipse to edit this particular method when modifying a window through its graphical representation in design-time. So, theoretically, you will be able to draw a window and buttons with the mouse;) (although I myself prefer to write with code — this is better). This is where internal events are assigned. About setDocument()
I have already said. And the supportsFullscreen()
and supportsMaximizing()
methods manage the corresponding features in Zetes. For example, a window that returns false
on supportsFullscreen()
will not have a deployment button with arrows on OS X, and will also not create a menu item "Fullscreen" on any platform. package tinyviewer; import java.io.IOException; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.FileTransfer; import zetes.wings.base.ViewWindowsManagerBase; public class ImageViewWindowsManager extends ViewWindowsManagerBase<ImageDocument, ImageViewWindow> { private DropTargetAdapter viewWindowDropTargetAdapter = new DropTargetAdapter() { public void drop(DropTargetEvent event) { String fileList[] = null; FileTransfer ft = FileTransfer.getInstance(); if (ft.isSupportedType(event.currentDataType)) { fileList = (String[]) event.data; for (int i = 0; i < fileList.length; i++) { ImageDocument document; try { document = new ImageDocument(fileList[i]); openWindowForDocument(document); } catch (IOException e) { e.printStackTrace(); } } } } }; @Override protected ImageViewWindow createViewWindow() { ImageViewWindow vw = new ImageViewWindow(); vw.addDropTargetListener(viewWindowDropTargetAdapter); return vw; } public DropTargetAdapter getViewWindowDropTargetAdapter() { return viewWindowDropTargetAdapter; } }
ViewWindowsManagerBase
generic class, which is associated with both the ImageViewWindow
window class and the ImageDocument
document ImageDocument
.DropTarget
in the picture window (remember, did we refer to this in the last section?). Here we have defined the createViewWindow()
factory createViewWindow()
in such a way that when creating a new window, a Drag-n-drop handler is immediately attached to it. package tinyviewer; import zetes.wings.HotKey; import zetes.wings.base.MenuConstructorBase; import zetes.wings.actions.Action; import zetes.wings.actions.Handler; public class TinyViewerMenuConstructor extends MenuConstructorBase<ImageViewWindow> { private Handler<ImageViewWindow> fileOpenHandler; private Action<ImageViewWindow> openAction; public TinyViewerMenuConstructor(ImageViewWindowsManager viewWindowsManager) { super(viewWindowsManager); openAction = new Action<>("&Open"); openAction.setHotKey(new HotKey(HotKey.MOD1, 'O')); getFileActionCategory().addFirstItem(openAction); } public Handler<ImageViewWindow> getFileOpenHandler() { return fileOpenHandler; } public void setFileOpenHandler(Handler<ImageViewWindow> fileOpenHandler) { this.fileOpenHandler = fileOpenHandler; if (openAction.getHandlers().get(null) == null) { openAction.getHandlers().put(null, fileOpenHandler); } } }
actionCopy
function, which the menu handler should call. That is, once again: the menu itself does nothing, it always asks for it either a window or a window manager (in our case, ImageViewWindowsManager
) .Action
class. This class implements the menu item. It consists of a title (“title”), a hot key combination (“hotKey”) and handlers (“handlers”), the latter being assigned as Map
, where the keys are ViewWindow
. The bottom line is simple. When a window is created, it adds its handlers to those actions that interest it. In addition, there is one handler that is not associated with any window (with the null
key). This handler is called when there is no active window (in OS X it is possible that there is a menu and no windows).Handler
class) is not just a listener. It contains options such as isEnabled()
, isVisible()
, isChecked()
. In addition, it is able to overlap the action header with its own (for the case of a context-sensitive header). In this case, it has its own getTitle()
.TinyViewerMenuConstructor
creates Action
objects for the main menu. In our case, there is only one non-standard element - “Open”. We create it and assign it a HotKey(HotKey.MOD1, 'O')
hot key HotKey(HotKey.MOD1, 'O')
. MOD1
is such a modifier, which under OS X corresponds to the Command key, and under other platforms, Control. Users of both traditions know that most hotkeys with Ctrl in Windows are pressed in Cmd in OS X.TinyViewerApplication
application TinyViewerApplication
. It is associated with all the classes listed above. Plus, it is associated with the heir of AboutBox
(in our case, DefaultAboutBox
), which implements the “About Application” window. package tinyviewer; import java.io.IOException; import java.util.ArrayList; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; import zetes.wings.base.ApplicationBase; import zetes.wings.DefaultAboutBox; import zetes.wings.actions.Handler; public class TinyViewerApplication extends ApplicationBase<DefaultAboutBox, ImageDocument, ImageViewWindow, TinyViewerMenuConstructor, ImageViewWindowsManager> { @Override public String getTitle() { return "Tiny Viewer"; } @Override public DefaultAboutBox createAboutBox(ImageViewWindow window) { DefaultAboutBox res = new DefaultAboutBox(window); res.setApplicationName(getTitle()); res.setIconResourceName("/tinyviewer/wingphotos64.png"); res.setDescriptionText("A simple image file viewer.\nThis application shows the power of Avian + SWT"); res.setCopyrightText("Copyright \u00a9 2013, Ilya Mizus"); res.setWindowSize(new Point(370, 180)); return res; } @Override public ImageDocument loadFromFile(String fileName) { try { return new ImageDocument(fileName); } catch (IOException e) { e.printStackTrace(); return null; } } private Handler<ImageViewWindow> fileOpenHandler = new Handler<ImageViewWindow>() { @Override public void execute(ImageViewWindow window) { Shell dummyShell = new Shell(Display.getDefault()); FileDialog fileDialog = new FileDialog(dummyShell, SWT.OPEN | SWT.MULTI); fileDialog.setText("Open image"); fileDialog.setFilterNames(new String[] { "Image (*.png; *.bmp; *.jpg; *.jpeg)", "All files" }); fileDialog.setFilterExtensions(new String[] { "*.png; *.bmp; *.jpg; *.jpeg", "*.*" }); String firstFile = fileDialog.open(); if (firstFile != null) { String[] names = fileDialog.getFileNames(); ArrayList<ImageDocument> documents = new ArrayList<ImageDocument>(); // Creating documents for files for (int i = 0; i < names.length; i++) { String fileName = fileDialog.getFilterPath() + "/" + names[i]; try { documents.add(new ImageDocument(fileName)); } catch (IOException e) { // TODO Show a message box here e.printStackTrace(); } } getViewWindowsManager().openWindowsForDocuments(documents.toArray(new ImageDocument[] {})); } dummyShell.dispose(); } }; public TinyViewerApplication() { } @Override public ImageViewWindowsManager createViewWindowsManager() { return new ImageViewWindowsManager(); } @Override public TinyViewerMenuConstructor createMenuConstructor(ImageViewWindowsManager viewWindowsManager) { TinyViewerMenuConstructor menuConstructor = new TinyViewerMenuConstructor(viewWindowsManager); menuConstructor.setFileOpenHandler(fileOpenHandler); return menuConstructor; } @Override public boolean needsAtLeastOneView() { return false; } public static void main(String... args) { new TinyViewerApplication().run(args); } }
fileOpenHandler
that contains the object , which is the global handler of the “Open” menu item. In addition, it contains several factory methods for our “global” objects (menu constructor, window manager and “about program” window). It also announced an interesting method needsAtLeastOneView()
, which can be a little different.needsAtLeastOneView()
false
, OS X .needsAtLeastOneView()
, true
. , . , OS X , .src/res
— src/java
— Java Java-. 3 SWT : shell.setImages(new Image[] { SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos16.png"), SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos64.png"), SWTResourceManager.getImage(ImageViewWindow.class, "/tinyviewer/wingphotos512.png") });
win-res
— , Windows EXE. , , win.rc
MainIcon ICON "win-res/wingphotos.ico" 1 VERSIONINFO FILEVERSION 0,1,0,0 PRODUCTVERSION 0,1,0,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "CompanyName", "bigfatbrowncat\0" VALUE "FileDescription", "A tiny multi document photo viewer app" VALUE "FileVersion", "0.1\0" VALUE "InternalName", "TinyViewer\0" VALUE "LegalCopyright", "Ilya Mizus\0" VALUE "OriginalFilename", "tinyviewer.exe\0" VALUE "ProductName", "TinyViewer\0" VALUE "ProductVersion", "0.1\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1251 END END
osx-bundle
— .app, OS X «application bundle» . bundleresources
- files placed in this folder under all systems, except OS X, will be copied to the application folder. Under OS X, they will be placed in the Resources folder located inside the bundle. These files are available from the application as resources through the class WinLinMacApi
. In this project, this folder is missing, but it is present in other projects from among zetes-examples .Source: https://habr.com/ru/post/225187/
All Articles