{"id":328020,"date":"2022-01-14T15:00:52","date_gmt":"2022-01-14T15:00:52","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=328020"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=328020","title":{"rendered":"<span>Pinch-to-zoom \u043f\u043e\u0434 \u043c\u0438\u043a\u0440\u043e\u0441\u043a\u043e\u043f\u043e\u043c<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/ae8\/74e\/741\/ae874e74112af38536e75fcc023c65f5.jpg\" width=\"3906\" height=\"2196\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ae8\/74e\/741\/ae874e74112af38536e75fcc023c65f5.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0451\u043c\u0438\u043d \u0410\u043b\u0435\u043a\u0441\u0435\u0439, \u044f Android-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0<a href=\"https:\/\/www.prequel.app\/\">Prequel<\/a>\u00a0&#8212; \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u0434\u043b\u044f \u0444\u043e\u0442\u043e \u0438 \u0432\u0438\u0434\u0435\u043e. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f Pinch-to-zoom. \u0422\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0448\u0438\u0440\u043e\u043a\u043e \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c \u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 api \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439. \u041e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u043e\u0435 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u043a\u0435\u0439\u0441\u043e\u0432, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043d\u044e\u0430\u043d\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043e\u0441\u0432\u0435\u0442\u0438\u0442\u044c \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<h3>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<ol>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442\u044c \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e View<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0443\u043c\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0433\u0440\u0430\u043d\u0438\u0446<\/p>\n<\/li>\n<li>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0443\u043c\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e \u0441\u043d\u0438\u0437\u0443 \u0438 \u0441\u0432\u0435\u0440\u0445\u0443<\/p>\n<\/li>\n<\/ol>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0431\u0443\u0434\u0435\u043c \u0437\u0443\u043c\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>ExoPlayer<\/code> \u0432 <code>PlayerView<\/code>.<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 &#8212; \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>ScaleGestureDetector<\/code>.<\/p>\n<p>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0432 <code>OnScaleGestureListener<\/code> \u043f\u0430\u0440\u0443 \u043c\u0435\u0442\u043e\u0434\u043e\u0432:<\/p>\n<pre><code class=\"kotlin\">ScaleGestureDetector(this, object : ScaleGestureDetector.OnScaleGestureListener { var totalScale = 1f  override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.pivotX = detector.focusX player_view.pivotY = detector.focusY return true }  override fun onScale(detector: ScaleGestureDetector): Boolean { totalScale *= detector.scaleFactor player_view.scale(totalScale) return true }  override fun onScaleEnd(detector: ScaleGestureDetector) = Unit }<\/code><\/pre>\n<p>\u0417\u0430\u0441\u0435\u0442\u0438\u043c \u0434\u043b\u044f <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0445\u043e\u0442\u0438\u043c \u0437\u0443\u043c\u0438\u0442\u044c, <code>TouchListener<\/code><\/p>\n<pre><code class=\"kotlin\">player_view.setOnTouchListener { _, event -> scaleGestureDetector.onTouchEvent(event) }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/54ad82859b4f385b5be2dc3dc7bace0114e3f538\">\u041f\u0435\u0440\u0432\u0430\u044f \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u0442\u043e\u0432\u0430.<\/a><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 &#8212; <code>View<\/code> \u0434\u0440\u043e\u0436\u0438\u0442.<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh3.googleusercontent.com\/ho-DszWlMoKbouqVyxdN413TtAHBMqMOLL9K6Ht_naELSlsKjIY14tBZonDLMGmZMrk=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem1.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/1p_-IDDO_G1bzi1SP5mOlKMvfjfjYirHK\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<p>\u0414\u0440\u043e\u0436\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e <code>View<\/code> \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0430\u0447\u0430. \u0412 \u043b\u043e\u0433\u0430\u0445 \u0432\u0438\u0434\u043d\u043e \u043a\u0430\u043a \u0441\u043a\u0430\u0447\u0435\u0442 <code>scaleFactor<\/code> \u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e <code>totalScale<\/code><\/p>\n<pre><code class=\"kotlin\">totalScale = 1.0 scaleFactor = 1.0 totalScale = 1.0942823 scaleFactor = 1.0942823 totalScale = 1.086125 scaleFactor = 0.9925456 totalScale = 1.1807202 scaleFactor = 1.0870942 totalScale = 1.1435295 scaleFactor = 0.96850175 totalScale = 1.2397153 scaleFactor = 1.0841131 totalScale = 1.1949267 scaleFactor = 0.9638719 <\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u043b\u0430\u0434\u0451\u043c \u043f\u043e\u0432\u0435\u0440\u0445 \u043d\u0430\u0448\u0435\u0439 <code>PlayerView<\/code> \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0443\u044e <code>View<\/code> \u0438 \u0441\u0435\u0442\u0438\u043c <code>TouchListener<\/code> \u0432 \u043d\u0435\u0451.<\/p>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/d2afe6525a35f7d20395cf781a21c4fc49109ad4\">\u0422\u0430\u043a-\u0442\u043e \u043b\u0443\u0447\u0448\u0435.<\/a><\/p>\n<p>\u041d\u043e \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442, \u0435\u0441\u043b\u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u043f\u043e\u0437\u0443\u043c\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437? \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c <code>View<\/code>, \u0430 \u043f\u043e\u0442\u043e\u043c \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043c?<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432\u0442\u043e\u0440\u043e\u0439 scale, \u0432\u0438\u0434\u0435\u043e \u0440\u0435\u0437\u043a\u043e \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f:<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh4.googleusercontent.com\/96FmqV-vNfpaOH86B_RpwGsNz_wS9Y11Qje3E6jK6QCTzDLftqFwzt5fSfmbqpwNqjk=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem2.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/12JJqVQaurnLdXZXlgkqOm8t4ROzvdwSu\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 pivot, \u0438 \u043a\u0430\u043a \u043e\u043d \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.<\/p>\n<h3>Pivot<\/h3>\n<p>Pivot &#8212; \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f zoom&#8217;\u0430 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0439.<\/p>\n<p>\u041d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043f\u0440\u0438\u043c\u0435\u0440 scale \u0432 2 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0438 <code>pivot = (2,2)<\/code><\/p>\n<figure class=\"float bordered full-width\"><img decoding=\"async\" src=\"\/img\/image-loader.svg\" height=\"1018\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5c7\/c9f\/167\/5c7c9f1676cc8da8cc54f07c8ad9f902.png\" data-width=\"598\"\/><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a &#8212; \u044d\u0442\u043e \u044d\u043a\u0440\u0430\u043d \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0440\u0430\u0441\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u043c, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0432\u0435\u0441\u044c \u044d\u043a\u0440\u0430\u043d. \u0412\u0441\u0451, \u0447\u0442\u043e \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u043c\u0438 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0433\u043e \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043d\u0435 \u0432\u0438\u0434\u043d\u043e.<\/p>\n<p>\u041a\u0430\u043a \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0438\u0437 \u0440\u0438\u0441\u0443\u043d\u043a\u0430, \u0442\u043e \u0447\u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430, \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442 scale, \u043d\u043e \u0438 \u043e\u0442 pivot. <\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u0435\u0449\u0451 \u0440\u0430\u0437 \u043d\u0430 \u043d\u0430\u0448 \u043a\u043e\u0434.<\/p>\n<pre><code class=\"kotlin\">override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.pivotX = detector.focusX player_view.pivotY = detector.focusY return true }<\/code><\/pre>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u043a\u0435\u0439\u043b\u0430 \u043c\u044b \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c pivot \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u0430\u043b\u044c\u0446\u0435\u0432. \u041d\u043e, \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u043a\u0435\u0439\u043b\u0430 \u043d\u0430\u0448\u0430 <code>View<\/code> \u0438\u043c\u0435\u043b\u0430 scale = 1, \u0442\u043e \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043d\u0430 \u0438\u043c\u0435\u0435\u0442 scale > 1 \u0438 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e <code>pivot = (pivotX, pivotY)<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0437\u0430\u0441\u043a\u0435\u0439\u043b\u0438\u043b\u0438 \u0432 2 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0438 \u043f\u043e\u0442\u043e\u043c \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043b\u0438  <code>pivot = (2,2)<\/code>, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0431\u044b \u0434\u0451\u0440\u043d\u0443\u043b\u0430\u0441\u044c, \u0440\u0435\u0437\u043a\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0432\u0448\u0438\u0441\u044c \u0438\u0437 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0451\u043d\u043d\u043e\u0433\u043e \u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435, \u0432 \u043d\u0438\u0436\u043d\u0438\u0439. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0451\u0440\u0433\u0430\u043d\u0438\u0435 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0441\u043c\u0435\u043d\u044b pivot&#8217;\u0430. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c pivot \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u043e\u0447\u043a\u0430 \u043c\u0435\u0436\u0434\u0443 \u043f\u0430\u043b\u044c\u0446\u0430\u043c\u0438 \u043e\u0441\u0442\u0430\u0432\u0430\u043b\u0430\u0441\u044c \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0439 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0443\u043c\u0430.<\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u0440\u0438\u0441\u0443\u043d\u043a\u0430, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0440\u0445\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 \u043d\u0438\u0436\u043d\u0435\u0435, \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043d\u0430 \u043e\u0434\u043d\u0443 \u043a\u043b\u0435\u0442\u043a\u0443 \u0432\u0432\u0435\u0440\u0445 \u0438 \u0432\u043b\u0435\u0432\u043e. \u0412\u043e\u043e\u0431\u0449\u0435 scale \u0441 \u043b\u044e\u0431\u044b\u043c pivot&#8217;\u043e\u043c &#8212; \u044d\u0442\u043e scale \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c pivot (\u0446\u0435\u043d\u0442\u0440 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438) + \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0441\u0434\u0432\u0438\u0433 \u043f\u043e \u043e\u0441\u044f\u043c X \u0438 Y.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u0451\u0440\u0433\u0430\u043d\u0438\u0439 \u0438 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 pivot, \u043d\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 pivot&#8217;\u0430.<\/p>\n<h3>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 pivot&#8217;\u0430<\/h3>\n<p>\u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430\u043f\u043e\u043c\u043d\u0438\u043c, \u0447\u0442\u043e \u0442\u0430\u0447\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f <code>View<\/code> \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u043e\u043a\u0443\u0441 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0432 \u0435\u0451 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0441\u043a\u0435\u0439\u043b\u0438\u043c.<\/p>\n<p>\u041f\u0443\u0441\u0442\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u043a\u0435\u0439\u043b \u0432 3 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0445\u043e\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0435\u0439\u043b \u0441 <code>pivot = (4,4)<\/code> \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<figure class=\"float full-width\"><img decoding=\"async\" src=\"\/img\/image-loader.svg\" height=\"706\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b0b\/5d0\/9de\/b0b5d09deacc3546705b3209986066f1.png\" data-width=\"768\"\/><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u044d\u043a\u0440\u0430\u043d\u0430 \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b View.<\/p>\n<p>\u0415\u0441\u043b\u0438 pivot \u0438\u043c\u0435\u043b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX, pivotY)<\/code>, \u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0441\u043a\u0435\u0439\u043b\u0430 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX * scale, pivotY * scale)<\/code><em> <\/em>\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u0447\u0430\u043b\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0441\u0434\u0432\u0438\u043d\u0443\u043b\u043e\u0441\u044c \u043d\u0430 <code>pivotX * (scale-1), pivotY*(scale-1)<\/code>, \u0442\u043e \u0435\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u0444\u043e\u043a\u0443\u0441 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(focusX, focusY)<\/code> \u0442\u043e \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u043e\u0439 <code>View<\/code> \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430 \u043f\u043e X \u0431\u0443\u0434\u0435\u0442 <code>focusX + pivotX * (scale - 1)<\/code>,  \u0434\u043b\u044f Y \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u0432\u044b\u0447\u0435\u0441\u0442\u044c translation \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0434\u043e \u0441\u043a\u0435\u0439\u043b\u0430, \u0432\u0441\u0451 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043d\u0430 scale.<\/p>\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"kotlin\">val actualPivot = PointF( (detector.focusX - translationX + pivotX * (totalScale - 1)) \/ totalScale, (detector.focusY - translationY + pivotY * (totalScale - 1)) \/ totalScale, )<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435: <\/p>\n<p><code>focusX = 4, pivotX = 1, scale = 3, translationX = 0<\/code><\/p>\n<p> <code>(4 + (3 - 1)) \/ 3 = 2<\/code> <\/p>\n<p>\u0412\u0441\u0451 \u0432\u0435\u0440\u043d\u043e, \u043d\u0438\u0436\u043d\u0438\u0439 \u0443\u0433\u043e\u043b \u0441\u0438\u043d\u0435\u0433\u043e \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430 &#8212; \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0430 <code>(2,2)<\/code> &#8212; \u0432 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<p>\u041d\u043e, \u043a\u0430\u043a \u043c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c pivot, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0430\u0447\u043e\u043a, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0435\u0433\u043e \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430 \u0441\u0447\u0451\u0442 translation. \u041d\u0430 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0435 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u043d\u0430\u0448\u0443 <code>View<\/code>? \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043a\u0430\u043a \u0441\u0434\u0432\u0438\u0433\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u0438 \u043f\u0440\u0438 \u0441\u043a\u0435\u0439\u043b\u0435 \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c pivot.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c <code>scale = 2<\/code> \u0441 <code>pivot = (1,1)<\/code><\/p>\n<figure class=\"float full-width\"><img decoding=\"async\" src=\"\/img\/image-loader.svg\" height=\"496\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/988\/330\/717\/988330717cfaff1ec4ac6caa3e7f7347.png\" data-width=\"602\"\/><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041b\u044e\u0431\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0435\u0451 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043e pivot \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u043e\u0441\u044c \u0432 scale \u0440\u0430\u0437. \u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0447\u0451\u0440\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u043b\u0430\u0441\u044c \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 <code>(1,2)<\/code> \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(1,3)<\/code> \u0442\u043e \u0435\u0441\u0442\u044c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e = 1, \u0430 \u0441\u0442\u0430\u043b\u043e <code>1 * scale = 2<\/code>.<\/p>\n<p>\u041e\u0431\u0449\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0425 (\u0434\u043b\u044f \u0442\u043e\u0447\u0435\u043a \u043f\u0440\u0430\u0432\u0435\u0435 pivot&#8217;\u0430 \u0438 scale > 1) <\/p>\n<p><code>x1<\/code> \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u0442 \u0432 <code>x1 + (x1 - pivotX) * (scale - 1)<\/code><\/p>\n<p>\u0412\u0435\u0440\u043d\u0451\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044e: <\/p>\n<p>\u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX, pivotY)<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043a\u0435\u0439\u043b\u0438\u043b\u0438 \u0441 \u043d\u0430\u0448\u0438\u043c <code>actualPivot<\/code>, \u0442\u043e \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0441\u0434\u0432\u0438\u043d\u0443\u043b\u0430\u0441\u044c \u0431\u044b \u043d\u0430 <code>(actualPivot.x - pivotX)*(scale-1)<\/code>. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0442\u0430\u043a\u043e\u0435 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043d\u0430\u0434\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c translation, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0434\u0432\u0438\u0433. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0442\u0435\u043f\u0435\u0440\u044c <code>onScaleBegin<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"kotlin\">override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.run { val actualPivot = PointF( (detector.focusX - translationX + pivotX * (totalScale - 1)) \/ totalScale, (detector.focusY - translationY + pivotY * (totalScale - 1)) \/ totalScale, ) translationX -= (pivotX - actualPivot.x) * (totalScale - 1) translationY -= (pivotY - actualPivot.y) * (totalScale - 1) setPivot(actualPivot) } return true }  <\/code><\/pre>\n<h4>\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>TouchListener<\/code> \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0439 <code>View<\/code>. \u0422\u0443\u0442 \u0432\u0441\u0451 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e. \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0434\u0430\u043b\u0435\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c \u043f\u043e <code>ACTION_MOVE<\/code><\/p>\n<pre><code class=\"kotlin\">when (event.actionMasked) {   MotionEvent.ACTION_DOWN -> {       prevX = event.x       prevY = event.y   }           MotionEvent.ACTION_MOVE -> {       moveStarted = true       contentView?.run {         translationX += (event.x - prevX)         translationY += (event.y - prevY)       }       prevX = event.x       prevY = event.y   }    MotionEvent.ACTION_UP -> {       if (!moveStarted) return false       reset()   } }<\/code><\/pre>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0445\u0438\u0442\u0440\u043e\u0441\u0442\u044c \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043c\u0443\u043b\u044c\u0442\u0438\u0442\u0430\u0447\u0430. \u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 <code>event<\/code> \u0434\u043b\u044f <code>ACTION_DOWN<\/code> &#8212; \u044d\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0430\u043b\u044c\u0446\u0430, \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u0438 \u043f\u043e\u0442\u043e\u043c \u0443\u0431\u0435\u0440\u0451\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u0438 \u043d\u0430\u0447\u043d\u0451\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c, \u0442\u043e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043d\u0443 \u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u043e\u043b\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u043b\u044c\u0446\u0430 \u043a\u0430\u0441\u0430\u044e\u0442\u0441\u044f <code>View<\/code>. \u0418\u0442\u043e\u0433\u043e:<\/p>\n<pre><code class=\"kotlin\">when (event.actionMasked) {   MotionEvent.ACTION_DOWN -> {       prevX = event.x       prevY = event.y   }            MotionEvent.ACTION_POINTER_UP -> {       if (event.actionIndex == 0) {         try {           prevX = event.getX(1)           prevY = event.getY(1)         } catch (e: Exception) {         }       }   }    MotionEvent.ACTION_MOVE -> {       if (event.pointerCount > 1) {         prevX = event.x         prevY = event.y         return false       }       moveStarted = true       contentView?.run {         translationX += (event.x - prevX)         translationY += (event.y - prevY)       }       prevX = event.x       prevY = event.y   }      MotionEvent.ACTION_UP -> {       if (!moveStarted) return false       reset()   } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0440\u0440\u0435\u043a\u0446\u0438\u044e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"kotlin\">private fun translateToOriginalRect() { getContentViewTranslation().takeIf { it != PointF(0f, 0f) }?.let { translation -> player_view?.let { view -> view.animateWithDetach() .translationXBy(translation.x) .translationYBy(translation.y) .apply { duration = CORRECT_LOCATION_ANIMATION_DURATION } .start()             }         } }  private fun getContentViewTranslation(): PointF {     return player_view.run {         originContentRect.let { rect ->             val array = IntArray(2)             getLocationOnScreen(array)             PointF(                 when {                     array[0] > rect.left -> rect.left - array[0].toFloat()                     array[0] + width * scaleX &lt; rect.right -> rect.right - (array[0] + width * scaleX)                     else -> 0f                 },                 when {                     array[1] > rect.top -> rect.top - array[1].toFloat()                     array[1] + height * scaleY &lt; rect.bottom -> rect.bottom - (array[1] + height * scaleY)                     else -> 0f                 }             )         }     } }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/6bf65c78d8637b8559ddbbdb3597ebb69bcb0e88\">\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c<\/a>, \u0442\u0435\u0441\u0442\u0438\u043c \u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 &#8212; <code>View<\/code> \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435:<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh5.googleusercontent.com\/fwAYOUC1y_PmIFx50HSzp2Fn7woEyjU-b42Jeog0v4B6Jpw7iXIaEXuBQpJTEC2JFfs=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem3.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/18W9xSxg0HW6PM1e0lRsmm-7II26L4lFP\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u043c\u044b \u0441\u043a\u0435\u0439\u043b\u0438\u043c \u0432 2 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (0,0)<\/code>. \u0422\u043e\u0433\u0434\u0430 \u0432\u0441\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f \u0432\u043d\u0438\u0437 \u0438 \u0432\u043f\u0440\u0430\u0432\u043e. \u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u0443\u0434\u0435\u043c \u0441\u043a\u0435\u0439\u043b\u0438\u0442\u044c \u0432 1\/2 c pivot \u0432 \u043f\u0440\u0430\u0432\u043e\u043c \u043d\u0438\u0436\u043d\u0435\u043c \u0443\u0433\u043b\u0443. \u0422\u043e\u0433\u0434\u0430 \u0432\u0441\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0442\u0441\u044f \u0438 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0430\u0442\u044c\u0441\u044f \u043a pivot, \u0442\u043e \u0435\u0441\u0442\u044c \u0442\u0430\u043a\u0436\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f \u0432\u043d\u0438\u0437 \u0438 \u0432\u043f\u0440\u0430\u0432\u043e.  \u0418 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0442\u043e\u0442 \u0436\u0435 \u0440\u0430\u0437\u043c\u0435\u0440, \u043d\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043c\u0435\u0449\u0435\u043d\u0430 \u0438\u0437-\u0437\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0441\u0442\u0438 pivot.<\/p>\n<h3>\u041a\u043e\u0440\u0440\u0435\u043a\u0446\u0438\u044f pivot<\/h3>\n<p>\u041f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043f\u043e\u043b\u043d\u043e\u0439 \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0446\u0438\u044e, \u043e\u043f\u044f\u0442\u044c \u0436\u0435 \u0437\u0430 \u0441\u0447\u0451\u0442 translation. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 <code>getContentViewTranslation()<\/code> \u0438 \u0441\u0434\u0432\u0438\u043d\u0435\u043c <code>View<\/code>, \u0447\u0442\u043e\u0431\u044b  \u043e\u043d\u0430 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0432 \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u0445. \u0422\u0435\u043f\u0435\u0440\u044c <code>onScale<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"kotlin\">override fun onScale(detector: ScaleGestureDetector): Boolean {   totalScale *= detector.scaleFactor   totalScale = totalScale.coerceIn(MIN_SCALE_FACTOR, MAX_SCALE_FACTOR)                  player_view.run {         scale(totalScale)         getContentViewTranslation().run {                translationX += x                translationY += y         }   }   return true }                          <\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/master\">\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/a><\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh6.googleusercontent.com\/HxnncqhDyuVqDIAA0XRrtHhqv6s2XCs7x7uu8GPcH5oCLZrpBmw2gjH6NACrX8B92-Q=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>result.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/1XlexwdtvPQ1RJx2W_J77iX1fOY9xQmQe\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<h3>\u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p><code>ScaleGestureDetector<\/code> \u0434\u0430\u0451\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043b\u0435\u0433\u043a\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0448\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u0448\u0430\u0433 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f Pinch-to-zoom. \u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0443\u0436\u0435 \u0437\u0430\u0437\u0443\u043c\u043b\u0435\u043d\u043d\u043e\u0439 <code>View<\/code> \u0438\u043c\u0435\u0435\u0442 \u0440\u044f\u0434 \u043d\u044e\u0430\u043d\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u044f \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u043c\u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u0441\u043a\u0440\u044b\u0442\u044c \u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442\u044c  \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f. <\/p>\n<p>\u0412\u0441\u0435\u043c \u0434\u043e\u0431\u0440\u0430 \u0438 \u043f\u043b\u0430\u0432\u043d\u043e\u0433\u043e Pinch-to-zoom!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/prequel\/blog\/645463\/\"> https:\/\/habr.com\/ru\/company\/prequel\/blog\/645463\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0451\u043c\u0438\u043d \u0410\u043b\u0435\u043a\u0441\u0435\u0439, \u044f Android-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0<a href=\"https:\/\/www.prequel.app\/\">Prequel<\/a>\u00a0&#8212; \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0435 \u0434\u043b\u044f \u0444\u043e\u0442\u043e \u0438 \u0432\u0438\u0434\u0435\u043e. \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f Pinch-to-zoom. \u0422\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0448\u0438\u0440\u043e\u043a\u043e \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u043c \u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439 api \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439. \u041e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u043e\u0435 \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u043a\u0435\u0439\u0441\u043e\u0432, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043d\u044e\u0430\u043d\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043e\u0441\u0432\u0435\u0442\u0438\u0442\u044c \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<h3>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/h3>\n<ol>\n<li>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442\u044c \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e View<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0437\u0443\u043c\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u043d\u043e \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0433\u0440\u0430\u043d\u0438\u0446<\/p>\n<\/li>\n<li>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0443\u043c\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e \u0441\u043d\u0438\u0437\u0443 \u0438 \u0441\u0432\u0435\u0440\u0445\u0443<\/p>\n<\/li>\n<\/ol>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0431\u0443\u0434\u0435\u043c \u0437\u0443\u043c\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>ExoPlayer<\/code> \u0432 <code>PlayerView<\/code>.<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 &#8212; \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>ScaleGestureDetector<\/code>.<\/p>\n<p>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0432 <code>OnScaleGestureListener<\/code> \u043f\u0430\u0440\u0443 \u043c\u0435\u0442\u043e\u0434\u043e\u0432:<\/p>\n<pre><code class=\"kotlin\">ScaleGestureDetector(this, object : ScaleGestureDetector.OnScaleGestureListener { var totalScale = 1f  override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.pivotX = detector.focusX player_view.pivotY = detector.focusY return true }  override fun onScale(detector: ScaleGestureDetector): Boolean { totalScale *= detector.scaleFactor player_view.scale(totalScale) return true }  override fun onScaleEnd(detector: ScaleGestureDetector) = Unit }<\/code><\/pre>\n<p>\u0417\u0430\u0441\u0435\u0442\u0438\u043c \u0434\u043b\u044f <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0445\u043e\u0442\u0438\u043c \u0437\u0443\u043c\u0438\u0442\u044c, <code>TouchListener<\/code><\/p>\n<pre><code class=\"kotlin\">player_view.setOnTouchListener { _, event -> scaleGestureDetector.onTouchEvent(event) }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/54ad82859b4f385b5be2dc3dc7bace0114e3f538\">\u041f\u0435\u0440\u0432\u0430\u044f \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u0442\u043e\u0432\u0430.<\/a><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 &#8212; <code>View<\/code> \u0434\u0440\u043e\u0436\u0438\u0442.<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh3.googleusercontent.com\/ho-DszWlMoKbouqVyxdN413TtAHBMqMOLL9K6Ht_naELSlsKjIY14tBZonDLMGmZMrk=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem1.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/1p_-IDDO_G1bzi1SP5mOlKMvfjfjYirHK\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<p>\u0414\u0440\u043e\u0436\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e <code>View<\/code> \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0430\u0447\u0430. \u0412 \u043b\u043e\u0433\u0430\u0445 \u0432\u0438\u0434\u043d\u043e \u043a\u0430\u043a \u0441\u043a\u0430\u0447\u0435\u0442 <code>scaleFactor<\/code> \u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e <code>totalScale<\/code><\/p>\n<pre><code class=\"kotlin\">totalScale = 1.0 scaleFactor = 1.0 totalScale = 1.0942823 scaleFactor = 1.0942823 totalScale = 1.086125 scaleFactor = 0.9925456 totalScale = 1.1807202 scaleFactor = 1.0870942 totalScale = 1.1435295 scaleFactor = 0.96850175 totalScale = 1.2397153 scaleFactor = 1.0841131 totalScale = 1.1949267 scaleFactor = 0.9638719 <\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043f\u0440\u043e\u0441\u0442\u043e \u043a\u043b\u0430\u0434\u0451\u043c \u043f\u043e\u0432\u0435\u0440\u0445 \u043d\u0430\u0448\u0435\u0439 <code>PlayerView<\/code> \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0443\u044e <code>View<\/code> \u0438 \u0441\u0435\u0442\u0438\u043c <code>TouchListener<\/code> \u0432 \u043d\u0435\u0451.<\/p>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/d2afe6525a35f7d20395cf781a21c4fc49109ad4\">\u0422\u0430\u043a-\u0442\u043e \u043b\u0443\u0447\u0448\u0435.<\/a><\/p>\n<p>\u041d\u043e \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442, \u0435\u0441\u043b\u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u043f\u043e\u0437\u0443\u043c\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437? \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c <code>View<\/code>, \u0430 \u043f\u043e\u0442\u043e\u043c \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043c?<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432\u0442\u043e\u0440\u043e\u0439 scale, \u0432\u0438\u0434\u0435\u043e \u0440\u0435\u0437\u043a\u043e \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f:<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh4.googleusercontent.com\/96FmqV-vNfpaOH86B_RpwGsNz_wS9Y11Qje3E6jK6QCTzDLftqFwzt5fSfmbqpwNqjk=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem2.mp4<\/span><\/div>\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/12JJqVQaurnLdXZXlgkqOm8t4ROzvdwSu\/view?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer nofollow\" class=\"embed__caption-link\">drive.google.com<\/a><\/div>\n<\/div>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 pivot, \u0438 \u043a\u0430\u043a \u043e\u043d \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.<\/p>\n<h3>Pivot<\/h3>\n<p>Pivot &#8212; \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f zoom&#8217;\u0430 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0439.<\/p>\n<p>\u041d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043f\u0440\u0438\u043c\u0435\u0440 scale \u0432 2 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0438 <code>pivot = (2,2)<\/code><\/p>\n<figure class=\"float bordered full-width\"><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a &#8212; \u044d\u0442\u043e \u044d\u043a\u0440\u0430\u043d \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430. \u0414\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0440\u0430\u0441\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u043c, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0432\u0435\u0441\u044c \u044d\u043a\u0440\u0430\u043d. \u0412\u0441\u0451, \u0447\u0442\u043e \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u043c\u0438 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e\u0433\u043e \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043d\u0435 \u0432\u0438\u0434\u043d\u043e.<\/p>\n<p>\u041a\u0430\u043a \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0438\u0437 \u0440\u0438\u0441\u0443\u043d\u043a\u0430, \u0442\u043e \u0447\u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430, \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442 scale, \u043d\u043e \u0438 \u043e\u0442 pivot. <\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u0435\u0449\u0451 \u0440\u0430\u0437 \u043d\u0430 \u043d\u0430\u0448 \u043a\u043e\u0434.<\/p>\n<pre><code class=\"kotlin\">override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.pivotX = detector.focusX player_view.pivotY = detector.focusY return true }<\/code><\/pre>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u043a\u0435\u0439\u043b\u0430 \u043c\u044b \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c pivot \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u0430\u043b\u044c\u0446\u0435\u0432. \u041d\u043e, \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u043a\u0435\u0439\u043b\u0430 \u043d\u0430\u0448\u0430 <code>View<\/code> \u0438\u043c\u0435\u043b\u0430 scale = 1, \u0442\u043e \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043d\u0430 \u0438\u043c\u0435\u0435\u0442 scale > 1 \u0438 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e <code>pivot = (pivotX, pivotY)<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0437\u0430\u0441\u043a\u0435\u0439\u043b\u0438\u043b\u0438 \u0432 2 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0438 \u043f\u043e\u0442\u043e\u043c \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u043b\u0438  <code>pivot = (2,2)<\/code>, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0431\u044b \u0434\u0451\u0440\u043d\u0443\u043b\u0430\u0441\u044c, \u0440\u0435\u0437\u043a\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0432\u0448\u0438\u0441\u044c \u0438\u0437 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0451\u043d\u043d\u043e\u0433\u043e \u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435, \u0432 \u043d\u0438\u0436\u043d\u0438\u0439. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0451\u0440\u0433\u0430\u043d\u0438\u0435 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437-\u0437\u0430 \u0441\u043c\u0435\u043d\u044b pivot&#8217;\u0430. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c pivot \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u043e\u0447\u043a\u0430 \u043c\u0435\u0436\u0434\u0443 \u043f\u0430\u043b\u044c\u0446\u0430\u043c\u0438 \u043e\u0441\u0442\u0430\u0432\u0430\u043b\u0430\u0441\u044c \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u043e\u0439 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0443\u043c\u0430.<\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u0440\u0438\u0441\u0443\u043d\u043a\u0430, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0440\u0445\u043d\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 \u043d\u0438\u0436\u043d\u0435\u0435, \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u043d\u0430 \u043e\u0434\u043d\u0443 \u043a\u043b\u0435\u0442\u043a\u0443 \u0432\u0432\u0435\u0440\u0445 \u0438 \u0432\u043b\u0435\u0432\u043e. \u0412\u043e\u043e\u0431\u0449\u0435 scale \u0441 \u043b\u044e\u0431\u044b\u043c pivot&#8217;\u043e\u043c &#8212; \u044d\u0442\u043e scale \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c pivot (\u0446\u0435\u043d\u0442\u0440 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438) + \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0441\u0434\u0432\u0438\u0433 \u043f\u043e \u043e\u0441\u044f\u043c X \u0438 Y.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u0451\u0440\u0433\u0430\u043d\u0438\u0439 \u0438 \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 pivot, \u043d\u0430\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0430 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 pivot&#8217;\u0430.<\/p>\n<h3>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 pivot&#8217;\u0430<\/h3>\n<p>\u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043d\u0430\u043f\u043e\u043c\u043d\u0438\u043c, \u0447\u0442\u043e \u0442\u0430\u0447\u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f <code>View<\/code> \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u043e\u043a\u0443\u0441 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0432 \u0435\u0451 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>View<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0441\u043a\u0435\u0439\u043b\u0438\u043c.<\/p>\n<p>\u041f\u0443\u0441\u0442\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0441\u043a\u0435\u0439\u043b \u0432 3 \u0440\u0430\u0437\u0430 \u0441 <code>pivot = (1,1)<\/code> \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u0445\u043e\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0435\u0439\u043b \u0441 <code>pivot = (4,4)<\/code> \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 \u044d\u043a\u0440\u0430\u043d\u0430<\/p>\n<figure class=\"float full-width\"><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u044d\u043a\u0440\u0430\u043d\u0430 \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b View.<\/p>\n<p>\u0415\u0441\u043b\u0438 pivot \u0438\u043c\u0435\u043b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX, pivotY)<\/code>, \u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u0441\u043a\u0435\u0439\u043b\u0430 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX * scale, pivotY * scale)<\/code><em> <\/em>\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u0447\u0430\u043b\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0441\u0434\u0432\u0438\u043d\u0443\u043b\u043e\u0441\u044c \u043d\u0430 <code>pivotX * (scale-1), pivotY*(scale-1)<\/code>, \u0442\u043e \u0435\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u0444\u043e\u043a\u0443\u0441 \u0434\u0435\u0442\u0435\u043a\u0442\u043e\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(focusX, focusY)<\/code> \u0442\u043e \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u043e\u0439 <code>View<\/code> \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430 \u043f\u043e X \u0431\u0443\u0434\u0435\u0442 <code>focusX + pivotX * (scale - 1)<\/code>,  \u0434\u043b\u044f Y \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u0432\u044b\u0447\u0435\u0441\u0442\u044c translation \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0434\u043e \u0441\u043a\u0435\u0439\u043b\u0430, \u0432\u0441\u0451 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043d\u0430 scale.<\/p>\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"kotlin\">val actualPivot = PointF( (detector.focusX - translationX + pivotX * (totalScale - 1)) \/ totalScale, (detector.focusY - translationY + pivotY * (totalScale - 1)) \/ totalScale, )<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435: <\/p>\n<p><code>focusX = 4, pivotX = 1, scale = 3, translationX = 0<\/code><\/p>\n<p> <code>(4 + (3 - 1)) \/ 3 = 2<\/code> <\/p>\n<p>\u0412\u0441\u0451 \u0432\u0435\u0440\u043d\u043e, \u043d\u0438\u0436\u043d\u0438\u0439 \u0443\u0433\u043e\u043b \u0441\u0438\u043d\u0435\u0433\u043e \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430 &#8212; \u044d\u0442\u043e \u0442\u043e\u0447\u043a\u0430 <code>(2,2)<\/code> &#8212; \u0432 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438.<\/p>\n<p>\u041d\u043e, \u043a\u0430\u043a \u043c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c pivot, \u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0430\u0447\u043e\u043a, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0435\u0433\u043e \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430 \u0441\u0447\u0451\u0442 translation. \u041d\u0430 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0435 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0441\u0434\u0432\u0438\u043d\u0443\u0442\u044c \u043d\u0430\u0448\u0443 <code>View<\/code>? \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043a\u0430\u043a \u0441\u0434\u0432\u0438\u0433\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u0447\u043a\u0438 \u043f\u0440\u0438 \u0441\u043a\u0435\u0439\u043b\u0435 \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c pivot.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c <code>scale = 2<\/code> \u0441 <code>pivot = (1,1)<\/code><\/p>\n<figure class=\"float full-width\"><figcaption><\/figcaption><\/figure>\n<\/p>\n<p>\u041b\u044e\u0431\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0435\u0451 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043e pivot \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u043e\u0441\u044c \u0432 scale \u0440\u0430\u0437. \u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0447\u0451\u0440\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u043b\u0430\u0441\u044c \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 <code>(1,2)<\/code> \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(1,3)<\/code> \u0442\u043e \u0435\u0441\u0442\u044c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0431\u044b\u043b\u043e = 1, \u0430 \u0441\u0442\u0430\u043b\u043e <code>1 * scale = 2<\/code>.<\/p>\n<p>\u041e\u0431\u0449\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0425 (\u0434\u043b\u044f \u0442\u043e\u0447\u0435\u043a \u043f\u0440\u0430\u0432\u0435\u0435 pivot&#8217;\u0430 \u0438 scale > 1) <\/p>\n<p><code>x1<\/code> \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u0442 \u0432 <code>x1 + (x1 - pivotX) * (scale - 1)<\/code><\/p>\n<p>\u0412\u0435\u0440\u043d\u0451\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044e: <\/p>\n<p>\u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b <code>(pivotX, pivotY)<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043a\u0435\u0439\u043b\u0438\u043b\u0438 \u0441 \u043d\u0430\u0448\u0438\u043c <code>actualPivot<\/code>, \u0442\u043e \u043d\u0435\u043f\u043e\u0434\u0432\u0438\u0436\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0441\u0434\u0432\u0438\u043d\u0443\u043b\u0430\u0441\u044c \u0431\u044b \u043d\u0430 <code>(actualPivot.x - pivotX)*(scale-1)<\/code>. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0438\u043c\u0435\u043d\u043d\u043e \u043d\u0430 \u0442\u0430\u043a\u043e\u0435 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043d\u0430\u0434\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c translation, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0434\u0432\u0438\u0433. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0442\u0435\u043f\u0435\u0440\u044c <code>onScaleBegin<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"kotlin\">override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { player_view.run { val actualPivot = PointF( (detector.focusX - translationX + pivotX * (totalScale - 1)) \/ totalScale, (detector.focusY - translationY + pivotY * (totalScale - 1)) \/ totalScale, ) translationX -= (pivotX - actualPivot.x) * (totalScale - 1) translationY -= (pivotY - actualPivot.y) * (totalScale - 1) setPivot(actualPivot) } return true }  <\/code><\/pre>\n<h4>\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>TouchListener<\/code> \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0439 <code>View<\/code>. \u0422\u0443\u0442 \u0432\u0441\u0451 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e. \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0434\u0430\u043b\u0435\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0442\u044c \u043f\u043e <code>ACTION_MOVE<\/code><\/p>\n<pre><code class=\"kotlin\">when (event.actionMasked) {   MotionEvent.ACTION_DOWN -> {       prevX = event.x       prevY = event.y   }           MotionEvent.ACTION_MOVE -> {       moveStarted = true       contentView?.run {         translationX += (event.x - prevX)         translationY += (event.y - prevY)       }       prevX = event.x       prevY = event.y   }    MotionEvent.ACTION_UP -> {       if (!moveStarted) return false       reset()   } }<\/code><\/pre>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0445\u0438\u0442\u0440\u043e\u0441\u0442\u044c \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043c\u0443\u043b\u044c\u0442\u0438\u0442\u0430\u0447\u0430. \u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 <code>event<\/code> \u0434\u043b\u044f <code>ACTION_DOWN<\/code> &#8212; \u044d\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0430\u043b\u044c\u0446\u0430, \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0432\u0442\u043e\u0440\u043e\u0439 \u0438 \u043f\u043e\u0442\u043e\u043c \u0443\u0431\u0435\u0440\u0451\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u0438 \u043d\u0430\u0447\u043d\u0451\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c, \u0442\u043e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043d\u0443 \u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u043e\u043b\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u043b\u044c\u0446\u0430 \u043a\u0430\u0441\u0430\u044e\u0442\u0441\u044f <code>View<\/code>. \u0418\u0442\u043e\u0433\u043e:<\/p>\n<pre><code class=\"kotlin\">when (event.actionMasked) {   MotionEvent.ACTION_DOWN -> {       prevX = event.x       prevY = event.y   }            MotionEvent.ACTION_POINTER_UP -> {       if (event.actionIndex == 0) {         try {           prevX = event.getX(1)           prevY = event.getY(1)         } catch (e: Exception) {         }       }   }    MotionEvent.ACTION_MOVE -> {       if (event.pointerCount > 1) {         prevX = event.x         prevY = event.y         return false       }       moveStarted = true       contentView?.run {         translationX += (event.x - prevX)         translationY += (event.y - prevY)       }       prevX = event.x       prevY = event.y   }      MotionEvent.ACTION_UP -> {       if (!moveStarted) return false       reset()   } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u0440\u0440\u0435\u043a\u0446\u0438\u044e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044e \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"kotlin\">private fun translateToOriginalRect() { getContentViewTranslation().takeIf { it != PointF(0f, 0f) }?.let { translation -> player_view?.let { view -> view.animateWithDetach() .translationXBy(translation.x) .translationYBy(translation.y) .apply { duration = CORRECT_LOCATION_ANIMATION_DURATION } .start()             }         } }  private fun getContentViewTranslation(): PointF {     return player_view.run {         originContentRect.let { rect ->             val array = IntArray(2)             getLocationOnScreen(array)             PointF(                 when {                     array[0] > rect.left -> rect.left - array[0].toFloat()                     array[0] + width * scaleX &lt; rect.right -> rect.right - (array[0] + width * scaleX)                     else -> 0f                 },                 when {                     array[1] > rect.top -> rect.top - array[1].toFloat()                     array[1] + height * scaleY &lt; rect.bottom -> rect.bottom - (array[1] + height * scaleY)                     else -> 0f                 }             )         }     } }<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Prequel-Inc\/ZoomUnderMicroscope\/tree\/6bf65c78d8637b8559ddbbdb3597ebb69bcb0e88\">\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c<\/a>, \u0442\u0435\u0441\u0442\u0438\u043c \u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u043c\u0441\u044f \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 &#8212; <code>View<\/code> \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435:<\/p>\n<div class=\"embed_link\">\n<div class=\"embed__thumb\" style=\"background-image: url(&quot;https:\/\/lh5.googleusercontent.com\/fwAYOUC1y_PmIFx50HSzp2Fn7woEyjU-b42Jeog0v4B6Jpw7iXIaEXuBQpJTEC2JFfs=w1200-h630-p&quot;);\"><\/div>\n<div class=\"embed__caption\">\n<div class=\"embed__caption-title\"><span>problem3.m<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-328020","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/328020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=328020"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/328020\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=328020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=328020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=328020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}