📜 ⬆️ ⬇️

How to connect the PhotoSwipe gallery in WebView android

I am not a professional developer, although I studied as a programmer. Now I work as a system administrator and plan to move to the developers. I am writing for myself an application that parses one of the most popular IT-related sites and shows articles in the native android application. I wanted all the pictures in the article to open in the gallery, as in the habr.

Most likely, the guide will be easy, but I want to get feedback and, most likely, this guide will be useful to someone.

I tried several plugins. Some did not start, others require jQuery, and this slows down the page load in the application. In the end, I chose the plugin PhotoSwipe. The plugin does not require jQuery, it works quickly and weighs almost nothing.

First you need to think where in the application it is better to place the source files of the plugin. At first, due to inexperience, I placed the files in the raw folder, but it is better to place them in the assets folder. Right-click on the app → new → folder → Assets Folder
')
The markup will be one simple webview:

Code
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent"></WebView> </android.support.constraint.ConstraintLayout> 


Next you need to download or clone the repository from PhotoSwipe (link on the plugin's website) and copy the entire contents of the dist folder into assets (dist can be renamed PhotoSwipe. You can also create a style.css file in the css folder so that the images take up the entire width.

 img { width: 100%; height: auto; } 

For the gallery you need to connect styles, javascipt files and a style file for the skin. You can generate html:

Here is going to html for webview
 public String getHTML() { String header = "<!DOCTYPE HTML><HTML><head><title>test</title>"; //  ,      //     . String headerEnd = "</head><body onload=\"init();\">"; String footer = "</body></HTML>"; String charset = "<meta charset=\"utf-8\">"; String style = "<link href=\"css/style.css\" type=\"text/css\" rel=\"stylesheet\" />"; String pscss = "<link rel=\"stylesheet\" href=\"PhotoSwipe/photoswipe.css\">"; String psdscss = "<link rel=\"stylesheet\" href=\"PhotoSwipe/default-skin/default-skin.css\">"; // ,        String script = "<script type=\"text/javascript\" src=\"js/script.js\"></script>"; String psjs = "<script src=\"PhotoSwipe/photoswipe.js\"></script> "; String psuijs = "<script src=\"PhotoSwipe/photoswipe-ui-default.min.js\"></script>"; StringBuilder stringBuilder = new StringBuilder(); //  head stringBuilder.append(header); stringBuilder.append(charset); stringBuilder.append(style); stringBuilder.append(pscss); stringBuilder.append(psdscss); stringBuilder.append(script); stringBuilder.append(psjs); stringBuilder.append(psuijs); stringBuilder.append(headerEnd); stringBuilder.append("<img src=\"https://ps.denko.me/images/linux1.jpg\" />"); stringBuilder.append("<img src=\"https://ps.denko.me/images/linux2.jpg\" />"); stringBuilder.append("<img src=\"https://ps.denko.me/images/linux3.jpeg\" />"); stringBuilder.append(getPhotoSwipeHTML()); stringBuilder.append(footer); return stringBuilder.toString(); 
.

In order for the plugin to work, you also need to insert a container for the gallery into the page source. The documentation says that this is not a simple plugin and you need to do some of the work yourself.

It is desirable to insert the code before the closing tag.

Gallery Container
 public String getPhotoSwipeHTML() { return "<!-- Root element of PhotoSwipe. Must have class pswp. -->\n" + "<div class=\"pswp\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n" + "\n" + " <!-- Background of PhotoSwipe. \n" + " It's a separate element as animating opacity is faster than rgba(). -->\n" + " <div class=\"pswp__bg\"></div>\n" + "\n" + " <!-- Slides wrapper with overflow:hidden. -->\n" + " <div class=\"pswp__scroll-wrap\">\n" + "\n" + " <!-- Container that holds slides. \n" + " PhotoSwipe keeps only 3 of them in the DOM to save memory.\n" + " Don't modify these 3 pswp__item elements, data is added later on. -->\n" + " <div class=\"pswp__container\">\n" + " <div class=\"pswp__item\"></div>\n" + " <div class=\"pswp__item\"></div>\n" + " <div class=\"pswp__item\"></div>\n" + " </div>\n" + "\n" + " <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->\n" + " <div class=\"pswp__ui pswp__ui--hidden\">\n" + "\n" + " <div class=\"pswp__top-bar\">\n" + "\n" + " <!-- Controls are self-explanatory. Order can be changed. -->\n" + "\n" + " <div class=\"pswp__counter\"></div>\n" + "\n" + " <button class=\"pswp__button pswp__button--close\" title=\"Close (Esc)\"></button>\n" + "\n" + " <button class=\"pswp__button pswp__button--share\" title=\"Share\"></button>\n" + "\n" + " <button class=\"pswp__button pswp__button--fs\" title=\"Toggle fullscreen\"></button>\n" + "\n" + " <button class=\"pswp__button pswp__button--zoom\" title=\"Zoom in/out\"></button>\n" + "\n" + " <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->\n" + " <!-- element will get class pswp__preloader--active when preloader is running -->\n" + " <div class=\"pswp__preloader\">\n" + " <div class=\"pswp__preloader__icn\">\n" + " <div class=\"pswp__preloader__cut\">\n" + " <div class=\"pswp__preloader__donut\"></div>\n" + " </div>\n" + " </div>\n" + " </div>\n" + " </div>\n" + "\n" + " <div class=\"pswp__share-modal pswp__share-modal--hidden pswp__single-tap\">\n" + " <div class=\"pswp__share-tooltip\"></div> \n" + " </div>\n" + "\n" + " <button class=\"pswp__button pswp__button--arrow--left\" title=\"Previous (arrow left)\">\n" + " </button>\n" + "\n" + " <button class=\"pswp__button pswp__button--arrow--right\" title=\"Next (arrow right)\">\n" + " </button>\n" + "\n" + " <div class=\"pswp__caption\">\n" + " <div class=\"pswp__caption__center\"></div>\n" + " </div>\n" + "\n" + " </div>\n" + "\n" + " </div>\n" + "\n" + "</div>"; } 


The documentation provides an example of enabling the plug-in by clicking on an image, but it is noted that PhotoSwipe does not care how it will be launched. In the end, I wrote my script. I show the article from another site in the application, so I get the ready html. In order not to work dynamically with the layout, I’m in my script for all the images (because there are definitely no extras in the article) changing the class, assigning an event handler (when clicking on which the url is transmitted - it serves as an identifier for the gallery, and the plugin is launched).

The plugin needs to know the size of the image. Since the gallery is connected when the entire page is loaded, it is easy to find out the final sizes of the images, through the properties of naturalWidth and naturalHeight images.

Script code First, when the page loads, the init () function starts; Then when you click on the image openGallery ().

Code
 //         function init() { var images = document.images; for (var i = 0; i < images.length; i++) { images[i].className = "photoSwipe"; images[i].onclick = function () { openGalery(this.attributes["src"].value); } } } //    function openGalery(url) { var images = document.getElementsByClassName("photoSwipe"); var items = new Array(); for (var i = 0; i < images.length; i++) { items.push(getAttributes(images[i])); } startPhotoSwipe(items, getIndex(images, url)); } //    function getAttributes(image) { return { src: image.attributes["src"].value, w: image.naturalWidth, h: image.naturalHeight } } //   ,    function getIndex(images, url) { for(var i = 0; i < images.length; i++) { if (url == images[i].attributes["src"].value) return i; } return false; } //   function startPhotoSwipe(items, index) { var pswpElement = document.querySelectorAll('.pswp')[0]; // define options (if needed) var options = { // optionName: 'option value' // for example: index: index, // start at index slide bgOpacity: 0.9, pinchToClose: false, fullscreenEl: false, closeEl:false, zoomEl: false, shareEl: false, indexIndicatorSep: '  ', tapToClose: true }; callAndroid.setGallery(true); //     Java  ,    // Initializes and opens PhotoSwipe var gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options); gallery.init(); } 


Next, you need to configure WebView and load the page.

 webView = findViewById(R.id.webView); webView.setWebViewClient(new WebViewClient()); webView.setWebChromeClient(new WebChromeClient()); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); 

At this stage, the page is loaded, the plugin starts when you click on the image, but when you press the button back when the gallery is open, the entire application closes, not the plugin.

To fix this, I made a bridge Javascipt.

First you need to make a singleton with the state of the gallery.

Code
 package me.denko.photoswipe; class GalleryState { private static final GalleryState ourInstance = new GalleryState(); static GalleryState getInstance() { return ourInstance; } private GalleryState() { } private boolean isGallery = false; public boolean isGallery() { return isGallery; } public void setGallery(boolean gallery) { isGallery = gallery; } } 


Next, the interface to call from WebView

Code
 package me.denko.photoswipe; import android.webkit.JavascriptInterface; public class JavascriptFromWebView { @JavascriptInterface public static void setGallery(boolean bool) { GalleryState.getInstance().setGallery(bool); } } 


You need to connect this interface to WebView.

 webView.addJavascriptInterface(new JavascriptFromWebView(), "callAndroid"); 

In the script for opening the gallery you need to add
 callAndroid.setGallery(true); 


In the script for closing the gallery in the photoswipe.js file

Code
 close: function() { if(!_isOpen) { return; } callAndroid.setGallery(false); //    _isOpen = false; _isDestroying = true; _shout('close'); _unbindEvents(); _showOrHide(self.currItem, null, true, self.destroy); }, 


Now when you open the gallery, the state is set to true, when closed, false. It remains only to add the handle of pressing the button back WebView.

Code
 webView.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) { if (GalleryState.getInstance().isGallery()) { webView.reload(); GalleryState.getInstance().setGallery(false); } else onBackPressed(); } return true; } }); 


Now you can open the gallery and then close it with the back button.

Full application code on github .

Criticism code is welcome. If I did something wrong, please write about it in the comments.

Source: https://habr.com/ru/post/454578/


All Articles