设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间08 现在时间08 缓存数据 生活如海,宽容作舟,泛舟于海,方知海之宽阔;生活如歌,宽容是曲,和曲而歌,方知生活甜美。早安

生活如海,宽容作舟,泛舟于海,方知海之宽阔;生活如歌,宽容是曲,和曲而歌,方知生活甜美。早安

查看: 620|回复: 0

Android仿微信聊天图片的放大缩小功能

[复制链接]

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
31
主题
29
精华
0
金钱
99
积分
60
注册时间
2023-10-4
最后登录
2025-5-31

发表于 2025-5-31 06:46:08 | 显示全部楼层 |阅读模式
一、前言

经常会遇到类似微信聊天图片点击放大缩小的效果,从点击位置的图片放大,再点击缩小回去原位置的效果,查找过网上代码,不过可参考比较少,这里将Android官方代码进行略作修改有了以下代码。
效果如下:
1.gif


二、核心源码
  1. item_zoom.xml
复制代码
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"
  4.     xmlns:tools="http://schemas.android.com/tools"
  5.     android:layout_width="wrap_content"
  6.     android:layout_height="wrap_content">
  7.     <ImageButton
  8.         android:id="@+id/thumb_button_1"
  9.         android:layout_width="wrap_content"
  10.         android:layout_height="wrap_content"
  11.         android:layout_marginRight="1dp"
  12.         android:contentDescription="@string/description_image_1"
  13.         android:scaleType="centerCrop"
  14.         android:src="@drawable/ic_main_img" />
  15. </FrameLayout>
复制代码
  1. activity_zoom_list.xml
复制代码
  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:app="http://schemas.android.com/apk/res-auto"
  3.     xmlns:tools="http://schemas.android.com/tools"
  4.     android:id="@+id/container"
  5.     android:layout_width="match_parent"
  6.     android:layout_height="match_parent">
  7.     <androidx.recyclerview.widget.RecyclerView
  8.         android:id="@+id/rv_room"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content"
  11.         android:orientation="vertical"
  12.         app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  13.         tools:itemCount="3"
  14.         tools:listitem="@layout/item_zoom"/>
  15.     <!-- This initially hidden ImageView holds the zoomed version of
  16.          the preceding images. Without transformations applied, it fills the entire
  17.          screen. To achieve the zoom animation, this view's bounds are animated
  18.          from the bounds of the preceding thumbnail button to its final laid-out
  19.          bounds.
  20.          -->
  21.     <ImageView
  22.         android:id="@+id/expanded_image"
  23.         android:layout_width="match_parent"
  24.         android:layout_height="match_parent"
  25.         android:visibility="invisible"
  26.         android:contentDescription="@string/description_zoom_touch_close" />
  27. </FrameLayout>
复制代码
  1. ZoomAdapter.kt
复制代码
  1. class ZoomAdapter: RecyclerView.Adapter<ZoomAdapter.VH>() {
  2.     var itemClick: ( (View) -> Unit ) ?= null
  3.     inner class VH(binding: ItemZoomBinding): RecyclerView.ViewHolder(binding.root)
  4.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
  5.         val layoutInflater = LayoutInflater.from(parent.context)
  6.         val binding = ItemZoomBinding.inflate(layoutInflater,parent,false)
  7.         binding.thumbButton1.setOnClickListener {
  8.             itemClick?.invoke(it)
  9.         }
  10.         return VH(binding)
  11.     }
  12.     override fun onBindViewHolder(holder: VH, position: Int) {
  13.     }
  14.     override fun getItemCount(): Int = 3
  15. }
复制代码
  1. ZoomActivity.kt
复制代码
  1. /**
  2. * 仿写微信应用中图片放大缩小效果
  3. */
  4. class ZoomActivity: FragmentActivity() {
  5.     // Hold a reference to the current animator so that it can be canceled
  6.     // midway.
  7.     private var currentAnimator: Animator? = null
  8.     // The system "short" animation time duration in milliseconds. This duration
  9.     // is ideal for subtle animations or animations that occur frequently.
  10.     private var shortAnimationDuration: Int = 0
  11.     private val binding: ActivityZoomListBinding by lazy {
  12.         ActivityZoomListBinding.inflate(layoutInflater)
  13.     }
  14.     private val adapter = ZoomAdapter()
  15.     override fun onCreate(savedInstanceState: Bundle?) {
  16.         super.onCreate(savedInstanceState)
  17.         setContentView(binding.root)
  18.         // Retrieve and cache the system's default "short" animation time.
  19.         shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
  20.         initView()
  21.     }
  22.     private fun initView(){
  23.         binding.rvRoom.adapter = adapter
  24.         // Hook up taps on the thumbnail views.
  25.         adapter.itemClick = {
  26.             zoomImageFromThumb(it, R.drawable.ic_main_img)
  27.         }
  28.     }
  29.     private fun zoomImageFromThumb(thumbView: View, imageResId: Int) {
  30.         // If there's an animation in progress, cancel it immediately and
  31.         // proceed with this one.
  32.         currentAnimator?.cancel()
  33.         // Load the high-resolution "zoomed-in" image.
  34.         binding.expandedImage.setImageResource(imageResId)
  35.         // Calculate the starting and ending bounds for the zoomed-in image.
  36.         val startBoundsInt = Rect()
  37.         val finalBoundsInt = Rect()
  38.         val globalOffset = Point()
  39.         // The start bounds are the global visible rectangle of the thumbnail,
  40.         // and the final bounds are the global visible rectangle of the
  41.         // container view. Set the container view's offset as the origin for the
  42.         // bounds, since that's the origin for the positioning animation
  43.         // properties (X, Y).
  44.         thumbView.getGlobalVisibleRect(startBoundsInt)
  45.         binding.container.getGlobalVisibleRect(finalBoundsInt, globalOffset)
  46.         startBoundsInt.offset(-globalOffset.x, -globalOffset.y)
  47.         finalBoundsInt.offset(-globalOffset.x, -globalOffset.y)
  48.         val startBounds = RectF(startBoundsInt)
  49.         val finalBounds = RectF(finalBoundsInt)
  50.         // Using the "center crop" technique, adjust the start bounds to be the
  51.         // same aspect ratio as the final bounds. This prevents unwanted
  52.         // stretching during the animation. Calculate the start scaling factor.
  53.         // The end scaling factor is always 1.0.
  54.         val startScale: Float
  55.         if ((finalBounds.width() / finalBounds.height() > startBounds.width() / startBounds.height())) {
  56.             // Extend start bounds horizontally.
  57.             startScale = startBounds.height() / finalBounds.height()
  58.             val startWidth: Float = startScale * finalBounds.width()
  59.             val deltaWidth: Float = (startWidth - startBounds.width()) / 2
  60.             startBounds.left -= deltaWidth.toInt()
  61.             startBounds.right += deltaWidth.toInt()
  62.         } else {
  63.             // Extend start bounds vertically.
  64.             startScale = startBounds.width() / finalBounds.width()
  65.             val startHeight: Float = startScale * finalBounds.height()
  66.             val deltaHeight: Float = (startHeight - startBounds.height()) / 2f
  67.             startBounds.top -= deltaHeight.toInt()
  68.             startBounds.bottom += deltaHeight.toInt()
  69.         }
  70.         // Hide the thumbnail and show the zoomed-in view. When the animation
  71.         // begins, it positions the zoomed-in view in the place of the
  72.         // thumbnail.
  73.         thumbView.alpha = 0f
  74.         animateZoomToLargeImage(startBounds, finalBounds, startScale)
  75.         setDismissLargeImageAnimation(thumbView, startBounds, startScale)
  76.     }
  77.     private fun animateZoomToLargeImage(startBounds: RectF, finalBounds: RectF, startScale: Float) {
  78.         binding.expandedImage.visibility = View.VISIBLE
  79.         // Set the pivot point for SCALE_X and SCALE_Y transformations to the
  80.         // top-left corner of the zoomed-in view. The default is the center of
  81.         // the view.
  82.         binding.expandedImage.pivotX = 0f
  83.         binding.expandedImage.pivotY = 0f
  84.         // Construct and run the parallel animation of the four translation and
  85.         // scale properties: X, Y, SCALE_X, and SCALE_Y.
  86.         currentAnimator = AnimatorSet().apply {
  87.             play(
  88.                 ObjectAnimator.ofFloat(
  89.                     binding.expandedImage,
  90.                     View.X,
  91.                     startBounds.left,
  92.                     finalBounds.left)
  93.             ).apply {
  94.                 with(ObjectAnimator.ofFloat(binding.expandedImage, View.Y, startBounds.top, finalBounds.top))
  95.                 with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_X, startScale, 1f))
  96.                 with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_Y, startScale, 1f))
  97.             }
  98.             duration = shortAnimationDuration.toLong()
  99.             interpolator = DecelerateInterpolator()
  100.             addListener(object : AnimatorListenerAdapter() {
  101.                 override fun onAnimationEnd(animation: Animator) {
  102.                     currentAnimator = null
  103.                 }
  104.                 override fun onAnimationCancel(animation: Animator) {
  105.                     currentAnimator = null
  106.                 }
  107.             })
  108.             start()
  109.         }
  110.     }
  111.     private fun setDismissLargeImageAnimation(thumbView: View, startBounds: RectF, startScale: Float) {
  112.         // When the zoomed-in image is tapped, it zooms down to the original
  113.         // bounds and shows the thumbnail instead of the expanded image.
  114.         binding.expandedImage.setOnClickListener {
  115.             currentAnimator?.cancel()
  116.             // Animate the four positioning and sizing properties in parallel,
  117.             // back to their original values.
  118.             currentAnimator = AnimatorSet().apply {
  119.                 play(ObjectAnimator.ofFloat(binding.expandedImage, View.X, startBounds.left)).apply {
  120.                     with(ObjectAnimator.ofFloat(binding.expandedImage, View.Y, startBounds.top))
  121.                     with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_X, startScale))
  122.                     with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_Y, startScale))
  123.                 }
  124.                 duration = shortAnimationDuration.toLong()
  125.                 interpolator = DecelerateInterpolator()
  126.                 addListener(object : AnimatorListenerAdapter() {
  127.                     override fun onAnimationEnd(animation: Animator) {
  128.                         thumbView.alpha = 1f
  129.                         binding.expandedImage.visibility = View.GONE
  130.                         currentAnimator = null
  131.                     }
  132.                     override fun onAnimationCancel(animation: Animator) {
  133.                         thumbView.alpha = 1f
  134.                         binding.expandedImage.visibility = View.GONE
  135.                         currentAnimator = null
  136.                     }
  137.                 })
  138.                 start()
  139.             }
  140.         }
  141.     }
  142. }
复制代码
三、参考链接:

使用缩放动画放大视图
到此这篇关于Android仿微信聊天图片的放大缩小效果的文章就介绍到这了,更多相关Android微信聊天图片放大缩小内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表