imageviewer
A simple and customizable Android full-screen image viewer 一个简单且可自定义的Android全屏图像浏览器
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
- Basic implementation of ImageViewer:
val imageUrls = listOf("https://example.com/image1.jpg", "https://example.com/image2.jpg")
ImageViewer.Builder(this, imageUrls)
.setStartPosition(0)
.show()
- Customizing the overlay view:
ImageViewer.Builder(this, imageUrls)
.setOverlayView(customOverlayView)
.setImageChangeListener { position ->
customOverlayView.updatePosition(position)
}
.show()
- 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:
- Add the JitPack repository to your project's
build.gradle
file:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
- Add the dependency to your app's
build.gradle
file:
dependencies {
implementation 'com.github.iielse:imageviewer:VERSION'
}
- 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 designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
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_LIMIT | vieweré¢å è½½æ¡æ° |
VIEWER_ORIENTATION | vieweræ»å¨æ¹å |
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
å¦ææ¨è§å¾æçå¼æºåºå¸®ä½ èçäºå¤§éçå¼åæ¶é´ï¼å¯æ«æä¸æ¹çäºç»´ç éææèµãä½ çé¼å±æ¯æç»´æ¤é¡¹ç®æ大çå¨å
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! 🍻
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot