[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7487":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":15,"stars7d":15,"stars30d":15,"stars90d":15,"forks30d":15,"starsTrendScore":15,"compositeScore":16,"rankGlobal":10,"rankLanguage":10,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":15,"starSnapshotCount":15,"syncStatus":37,"lastSyncTime":38,"discoverSource":39},7487,"imageviewer","iielse\u002Fimageviewer","iielse","A simple and customizable Android full-screen image viewer 一个简单且可自定义的Android全屏图像浏览器","",null,"Kotlin",2300,313,30,0,29.49,"MIT License",false,"master",true,[22,23,24,25,26,5,27,28,29,30,31,32,33],"draggable","gallery","gesture","gif","image","photobrowser","transferee","video","viewer","viewpager","wechat","zoom","2026-06-12 02:01:40","# Imageviewer\n\n提供查看缩略视图到原视图的无缝过渡转变的视觉效果，优雅的浏览普通图、长图、动图.\n\n#### 主要特征\n\n- **过渡动画** 缩略图到大图或大图到缩略图时提供无缝衔接动画\n- **浏览手势** 浏览大图时可使用常势操用手.如缩放图片等.（[PhotoView](https:\u002F\u002Fgithub.com\u002Fchrisbanes\u002FPhotoView)）\n- **超大图** 图片区块加载 （[SubsamplingScaleImageView](https:\u002F\u002Fgithub.com\u002Fdavemorrissey\u002Fsubsampling-scale-image-view)）\n- **Video** 支持Video加载 ([ExoPlayer](https:\u002F\u002Fgithub.com\u002Fgoogle\u002FExoPlayer))\n- **拖拽关闭** 对大图进行上\u002F下滑操作退出浏览.\n- **数据分页加载** 在浏览大图的情况下异步加载数据.\n- **数据删除**\n- **自定义UI** 对预览页的UI元素自定义追加\n- **已适配RTL**\n\n![](https:\u002F\u002Fgithub.com\u002Fiielse\u002Fres\u002Fblob\u002Fmaster\u002Fimageviewer\u002F1.gif)\n\n### 引入 [![](https:\u002F\u002Fjitpack.io\u002Fv\u002Fiielse\u002Fimageviewer.svg)](https:\u002F\u002Fjitpack.io\u002F#iielse\u002Fimageviewer)\n\n```\nimplementation 'com.github.iielse:imageviewer:x.y.z' \n```\n\n### 最简单的调用代码\n\n```\nfun show() { \u002F\u002F\n    val dataList： List\u003CPhoto> = \u002F\u002F 将要展示的图片集合列表\n    val clickedData: Photo = \u002F\u002F 被点击的其中的那个图片元素信息\n    val builder = ImageViewerBuilder(\n        context = view.context,\n        dataProvider = SimpleDataProvider(clickedData, dataList), \u002F\u002F 一次性全量加载 \u002F\u002F 实现DataProvider接口支持分页加载\n        imageLoader = SimpleImageLoader(), \u002F\u002F 可使用demo固定写法 \u002F\u002F 实现对数据源的加载.支持自定义加载数据类型，加载方案\n        transformer = SimpleTransformer(), \u002F\u002F 可使用demo固定写法 \u002F\u002F 以photoId为标示，设置过渡动画的'配对'.\n    )\n    builder.show()\n}\n```\n\n```\n\u002F\u002F 基本是固定写法. Glide 可以换成别的. demo代码中有video的写法.\nclass SimpleImageLoader : ImageLoader {\n    \u002F** 根据自身photo数据加载图片.可以使用其它图片加载框架. *\u002F\n    override fun load(view: ImageView, data: Photo, viewHolder: RecyclerView.ViewHolder) {\n        val it = (data as? MyData?)?.url ?: return\n        Glide.with(view).load(it)\n                .placeholder(view.drawable)\n                .into(view)\n    }\n\n    \u002F**\n     * 根据自身photo数据加载超大图.subsamplingView数据源需要先将内容完整下载到本地.\n     *\u002F\n    override fun load(subsamplingView: SubsamplingScaleImageView, data: Photo, viewHolder: RecyclerView.ViewHolder) {\n        val it = (data as? MyData?)?.url ?: return\n        subsamplingDownloadRequest(it)\n                .subscribeOn(Schedulers.io())\n                .observeOn(AndroidSchedulers.mainThread())\n                .doOnSubscribe { findLoadingView(viewHolder)?.visibility = View.VISIBLE }\n                .doFinally { findLoadingView(viewHolder)?.visibility = View.GONE }\n                .doOnNext { subsamplingView.setImage(ImageSource.uri(Uri.fromFile(it))) }\n                .doOnError { toast(it.message) }\n                .subscribe().bindLifecycle(subsamplingView)\n    }\n\n    private fun subsamplingDownloadRequest(url: String): Observable\u003CFile> {\n        return Observable.create {\n            try {\n                it.onNext(Glide.with(appContext).downloadOnly().load(url).submit().get())\n                it.onComplete()\n            } catch (e: java.lang.Exception) {\n                if (!it.isDisposed) it.onError(e)\n            }\n        }\n    }\n\n    private fun findLoadingView(viewHolder: RecyclerView.ViewHolder): View? {\n        return viewHolder.itemView.findViewById\u003CProgressBar>(R.id.loadingView)\n    }\n\n    ......\n}\n```\n\n```\n\u002F\u002F 基本是可以作为固定写法.\nclass SimpleTransformer : Transformer {\n    override fun getView(key: Long): ImageView? = provide(key)\n    \n    companion object {\n        private val transition = HashMap\u003CImageView, Long>()\n        fun put(photoId: Long, imageView: ImageView) {\n            require(isMainThread())\n            if (!imageView.isAttachedToWindow) return\n            imageView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {\n                override fun onViewAttachedToWindow(p0: View?) = Unit\n                override fun onViewDetachedFromWindow(p0: View?) {\n                    transition.remove(imageView)\n                    imageView.removeOnAttachStateChangeListener(this)\n                }\n            })\n            transition[imageView] = photoId\n        }\n\n        private fun provide(photoId: Long): ImageView? {\n            transition.keys.forEach {\n                if (transition[it] == photoId)\n                    return it\n            }\n            return null\n        }\n    }\n} \n```\n\n到此简单的集成已经完毕.\n\n\n## 进阶使用.\n\n（实现以下3个方法.可以追加自定义的展示和功能）\n\n* 自定义'每一页'上的UI.比如可显示图片的更多信息.提供存储分享等更多功能等 `builder.setVHCustomizer(MyCustomViewHolderUI())`\n* 自定义'覆盖(最上)层'上的UI.比如添加指示器等 `builder.setOverlayCustomizer(MyCustomIndicatorUI())`\n* 监听viewer的各种状态变化.包括页面的切换(显示当前在第几页).；过渡动画的执行状态；维护video的播放状态等 `builder.setViewerCallback(MyViewerStateChangedListener())`\n\n```\n\u002F\u002F 一般监听翻页onPageSelected可以控制 video播放的状态\n\u002F\u002F viewer 各状态监听回调\ninterface ViewerCallback : ImageViewerAdapterListener {\n    \u002F\u002F 当点击缩略图变化大图的瞬间\n    override fun onInit(viewHolder: RecyclerView.ViewHolder) {}\n    \u002F\u002F 当图片被拖动时\n    override fun onDrag(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {}\n    \u002F\u002F 当图片被拖动但不至于退出浏览\n    override fun onRestore(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {}\n    \u002F\u002F 当图片被拖动执行退出浏览\n    override fun onRelease(viewHolder: RecyclerView.ViewHolder, view: View) {}\n    \u002F\u002F 翻页中状态变化\n    fun onPageScrollStateChanged(state: Int) {}\n    \u002F\u002F 翻页中\n    fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}\n    \u002F\u002F 当某大图页面被选中\n    fun onPageSelected(position: Int, viewHolder: RecyclerView.ViewHolder) {}\n}\n```\n\n#### 参数配置. 一般不用调整\n\n属性  | 作用说明\n------------- | -------------\nOFFSCREEN_PAGE_LIMIT  | viewer预加载条数\nVIEWER_ORIENTATION  | viewer滑动方向\nVIEWER_BACKGROUND_COLOR  | 大图预览时背景色(默认纯黑)\nDURATION_TRANSITION  | 过渡动画时长\nDURATION_BG  | 过渡动画背景变化时长\nSWIPE_DISMISS  | 是否支持拖拽返回\nSWIPE_TOUCH_SLOP | 拖拽触摸感知阈值\nDISMISS_FRACTION  | 拖拽返回边界阈值\nTRANSITION_OFFSET_Y | 修正透明状态栏下过渡动画的起始位置\n\n### 数据源的定义\n\n```\ninterface Photo {\n    fun id(): Long \u002F\u002F 每条图片数据的唯一标示. 主要用于分页数据加载. 定位过渡动画的对应关系\n    fun itemType(): @ItemType.Type Int \u002F\u002F 是否启用SubsamplingScaleImageView实现图片区块加载或ExoVideoView实现Video加载\n}\n```\n\n### FAQ\n\n- 如何手动关闭退出整个页面？\n- 如何删除一条数据？\n\n通过 `ViewModelProvider(activity).get(ImageViewerActionViewModel::class.java)`获取`viewer` 对象引用.\n之后可使用 `setCurrentItem(pos: Int)`切换大图位置到指定位置; `dismiss()`退出浏览大图; `remove(item: List\u003CPhoto>)`删除其中的元素\n\n- 如何实现Video的展示？\n  可参考demo实现 demo代码位置 `SimpleViewerCustomizer`\n- 为什么没有过渡动画？\n  需要正确的配置 `Transformer`。需要保证`getView` 返回不为null.\n- 为什么动画的执行和原图有高度偏差？\n  注意状态栏的影响。配置`Config.TRANSITION_OFFSET_Y`\n\n### 其它重要说明\n\ndemo可运行. demo可运行. demo可运行 .demo代码已重构.\n\n都看到这里了，不点下`Star`吗 [旺柴]\n\n### Thanks\n\n如果您觉得我的开源库帮你节省了大量的开发时间，可扫描下方的二维码随意打赏。你的鼓励是我维护项目最大的动力\n","iielse\u002Fimageviewer 是一个简单且可自定义的 Android 全屏图像浏览器。其核心功能包括缩略图到大图的无缝过渡动画、支持手势操作（如缩放）、超大图分块加载、视频播放支持以及拖拽关闭等。该项目采用 Kotlin 编写，集成了 PhotoView 和 SubsamplingScaleImageView 等库来增强图片浏览体验，并通过 ExoPlayer 支持视频内容。此外，它还提供了数据异步加载和自定义 UI 的能力，使得开发者可以根据需要调整预览界面。适用于需要在移动应用中提供流畅、美观的图片或视频浏览体验的各种场景，如社交媒体、相册应用等。",2,"2026-06-11 03:12:42","top_language"]