Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to Dialog Fragment #40

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

package com.stfalcon.imageviewer;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.*;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

import com.stfalcon.imageviewer.listeners.OnDismissListener;
import com.stfalcon.imageviewer.listeners.OnImageChangeListener;
import com.stfalcon.imageviewer.loader.ImageLoader;
Expand All @@ -43,24 +47,24 @@ public class StfalconImageViewer<T> {
protected StfalconImageViewer(@NonNull Context context, @NonNull BuilderData<T> builderData) {
this.context = context;
this.builderData = builderData;
this.dialog = new ImageViewerDialog<>(context, builderData);
this.dialog = ImageViewerDialog.Companion.newInstance(builderData);
}

/**
* Displays the built viewer if passed list of images is not empty
*/
public void show() {
show(true);
public void show(FragmentManager fragmentManager) {
show(fragmentManager, true);
}

/**
* Displays the built viewer if passed list of images is not empty
*
* @param animate whether the passed transition view should be animated on open. Useful for screen rotation handling.
*/
public void show(boolean animate) {
public void show(FragmentManager fragmentManager, boolean animate) {
if (!builderData.getImages().isEmpty()) {
dialog.show(animate);
dialog.show(fragmentManager, animate);
} else {
Log.w(context.getString(R.string.library_name),
"Images list cannot be empty! Viewer ignored.");
Expand Down Expand Up @@ -99,6 +103,13 @@ public void updateImages(List<T> images) {
}
}

/**
* Sets the target fragment of the dialog
*/
public void setTargetFragment(Fragment fragment, int requestCode) {
dialog.setTargetFragment(fragment, requestCode);
}

public int currentPosition() {
return dialog.getCurrentPosition();
}
Expand All @@ -123,13 +134,13 @@ public static class Builder<T> {
private Context context;
private BuilderData<T> data;

public Builder(Context context, T[] images, ImageLoader<T> imageLoader) {
this(context, new ArrayList<>(Arrays.asList(images)), imageLoader);
public Builder(Context context, T[] images) {
this(context, new ArrayList<>(Arrays.asList(images)));
}

public Builder(Context context, List<T> images, ImageLoader<T> imageLoader) {
public Builder(Context context, List<T> images) {
this.context = context;
this.data = new BuilderData<>(images, imageLoader);
this.data = new BuilderData<>(images);
}

/**
Expand Down Expand Up @@ -161,17 +172,6 @@ public Builder<T> withBackgroundColorResource(@ColorRes int color) {
return this.withBackgroundColor(ContextCompat.getColor(context, color));
}

/**
* Sets custom overlay view to be shown over the viewer.
* Commonly used for image description or counter displaying.
*
* @return This Builder object to allow calls chaining
*/
public Builder<T> withOverlayView(View view) {
this.data.setOverlayView(view);
return this;
}

/**
* Sets space between the images using dimension.
*
Expand Down Expand Up @@ -278,30 +278,10 @@ public Builder<T> withTransitionFrom(ImageView imageView) {
return this;
}

/**
* Sets {@link OnImageChangeListener} for the viewer.
*
* @return This Builder object to allow calls chaining
*/
public Builder<T> withImageChangeListener(OnImageChangeListener imageChangeListener) {
this.data.setImageChangeListener(imageChangeListener);
return this;
}

/**
* Sets {@link OnDismissListener} for viewer.
*
* @return This Builder object to allow calls chaining
*/
public Builder<T> withDismissListener(OnDismissListener onDismissListener) {
this.data.setOnDismissListener(onDismissListener);
return this;
}

/**
* Creates a {@link StfalconImageViewer} with the arguments supplied to this builder. It does not
* show the dialog. This allows the user to do any extra processing
* before displaying the dialog. Use {@link #show()} if you don't have any other processing
* before displaying the dialog. Use {@link #show(FragmentManager fragmentManager)} if you don't have any other processing
* to do and want this to be created and displayed.
*/
public StfalconImageViewer<T> build() {
Expand All @@ -312,8 +292,8 @@ public StfalconImageViewer<T> build() {
* Creates the {@link StfalconImageViewer} with the arguments supplied to this builder and
* shows the dialog.
*/
public StfalconImageViewer<T> show() {
return show(true);
public StfalconImageViewer<T> show(FragmentManager fragmentManager) {
return show(fragmentManager, true);
}

/**
Expand All @@ -322,9 +302,9 @@ public StfalconImageViewer<T> show() {
*
* @param animate whether the passed transition view should be animated on open. Useful for screen rotation handling.
*/
public StfalconImageViewer<T> show(boolean animate) {
public StfalconImageViewer<T> show(FragmentManager fragmentManager, boolean animate) {
StfalconImageViewer<T> viewer = build();
viewer.show(animate);
viewer.show(fragmentManager, animate);
return viewer;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.stfalcon.imageviewer.listeners;

import com.stfalcon.imageviewer.viewer.dialog.ImageViewerDialog;

/**
* Interface definition for a callback to be invoked when current image position was changed.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.stfalcon.imageviewer.loader;

import android.view.View;
import android.widget.ImageView;

import com.stfalcon.imageviewer.StfalconImageViewer;
import com.stfalcon.imageviewer.viewer.dialog.ImageViewerDialog;

/**
* Interface definition for a callback to be invoked when the overlay should be loaded
*/
//N.B.! This class is written in Java for convenient use of lambdas due to languages compatibility issues.
public interface OverlayLoader<T> {

/**
* Fires when the overlay needs to be loaded
*
*/
View loadOverlayFor(int position, ImageViewerDialog<T> viewer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@
package com.stfalcon.imageviewer.viewer.builder

import android.graphics.Color
import android.os.Parcelable
import android.view.View
import android.widget.ImageView
import com.stfalcon.imageviewer.listeners.OnDismissListener
import com.stfalcon.imageviewer.listeners.OnImageChangeListener
import com.stfalcon.imageviewer.loader.ImageLoader
import java.io.Serializable

internal class BuilderData<T>(
val images: List<T>,
val imageLoader: ImageLoader<T>
) {

class BuilderData<T>(
val images: List<T>
) : Serializable {
var backgroundColor = Color.BLACK
var startPosition: Int = 0
var imageChangeListener: OnImageChangeListener? = null
var onDismissListener: OnDismissListener? = null
var overlayView: View? = null
var imageMarginPixels: Int = 0
var containerPaddingPixels = IntArray(4)
var shouldStatusBarHide = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,73 @@

package com.stfalcon.imageviewer.viewer.dialog

import android.content.Context
import android.app.Dialog
import android.os.Bundle
import android.view.KeyEvent
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.stfalcon.imageviewer.R
import com.stfalcon.imageviewer.StfalconImageViewer
import com.stfalcon.imageviewer.listeners.OnDismissListener
import com.stfalcon.imageviewer.listeners.OnImageChangeListener
import com.stfalcon.imageviewer.loader.ImageLoader
import com.stfalcon.imageviewer.loader.OverlayLoader
import com.stfalcon.imageviewer.viewer.builder.BuilderData
import com.stfalcon.imageviewer.viewer.view.ImageViewerView
import kotlin.math.max

internal class ImageViewerDialog<T>(
context: Context,
private val builderData: BuilderData<T>
) {
class ImageViewerDialog<T>: DialogFragment() {

private val dialog: AlertDialog
private val viewerView: ImageViewerView<T> = ImageViewerView(context)
private lateinit var dialog: AlertDialog
private lateinit var viewerView: ImageViewerView<T>
private var animateOpen = true
private lateinit var builderData: BuilderData<T>

private val dialogStyle: Int
get() = if (builderData.shouldStatusBarHide)
R.style.ImageViewerDialog_NoStatusBar
else
R.style.ImageViewerDialog_Default

init {
companion object {
private const val builderDataKey = "BuilderDataKey"
fun <T> newInstance(builderData: BuilderData<T>): ImageViewerDialog<T> {
val args = Bundle()
args.putSerializable(builderDataKey, builderData)
val f = ImageViewerDialog<T>()
f.arguments = args
return f
}
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
builderData = arguments?.getSerializable(builderDataKey) as BuilderData<T>
setupViewerView()
dialog = AlertDialog
.Builder(context, dialogStyle)
.Builder(requireContext(), dialogStyle)
.setView(viewerView)
.setOnKeyListener { _, keyCode, event -> onDialogKeyEvent(keyCode, event) }
.create()
.apply {
setOnShowListener { viewerView.open(builderData.transitionView, animateOpen) }
setOnDismissListener { builderData.onDismissListener?.onDismiss() }
}
setOnDismissListener { ((targetFragment ?: activity) as? OnDismissListener)?.onDismiss() }
}
return dialog
}

fun show(animate: Boolean) {
fun show(fragmentManager: FragmentManager, animate: Boolean) {
animateOpen = animate
dialog.show()
show(fragmentManager, "")
}

fun close() {
viewerView.close()
}

fun dismiss() {
override fun dismiss() {
dialog.dismiss()
}

Expand Down Expand Up @@ -97,19 +118,27 @@ internal class ImageViewerDialog<T>(
}

private fun setupViewerView() {
viewerView = ImageViewerView(requireContext())
viewerView.apply {
isZoomingAllowed = builderData.isZoomingAllowed
isSwipeToDismissAllowed = builderData.isSwipeToDismissAllowed

containerPadding = builderData.containerPaddingPixels
imagesMargin = builderData.imageMarginPixels
overlayView = builderData.overlayView
overlayView = ((targetFragment ?: activity) as? OverlayLoader<T>)?.loadOverlayFor(max(viewerView.currentPosition, builderData.startPosition),
this@ImageViewerDialog)

setBackgroundColor(builderData.backgroundColor)
setImages(builderData.images, builderData.startPosition, builderData.imageLoader)
val imageLoader = (targetFragment ?: activity) as? ImageLoader<T>
if (imageLoader != null) {
setImages(builderData.images, builderData.startPosition, imageLoader)
}

onPageChange = { position -> builderData.imageChangeListener?.onImageChange(position) }
onDismiss = { dialog.dismiss() }
onPageChange = { position -> ((targetFragment ?: activity) as? OnImageChangeListener)?.onImageChange(position) }
onDismiss = {
dialog.dismiss()
((targetFragment ?: activity) as? OnDismissListener)?.onDismiss()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import com.stfalcon.imageviewer.common.pager.MultiTouchViewPager
import com.stfalcon.imageviewer.loader.ImageLoader
import com.stfalcon.imageviewer.viewer.adapter.ImagesPagerAdapter

internal class ImageViewerView<T> @JvmOverloads constructor(
class ImageViewerView<T> @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
Expand Down Expand Up @@ -81,7 +81,12 @@ internal class ImageViewerView<T> @JvmOverloads constructor(
internal var overlayView: View? = null
set(value) {
field = value
value?.let { rootContainer.addView(it) }
value?.let {
if (it.parent != null) {
(it.parent as? ViewGroup)?.removeView(it)
}
rootContainer.addView(it)
}
}

private var rootContainer: ViewGroup
Expand Down