Convert Figma logo to code with AI

iielse logoimageviewer

A simple and customizable Android full-screen image viewer 一个简单且可自定义的Android全屏图像浏览器

2,225
310
2,225
0

Top Related Projects

A simple and customizable Android full-screen image viewer with shared image transition support, "pinch to zoom" and "swipe to dismiss" gestures

图片浏览缩放控件

Android library (AAR). Highly configurable, easily extendable deep zoom view for displaying huge images without loss of detail. Perfect for photo galleries, maps, building plans etc.

Big image viewer supporting pan and zoom, with very little memory usage and full featured image loading choices. Powered by Subsampling Scale Image View, Fresco, Glide, and Picasso. Even with gif and webp support! 🍻

Quick Overview

ImageViewer is an Android library for displaying and interacting with images. It provides a customizable image viewer with features like zooming, panning, and swiping between multiple images. The library is designed to be easy to integrate into existing Android applications.

Pros

  • Smooth and responsive image viewing experience
  • Supports zooming, panning, and swiping gestures
  • Customizable UI elements and transitions
  • Easy integration with existing Android projects

Cons

  • Limited documentation and examples
  • May require additional setup for advanced customization
  • Not actively maintained (last update was over a year ago)
  • Potential performance issues with very large images

Code Examples

  1. Basic implementation of ImageViewer:
val imageUrls = listOf("https://example.com/image1.jpg", "https://example.com/image2.jpg")
ImageViewer.Builder(this, imageUrls)
    .setStartPosition(0)
    .show()
  1. Customizing the overlay view:
ImageViewer.Builder(this, imageUrls)
    .setOverlayView(customOverlayView)
    .setImageChangeListener { position ->
        customOverlayView.updatePosition(position)
    }
    .show()
  1. Implementing a custom transition:
ImageViewer.Builder(this, imageUrls)
    .setCustomImageRequestBuilder { requestBuilder ->
        requestBuilder.transition(DrawableTransitionOptions.withCrossFade())
    }
    .show()

Getting Started

To use ImageViewer in your Android project, follow these steps:

  1. Add the JitPack repository to your project's build.gradle file:
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  1. Add the dependency to your app's build.gradle file:
dependencies {
    implementation 'com.github.iielse:imageviewer:VERSION'
}
  1. Initialize and show the ImageViewer in your activity or fragment:
val imageUrls = listOf("https://example.com/image1.jpg", "https://example.com/image2.jpg")
ImageViewer.Builder(this, imageUrls)
    .setStartPosition(0)
    .show()

Replace VERSION with the latest version of the library. Make sure to handle necessary permissions for internet access and image loading in your Android manifest file.

Competitor Comparisons

A simple and customizable Android full-screen image viewer with shared image transition support, "pinch to zoom" and "swipe to dismiss" gestures

Pros of StfalconImageViewer

  • More customizable UI elements, including custom overlays and transitions
  • Built-in support for zooming and panning gestures
  • Easier integration with various image loading libraries

Cons of StfalconImageViewer

  • Less frequent updates and maintenance compared to ImageViewer
  • Slightly more complex implementation due to additional features
  • Limited documentation and examples available

Code Comparison

StfalconImageViewer:

StfalconImageViewer.Builder<MyImage>(context, images) { imageView, image ->
    Glide.with(context).load(image.url).into(imageView)
}.show()

ImageViewer:

ImageViewer.Builder(fragments, ImageViewerAdapter())
    .setStartPosition(startPosition)
    .show()

Both libraries offer simple implementation, but StfalconImageViewer provides more flexibility in image loading, while ImageViewer focuses on a streamlined approach with fragment support.

StfalconImageViewer excels in customization and gesture handling, making it suitable for complex image viewing scenarios. ImageViewer, on the other hand, offers a more straightforward implementation with regular updates, making it ideal for simpler use cases and long-term maintenance.

图片浏览缩放控件

Pros of PhotoView

  • More comprehensive feature set, including support for video playback and GIF animations
  • Better performance optimization, especially for large images and smooth zooming
  • More active development and community support

Cons of PhotoView

  • Larger library size, which may impact app size and load times
  • Steeper learning curve due to more complex API and configuration options
  • Less customizable UI compared to ImageViewer's simpler approach

Code Comparison

ImageViewer:

val imageViewer = ImageViewer.Builder(fragments = images, startPosition = startPosition)
    .setBackgroundColorRes(R.color.black)
    .setOverlayView(overlayView)
    .build()
imageViewer.show()

PhotoView:

PhotoView photoView = findViewById(R.id.photo_view);
photoView.setImageResource(R.drawable.image);
photoView.setScaleType(ImageView.ScaleType.CENTER_CROP);
photoView.setZoomable(true);
photoView.setOnPhotoTapListener(new OnPhotoTapListener() { ... });

Both libraries offer easy-to-use APIs for displaying and interacting with images. PhotoView provides more granular control over zoom behavior and touch events, while ImageViewer focuses on simplicity and quick implementation. PhotoView's code is more verbose but offers greater flexibility, whereas ImageViewer's builder pattern allows for concise setup with fewer lines of code.

Android library (AAR). Highly configurable, easily extendable deep zoom view for displaying huge images without loss of detail. Perfect for photo galleries, maps, building plans etc.

Pros of subsampling-scale-image-view

  • Efficient memory usage through subsampling and tiling
  • Supports very large images without OutOfMemoryErrors
  • Smooth pan and zoom with inertia scrolling

Cons of subsampling-scale-image-view

  • Less feature-rich compared to imageviewer
  • Limited customization options for UI elements
  • Lacks built-in support for image transitions or animations

Code Comparison

subsampling-scale-image-view:

SubsamplingScaleImageView imageView = new SubsamplingScaleImageView(context);
imageView.setImage(ImageSource.uri("/sdcard/image.jpg"));
imageView.setMaxScale(10f);

imageviewer:

val imageViewer = ImageViewer.Builder(fragments, images)
    .setStartPosition(startPosition)
    .setImageChangeListener(onImageChangeListener)
    .setOnItemClickListener(onItemClickListener)
    .build()
imageViewer.show()

subsampling-scale-image-view focuses on efficient handling of large images with smooth zooming and panning. It's ideal for applications dealing with high-resolution images or maps. However, it lacks some of the advanced features and customization options offered by imageviewer.

imageviewer provides a more comprehensive solution for image viewing, including transitions, customizable UI elements, and support for various image sources. It's better suited for applications requiring a full-featured image gallery or viewer with enhanced user interaction.

The code comparison shows that subsampling-scale-image-view is more focused on individual image display and manipulation, while imageviewer offers a more complete gallery-like experience with multiple images and customizable behaviors.

Big image viewer supporting pan and zoom, with very little memory usage and full featured image loading choices. Powered by Subsampling Scale Image View, Fresco, Glide, and Picasso. Even with gif and webp support! 🍻

Pros of BigImageViewer

  • Supports multiple image loading libraries (Fresco, Glide, and custom image loaders)
  • Handles very large images efficiently with tiling and subsampling
  • Provides smooth transitions between images in a gallery view

Cons of BigImageViewer

  • More complex setup and configuration compared to ImageViewer
  • Larger library size due to additional features and dependencies
  • May have a steeper learning curve for basic use cases

Code Comparison

ImageViewer:

val imageViewer = ImageViewer.Builder(context, images)
    .setStartPosition(startPosition)
    .show()

BigImageViewer:

BigImageViewer.initialize(FrescoImageLoader.with(context))

val bigImageView = findViewById<BigImageView>(R.id.big_image_view)
bigImageView.showImage(Uri.parse("https://example.com/image.jpg"))

Both libraries offer easy-to-use APIs for displaying images, but BigImageViewer requires initialization of an image loader and uses a custom view, while ImageViewer provides a more straightforward builder pattern for quick implementation.

ImageViewer is generally simpler to use for basic image viewing needs, while BigImageViewer offers more advanced features and flexibility for handling large images and complex scenarios. The choice between the two depends on the specific requirements of your project and the level of customization needed.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Imageviewer

提供查看缩略视图到原视图的无缝过渡转变的视觉效果,优雅的浏览普通图、长图、动图.

主要特征

  • 过渡动画 缩略图到大图或大图到缩略图时提供无缝衔接动画
  • 浏览手势 浏览大图时可使用常势操用手.如缩放图片等.(PhotoView)
  • 超大图 图片区块加载 (SubsamplingScaleImageView)
  • Video 支持Video加载 (ExoPlayer)
  • 拖拽关闭 对大图进行上/下滑操作退出浏览.
  • 数据分页加载 在浏览大图的情况下异步加载数据.
  • 数据删除
  • 自定义UI 对预览页的UI元素自定义追加
  • 已适配RTL

引入

implementation 'com.github.iielse:imageviewer:x.y.z' 

最简单的调用代码

fun show() { //
    val dataList: List<Photo> = // 将要展示的图片集合列表
    val clickedData: Photo = // 被点击的其中的那个图片元素信息
    val builder = ImageViewerBuilder(
        context = view.context,
        dataProvider = SimpleDataProvider(clickedData, dataList), // 一次性全量加载 // 实现DataProvider接口支持分页加载
        imageLoader = SimpleImageLoader(), // 可使用demo固定写法 // 实现对数据源的加载.支持自定义加载数据类型,加载方案
        transformer = SimpleTransformer(), // 可使用demo固定写法 // 以photoId为标示,设置过渡动画的'配对'.
    )
    builder.show()
}
// 基本是固定写法. Glide 可以换成别的. demo代码中有video的写法.
class SimpleImageLoader : ImageLoader {
    /** 根据自身photo数据加载图片.可以使用其它图片加载框架. */
    override fun load(view: ImageView, data: Photo, viewHolder: RecyclerView.ViewHolder) {
        val it = (data as? MyData?)?.url ?: return
        Glide.with(view).load(it)
                .placeholder(view.drawable)
                .into(view)
    }

    /**
     * 根据自身photo数据加载超大图.subsamplingView数据源需要先将内容完整下载到本地.
     */
    override fun load(subsamplingView: SubsamplingScaleImageView, data: Photo, viewHolder: RecyclerView.ViewHolder) {
        val it = (data as? MyData?)?.url ?: return
        subsamplingDownloadRequest(it)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe { findLoadingView(viewHolder)?.visibility = View.VISIBLE }
                .doFinally { findLoadingView(viewHolder)?.visibility = View.GONE }
                .doOnNext { subsamplingView.setImage(ImageSource.uri(Uri.fromFile(it))) }
                .doOnError { toast(it.message) }
                .subscribe().bindLifecycle(subsamplingView)
    }

    private fun subsamplingDownloadRequest(url: String): Observable<File> {
        return Observable.create {
            try {
                it.onNext(Glide.with(appContext).downloadOnly().load(url).submit().get())
                it.onComplete()
            } catch (e: java.lang.Exception) {
                if (!it.isDisposed) it.onError(e)
            }
        }
    }

    private fun findLoadingView(viewHolder: RecyclerView.ViewHolder): View? {
        return viewHolder.itemView.findViewById<ProgressBar>(R.id.loadingView)
    }

    ......
}
// 基本是可以作为固定写法.
class SimpleTransformer : Transformer {
    override fun getView(key: Long): ImageView? = provide(key)
    
    companion object {
        private val transition = HashMap<ImageView, Long>()
        fun put(photoId: Long, imageView: ImageView) {
            require(isMainThread())
            if (!imageView.isAttachedToWindow) return
            imageView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
                override fun onViewAttachedToWindow(p0: View?) = Unit
                override fun onViewDetachedFromWindow(p0: View?) {
                    transition.remove(imageView)
                    imageView.removeOnAttachStateChangeListener(this)
                }
            })
            transition[imageView] = photoId
        }

        private fun provide(photoId: Long): ImageView? {
            transition.keys.forEach {
                if (transition[it] == photoId)
                    return it
            }
            return null
        }
    }
} 

到此简单的集成已经完毕.

进阶使用.

(实现以下3个方法.可以追加自定义的展示和功能)

  • 自定义'每一页'上的UI.比如可显示图片的更多信息.提供存储分享等更多功能等 builder.setVHCustomizer(MyCustomViewHolderUI())
  • 自定义'覆盖(最上)层'上的UI.比如添加指示器等 builder.setOverlayCustomizer(MyCustomIndicatorUI())
  • 监听viewer的各种状态变化.包括页面的切换(显示当前在第几页).;过渡动画的执行状态;维护video的播放状态等 builder.setViewerCallback(MyViewerStateChangedListener())
// 一般监听翻页onPageSelected可以控制 video播放的状态
// viewer 各状态监听回调
interface ViewerCallback : ImageViewerAdapterListener {
    // 当点击缩略图变化大图的瞬间
    override fun onInit(viewHolder: RecyclerView.ViewHolder) {}
    // 当图片被拖动时
    override fun onDrag(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {}
    // 当图片被拖动但不至于退出浏览
    override fun onRestore(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {}
    // 当图片被拖动执行退出浏览
    override fun onRelease(viewHolder: RecyclerView.ViewHolder, view: View) {}
    // 翻页中状态变化
    fun onPageScrollStateChanged(state: Int) {}
    // 翻页中
    fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
    // 当某大图页面被选中
    fun onPageSelected(position: Int, viewHolder: RecyclerView.ViewHolder) {}
}

参数配置. 一般不用调整

属性作用说明
OFFSCREEN_PAGE_LIMITviewer预加载条数
VIEWER_ORIENTATIONviewer滑动方向
VIEWER_BACKGROUND_COLOR大图预览时背景色(默认纯黑)
DURATION_TRANSITION过渡动画时长
DURATION_BG过渡动画背景变化时长
SWIPE_DISMISS是否支持拖拽返回
SWIPE_TOUCH_SLOP拖拽触摸感知阈值
DISMISS_FRACTION拖拽返回边界阈值
TRANSITION_OFFSET_Y修正透明状态栏下过渡动画的起始位置

数据源的定义

interface Photo {
    fun id(): Long // 每条图片数据的唯一标示. 主要用于分页数据加载. 定位过渡动画的对应关系
    fun itemType(): @ItemType.Type Int // 是否启用SubsamplingScaleImageView实现图片区块加载或ExoVideoView实现Video加载
}

FAQ

  • 如何手动关闭退出整个页面?
  • 如何删除一条数据?

通过 ViewModelProvider(activity).get(ImageViewerActionViewModel::class.java)获取viewer 对象引用. 之后可使用 setCurrentItem(pos: Int)切换大图位置到指定位置; dismiss()退出浏览大图; remove(item: List<Photo>)删除其中的元素

  • 如何实现Video的展示? 可参考demo实现 demo代码位置 SimpleViewerCustomizer
  • 为什么没有过渡动画? 需要正确的配置 Transformer。需要保证getView 返回不为null.
  • 为什么动画的执行和原图有高度偏差? 注意状态栏的影响。配置Config.TRANSITION_OFFSET_Y

其它重要说明

demo可运行. demo可运行. demo可运行 .demo代码已重构.

都看到这里了,不点下Star吗 [旺柴]

Thanks

如果您觉得我的开源库帮你节省了大量的开发时间,可扫描下方的二维码随意打赏。你的鼓励是我维护项目最大的动力