diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..b5b9d46 Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..223bbca --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..cf4c1a0 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..921bc77 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c565aeb --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml new file mode 100644 index 0000000..4513118 --- /dev/null +++ b/.idea/navEditor.xml @@ -0,0 +1,73 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 6aa8ba8..c649577 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,30 @@ -# MultiImageView -[![codebeat badge](https://codebeat.co/badges/a7126021-293c-4ba8-887c-2ccae7379b5c)](https://codebeat.co/projects/github-com-stfalcon-studio-multiimageview-master) [ ![Download](https://api.bintray.com/packages/bevzaanton/maven/MultiImageView/images/download.svg) ](https://bintray.com/bevzaanton/maven/MultiImageView/_latestVersion) +I forked this library to carry on it's development, I've extended the examples to include a recycler view example, the shape and corner radius can now be set from xml, -Library for display a few images in one MultiImageView like avatar of group chat -![alt tag](http://i.imgur.com/S4QoKVB.gif) +to create the image view in xml -### Who we are -Need iOS and Android apps, MVP development or prototyping? Contact us via info@stfalcon.com. We develop software since 2009, and we're known experts in this field. Check out our [portfolio](https://stfalcon.com/en/portfolio) and see more libraries from [stfalcon-studio](https://stfalcon-studio.github.io/). - -### Download - -Download via Gradle: -```gradle -compile 'com.github.stfalcon:multiimageview:0.1' -``` - -or Maven: -```xml - - com.github.stfalcon - multiimageview - 0.1 - pom - -``` - -### Usage -Add MultiImageView to layout xml file -```xml - -``` -In java class find view by id -```java -final MultiImageView multiImageView = (MultiImageView) findViewById(R.id.iv); -``` -For adding image to MultiImageView use method addImage(Bitmap bitmap). For exapple: -```java -multiImageView.addImage(BitmapFactory.decodeResource(getResources(), R.drawable.avatar1)); -``` -For setting shape of MultiImageView use method setShape(MultiImageView.Shape shape). -```java -multiImageView.setShape(MultiImageView.Shape.RECTANGLE);//Rectangle with round corners -multiImageView.setShape(MultiImageView.Shape.CIRCLE);//Circle -multiImageView.setShape(MultiImageView.Shape.NONE);//Without shape -``` -If you choose rectangle shape you can set corner radius -```java -multiImageView.setRectCorners(50); -``` -To clear MultiImageView use: -```java -multiImageView.clear(); -``` - - -Take a look at the [sample project](sample) for more information - -### License + android:layout_height="100dp" + app:shape="rectangle" + app:corner_radius="60" /> + +![Image description](https://github.com/martipello/MultiImageView/blob/master/sample/src/main/res/drawable/screenshot_1582383274.png) -``` -Copyright 2017 stfalcon.com + +![Image description](https://github.com/martipello/MultiImageView/blob/master/sample/src/main/res/drawable/screenshot_1582383283.png) -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +to add an image use -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -``` + multiImageView.addImage(BitmapFactory.decodeResource(getResources(), R.drawable.avatar1)); + +to clear images use + multiImageView.clear(); + +the corner radius only applys to the rectangle shape other wise it will be ignored you can set this in xml or programmatically + +![Image description](https://github.com/martipello/MultiImageView/blob/master/sample/src/main/res/drawable/screenshot_1582383289.png) -[sample]: diff --git a/build.gradle b/build.gradle index 8e45e2b..d956c7f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,34 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.0.5-3' + ext.kotlin_version = '1.3.72' repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:4.1.0-rc02' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.novoda:bintray-release:0.3.4' + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files + } } + allprojects { repositories { jcenter() + google() + maven { + url 'https://maven.google.com/' + name 'Google' + } + maven { url 'https://jitpack.io' } } } diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04e285f..e08c3fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Dec 28 10:00:20 PST 2015 +#Thu Sep 24 16:45:00 BST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/multiimageview/build.gradle b/multiimageview/build.gradle index ea36db7..99b27f6 100644 --- a/multiimageview/build.gradle +++ b/multiimageview/build.gradle @@ -1,18 +1,17 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'com.novoda.bintray-release' android { - compileSdkVersion 25 - buildToolsVersion "25.0.0" + compileSdkVersion 29 + buildToolsVersion "29.0.2" defaultConfig { minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 29 versionCode 1 versionName "0.1" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -25,26 +24,25 @@ android { main.java.srcDirs += 'src/main/kotlin' } } - -publish { - groupId = 'com.github.stfalcon' - artifactId = 'multiimageview' - publishVersion = '0.1' - desc = 'Library for display a few images in one MultiImageView like avatar of group chat' - licences = ['Apache-2.0'] - uploadName='MultiImageView' - website = 'https://github.com/stfalcon-studio/MultiImageView.git' +buildscript { + repositories { + jcenter() + } } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.0.0' - testCompile 'junit:junit:4.12' - compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.2.0' + testImplementation 'junit:junit:4.12' + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'com.github.martipello:MultiImageView:1.0.7' + } repositories { mavenCentral() } + + diff --git a/multiimageview/src/androidTest/java/com/stfalcon/multiimageview/ExampleInstrumentedTest.java b/multiimageview/src/androidTest/java/com/sealstudios/multiimageview/ExampleInstrumentedTest.java similarity index 94% rename from multiimageview/src/androidTest/java/com/stfalcon/multiimageview/ExampleInstrumentedTest.java rename to multiimageview/src/androidTest/java/com/sealstudios/multiimageview/ExampleInstrumentedTest.java index aa82c05..b0ad792 100644 --- a/multiimageview/src/androidTest/java/com/stfalcon/multiimageview/ExampleInstrumentedTest.java +++ b/multiimageview/src/androidTest/java/com/sealstudios/multiimageview/ExampleInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.stfalcon.multiimageview; +package com.sealstudios.multiimageview; import android.content.Context; import android.support.test.InstrumentationRegistry; diff --git a/multiimageview/src/main/AndroidManifest.xml b/multiimageview/src/main/AndroidManifest.xml index 3577cd3..78f6a54 100644 --- a/multiimageview/src/main/AndroidManifest.xml +++ b/multiimageview/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.sealstudios.multiimageview"> diff --git a/multiimageview/src/main/java/com/sealstudios/multiimageview/MultiImageView.kt b/multiimageview/src/main/java/com/sealstudios/multiimageview/MultiImageView.kt new file mode 100644 index 0000000..851e03f --- /dev/null +++ b/multiimageview/src/main/java/com/sealstudios/multiimageview/MultiImageView.kt @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright 2016 stfalcon.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +package com.sealstudios.multiimageview + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.* +import android.graphics.drawable.Drawable +import android.os.Build +import android.util.AttributeSet +import android.util.TypedValue +import android.widget.ImageView +import java.util.* +import kotlin.math.max + + +/** + * Created by Anton Bevza on 12/22/16. + */ + +class MultiImageView @JvmOverloads constructor (context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ImageView(context, attrs) { + + val TOP_RIGHT = 0 + val TOP_LEFT = 1 + val BOTTOM_RIGHT = 2 + val BOTTOM_LEFT = 3 + + //Shape of view + var shape = Shape.NONE + set(value) { + field = value + invalidate() + } + //Corners radius for rectangle shape + var rectCorners = 100 + + private val bitmaps = ArrayList() + private val path = Path() + private val rect = RectF() + private var multiDrawable: Drawable? = null + + /** + * Add image to view + */ + fun addImage(bitmap: Bitmap) { + bitmaps.add(bitmap) + refresh() + } + + /** + * Add image to view + */ + fun addAllImages(bitmaps: MutableList) { + this.bitmaps.clear() + this.bitmaps.addAll(bitmaps) + refresh() + } + + /** + * Remove all images + */ + fun clear() { + bitmaps.clear() + refresh() + } + + /** + * Get images count + */ + fun getBitmapCount(): Int { + return bitmaps.size + } + + /** + * Set badge text + */ + fun setBadgeText() { + + } + +// override fun onAttachedToWindow() { +// super.onAttachedToWindow() +// refresh() +// } + + init { + attrs?.let { + bitmaps.clear() + val typedArray = context.obtainStyledAttributes(it, R.styleable.MultiImageView) + shape = typedArray.getEnum(R.styleable.MultiImageView_shape, Shape.NONE) + rectCorners = typedArray.getInt(R.styleable.MultiImageView_corner_radius, 100) + val badgeColor = typedArray.getColor(R.styleable.MultiImageView_badge_color, getThemeAccentColor(context)) + val badgeTextColor = typedArray.getColor(R.styleable.MultiImageView_badge_text_color, android.R.attr.textColor) + val badgeLocation = typedArray.getString(R.styleable.MultiImageView_badge_location) + val badgeMinCount = typedArray.getInt(R.styleable.MultiImageView_badge_shown_from_count, 4) + val badgeMaxCount = typedArray.getInt(R.styleable.MultiImageView_badge_shown_to_count, 9) + typedArray.recycle() + } + } + + + /** + * recreate MultiDrawable and set it as Drawable to ImageView + */ + private fun refresh() { + multiDrawable = MultiDrawable(bitmaps) + setImageDrawable(multiDrawable) + } + + override fun onDraw(canvas: Canvas?) { + if (canvas != null) { + if (drawable != null) { + //if shape not set - just draw + if (shape != Shape.NONE) { + path.reset() + //ImageView size + rect.set(0f, 0f, width.toFloat(), height.toFloat()) + if (shape == Shape.RECTANGLE) { + //Rectangle with corners + path.addRoundRect(rect, rectCorners.toFloat(), + rectCorners.toFloat(), Path.Direction.CW) + } else { + //Oval + path.addOval(rect, Path.Direction.CW) + } + //Clip with shape + canvas.clipPath(path) + } + super.onDraw(canvas) + } + } + } + + + private fun getThemeAccentColor(context: Context): Int { + val colorAttr: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + android.R.attr.colorAccent + } else { + //Get colorAccent defined for AppCompat + context.resources.getIdentifier("colorAccent", "attr", context.packageName) + } + val outValue = TypedValue() + context.theme.resolveAttribute(colorAttr, outValue, true) + return outValue.data + } + + + //Types of shape + enum class Shape { + CIRCLE, RECTANGLE, NONE + } + + inline fun > TypedArray.getEnum(index: Int, default: T) = + getInt(index, -1).let { if (it >= 0) enumValues()[it] else default + } +} + +class MultiDrawable(private val bitmaps: ArrayList) : Drawable() { + + private val paint = Paint(Paint.ANTI_ALIAS_FLAG) + private val items = ArrayList() + + /** + * Create PhotoItem with position and size depends of count of images + */ + private fun init() { + items.clear() + paint.isAntiAlias = true + paint.isFilterBitmap = true + paint.isDither = true + + when { + bitmaps.size == 1 -> { + val bitmap = scaleCenterCrop(bitmaps[0], bounds.width(), bounds.height()) + items.add(PhotoItem(bitmap, Rect(0, 0, bounds.width(), bounds.height()))) + } + bitmaps.size == 2 -> { + val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width() / 2, bounds.height()) + val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width() / 2, bounds.height()) + items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width(), bounds.height()))) + items.add(PhotoItem(bitmap2, Rect(bounds.width() / 2, 0, bounds.width() + bounds.width() / 2, bounds.height()))) + } + bitmaps.size == 3 -> { + val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width() / 2, bounds.height()) + val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width(), bounds.height()) + val bitmap3 = scaleCenterCrop(bitmaps[2], bounds.width(), bounds.height()) + items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width(), bounds.height()))) + items.add(PhotoItem(bitmap2, Rect(bounds.width() / 2, 0, bounds.width(), bounds.height() / 2))) + items.add(PhotoItem(bitmap3, Rect(bounds.width() / 2, bounds.height() / 2, bounds.width(), bounds.height()))) + } + bitmaps.size > 3 -> { + val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width(), bounds.height()) + val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width(), bounds.height()) + val bitmap3 = scaleCenterCrop(bitmaps[2], bounds.width(), bounds.height()) + val bitmap4 = scaleCenterCrop(bitmaps[3], bounds.width(), bounds.height()) + items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width() / 2, bounds.height() / 2))) + items.add(PhotoItem(bitmap3, Rect(0, bounds.height() / 2, bounds.width() / 2, bounds.height()))) + items.add(PhotoItem(bitmap2, Rect(bounds.width() / 2, 0, bounds.width(), bounds.height() / 2))) + items.add(PhotoItem(bitmap4, Rect(bounds.width() / 2, bounds.height() / 2, bounds.width(), bounds.height()))) + } + } + } + + override fun draw(canvas: Canvas) { + items.forEach { + canvas.drawBitmap(it.bitmap, bounds, it.position, paint) + } + + + } + + /** + * scale and center crop image + */ +// private fun scaleCenterCrop(source: Bitmap, newHeight: Int, newWidth: Int): Bitmap { +// return ThumbnailUtils.extractThumbnail(source, newWidth, newHeight) +// } + + + private fun scaleCenterCrop(source: Bitmap, newWidth: Int, newHeight: Int): Bitmap { + val sourceWidth = source.width + val sourceHeight = source.height + + // Compute the scaling factors to fit the new height and width, respectively. + // To cover the final image, the final scaling will be the bigger + // of these two. + val xScale = newWidth.toFloat() / sourceWidth + val yScale = newHeight.toFloat() / sourceHeight + val scale = max(xScale, yScale) + + // Now get the size of the source bitmap when scaled + val scaledWidth = scale * sourceWidth + val scaledHeight = scale * sourceHeight + + // Let's find out the upper left coordinates if the scaled bitmap + // should be centered in the new size give by the parameters + val left = (newWidth - scaledWidth) / 2 + val top = (newHeight - scaledHeight) / 2 + + // The target rectangle for the new, scaled version of the source bitmap will now + // be + val targetRect = RectF(left, top, left + scaledWidth, top + scaledHeight) + + // Finally, we create a new bitmap of the specified size and draw our new, + // scaled bitmap onto it. + val dest = Bitmap.createBitmap(newWidth, newHeight, source.config) + val canvas = Canvas(dest) + canvas.drawBitmap(source, null, targetRect, null) + + return dest + } + + /*** + * Data class for store bitmap and position + */ + data class PhotoItem(val bitmap: Bitmap, val position: Rect) + + + //***Needed to override***// + override fun setAlpha(alpha: Int) { + paint.alpha = alpha + } + + override fun onBoundsChange(bounds: Rect) { + super.onBoundsChange(bounds) + init() + } + + override fun getOpacity() = PixelFormat.TRANSLUCENT + + override fun setColorFilter(colorFilter: ColorFilter?) { + paint.colorFilter = colorFilter + } + + + //***------------------***// +} + diff --git a/multiimageview/src/main/java/com/stfalcon/multiimageview/MultiImageView.kt b/multiimageview/src/main/java/com/stfalcon/multiimageview/MultiImageView.kt deleted file mode 100644 index 243c0dc..0000000 --- a/multiimageview/src/main/java/com/stfalcon/multiimageview/MultiImageView.kt +++ /dev/null @@ -1,180 +0,0 @@ -/******************************************************************************* - * Copyright 2016 stfalcon.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ - -package com.stfalcon.multiimageview - -import android.content.Context -import android.graphics.* -import android.graphics.drawable.Drawable -import android.media.ThumbnailUtils -import android.util.AttributeSet -import android.widget.ImageView -import java.util.* - -/** - * Created by Anton Bevza on 12/22/16. - */ - -class MultiImageView(context: Context, attrs: AttributeSet) : ImageView(context, attrs) { - //Shape of view - var shape = Shape.NONE - set(value) { - field = value - invalidate() - } - //Corners radius for rectangle shape - var rectCorners = 100 - - private val bitmaps = ArrayList() - private val path = Path() - private val rect = RectF() - private var multiDrawable: Drawable? = null - - /** - * Add image to view - */ - fun addImage(bitmap: Bitmap) { - bitmaps.add(bitmap) - refresh() - } - - /** - * Remove all images - */ - fun clear() { - bitmaps.clear() - refresh() - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - refresh() - } - - /** - * recreate MultiDrawable and set it as Drawable to ImageView - */ - private fun refresh() { - multiDrawable = MultiDrawable(bitmaps) - setImageDrawable(multiDrawable) - } - - override fun onDraw(canvas: Canvas?) { - if (canvas != null) { - if (drawable != null) { - //if shape not set - just draw - if (shape != Shape.NONE) { - path.reset() - //ImageView size - rect.set(0f, 0f, width.toFloat(), height.toFloat()) - if (shape == Shape.RECTANGLE) { - //Rectangle with corners - path.addRoundRect(rect, rectCorners.toFloat(), - rectCorners.toFloat(), Path.Direction.CW) - } else { - //Oval - path.addOval(rect, Path.Direction.CW) - } - //Clip with shape - canvas.clipPath(path) - } - super.onDraw(canvas) - } - } - } - - //Types of shape - enum class Shape { - CIRCLE, RECTANGLE, NONE - } -} - -class MultiDrawable(val bitmaps: ArrayList) : Drawable() { - private val paint = Paint(Paint.ANTI_ALIAS_FLAG) - private val items = ArrayList() - - /** - * Create PhotoItem with position and size depends of count of images - */ - private fun init() { - items.clear() - if (bitmaps.size == 1) { - val bitmap = scaleCenterCrop(bitmaps[0], bounds.width(), bounds.height()) - items.add(PhotoItem(bitmap, Rect(0, 0, bounds.width(), bounds.height()))) - } else if (bitmaps.size == 2) { - val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width(), bounds.height() / 2) - val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width(), bounds.height() / 2) - items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width() / 2, bounds.height()))) - items.add(PhotoItem(bitmap2, Rect(bounds.width() / 2, 0, bounds.width(), bounds.height()))) - } else if (bitmaps.size == 3) { - val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width(), bounds.height() / 2) - val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width() / 2, bounds.height() / 2) - val bitmap3 = scaleCenterCrop(bitmaps[2], bounds.width() / 2, bounds.height() / 2) - items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width() / 2, bounds.height()))) - items.add(PhotoItem(bitmap2, Rect(bounds.width() / 2, 0, bounds.width(), bounds.height() / 2))) - items.add(PhotoItem(bitmap3, Rect(bounds.width() / 2, bounds.height() / 2, bounds.width(), bounds.height()))) - } - if (bitmaps.size == 4) { - val bitmap1 = scaleCenterCrop(bitmaps[0], bounds.width() / 2, bounds.height() / 2) - val bitmap2 = scaleCenterCrop(bitmaps[1], bounds.width() / 2, bounds.height() / 2) - val bitmap3 = scaleCenterCrop(bitmaps[2], bounds.width() / 2, bounds.height() / 2) - val bitmap4 = scaleCenterCrop(bitmaps[3], bounds.width() / 2, bounds.height() / 2) - items.add(PhotoItem(bitmap1, Rect(0, 0, bounds.width() / 2, bounds.height() / 2))) - items.add(PhotoItem(bitmap2, Rect(0, bounds.height() / 2, bounds.width() / 2, bounds.height()))) - items.add(PhotoItem(bitmap3, Rect(bounds.width() / 2, 0, bounds.width(), bounds.height() / 2))) - items.add(PhotoItem(bitmap4, Rect(bounds.width() / 2, bounds.height() / 2, bounds.width(), bounds.height()))) - } - } - - override fun draw(canvas: Canvas?) { - if (canvas != null) { - items.forEach { - canvas.drawBitmap(it.bitmap, bounds, it.position, paint) - } - } - } - - /** - * scale and center crop image - */ - private fun scaleCenterCrop(source: Bitmap, newHeight: Int, newWidth: Int): Bitmap { - return ThumbnailUtils.extractThumbnail(source, newWidth, newHeight) - } - - /*** - * Data class for store bitmap and position - */ - data class PhotoItem(val bitmap: Bitmap, val position: Rect) - - - //***Needed to override***// - override fun setAlpha(alpha: Int) { - paint.alpha = alpha - } - - override fun onBoundsChange(bounds: Rect) { - super.onBoundsChange(bounds) - init() - } - - override fun getOpacity() = PixelFormat.TRANSLUCENT - - override fun setColorFilter(colorFilter: ColorFilter) { - paint.colorFilter = colorFilter - } - //***------------------***// -} - diff --git a/multiimageview/src/main/res/values/attrs.xml b/multiimageview/src/main/res/values/attrs.xml new file mode 100755 index 0000000..4fe4ce5 --- /dev/null +++ b/multiimageview/src/main/res/values/attrs.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/multiimageview/src/test/java/com/stfalcon/multiimageview/ExampleUnitTest.java b/multiimageview/src/test/java/com/sealstudios/multiimageview/ExampleUnitTest.java similarity index 90% rename from multiimageview/src/test/java/com/stfalcon/multiimageview/ExampleUnitTest.java rename to multiimageview/src/test/java/com/sealstudios/multiimageview/ExampleUnitTest.java index 533d5ec..7c24778 100644 --- a/multiimageview/src/test/java/com/stfalcon/multiimageview/ExampleUnitTest.java +++ b/multiimageview/src/test/java/com/sealstudios/multiimageview/ExampleUnitTest.java @@ -1,4 +1,4 @@ -package com.stfalcon.multiimageview; +package com.sealstudios.multiimageview; import org.junit.Test; diff --git a/sample/build.gradle b/sample/build.gradle index 35cf6de..2d2721f 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,15 +1,16 @@ apply plugin: 'com.android.application' +apply plugin: "androidx.navigation.safeargs" android { - compileSdkVersion 25 - buildToolsVersion "25.0.0" + compileSdkVersion 29 + buildToolsVersion "29.0.2" defaultConfig { - applicationId "com.stfalcon.multiimageview.sample" + applicationId "com.sealstudios.multiimageview.sample" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 29 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -20,11 +21,31 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.0.0' - testCompile 'junit:junit:4.12' - compile project (':multiimageview') + implementation 'androidx.appcompat:appcompat:1.2.0' + + //Navigation + implementation "androidx.navigation:navigation-fragment:2.3.0" + //Navigation ui + implementation "androidx.navigation:navigation-ui:2.3.0" + + //Design + implementation "androidx.legacy:legacy-support-core-utils:1.0.0" + implementation 'com.google.android.material:material:1.3.0-alpha02' + implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha05' + implementation "androidx.constraintlayout:constraintlayout:2.0.1" + + //Glide + implementation ("com.github.bumptech.glide:glide:4.11.0") { + exclude group: "com.android.support" + } + annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' + annotationProcessor 'androidx.annotation:annotation:1.1.0' + +// testImpelementation 'junit:junit:4.12' + implementation project (':multiimageview') + } diff --git a/sample/src/androidTest/java/com/stfalcon/multiimageview/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/com/sealstudios/multiimageview/sample/ExampleInstrumentedTest.java similarity index 93% rename from sample/src/androidTest/java/com/stfalcon/multiimageview/sample/ExampleInstrumentedTest.java rename to sample/src/androidTest/java/com/sealstudios/multiimageview/sample/ExampleInstrumentedTest.java index ae84188..b217159 100644 --- a/sample/src/androidTest/java/com/stfalcon/multiimageview/sample/ExampleInstrumentedTest.java +++ b/sample/src/androidTest/java/com/sealstudios/multiimageview/sample/ExampleInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.stfalcon.multiimageview.sample; +package com.sealstudios.multiimageview.sample; import android.content.Context; import android.support.test.InstrumentationRegistry; diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 6b42df4..5afe00d 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.sealstudios.multiimageview.sample"> - + diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/MainActivity.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/MainActivity.java new file mode 100644 index 0000000..af81609 --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/MainActivity.java @@ -0,0 +1,28 @@ +package com.sealstudios.multiimageview.sample; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; + +public class MainActivity extends AppCompatActivity { + + private NavController navController; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + navController = Navigation.findNavController(this, R.id.main_navigation_fragment); + + } + + + @Override + public boolean onSupportNavigateUp() { + return navController.navigateUp() || + super.onSupportNavigateUp(); + } + +} diff --git a/sample/src/main/java/com/stfalcon/multiimageview/sample/MainActivity.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/MainFragment.java similarity index 52% rename from sample/src/main/java/com/stfalcon/multiimageview/sample/MainActivity.java rename to sample/src/main/java/com/sealstudios/multiimageview/sample/MainFragment.java index 885a633..7fa2926 100644 --- a/sample/src/main/java/com/stfalcon/multiimageview/sample/MainActivity.java +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/MainFragment.java @@ -1,24 +1,48 @@ -package com.stfalcon.multiimageview.sample; +package com.sealstudios.multiimageview.sample; import android.graphics.BitmapFactory; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.Button; -import com.stfalcon.multiimageview.MultiImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.NavigationUI; + +import com.sealstudios.multiimageview.MultiImageView; + +public class MainFragment extends Fragment { -public class MainActivity extends AppCompatActivity { private int imageCount = 0; + private NavController navController; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - final MultiImageView multiImageView = (MultiImageView) findViewById(R.id.iv); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_main, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + navController = Navigation.findNavController(view); + NavigationUI.setupActionBarWithNavController((AppCompatActivity) view.getContext(), + navController); + final MultiImageView multiImageView = view.findViewById(R.id.iv); //add images - Button button = (Button) findViewById(R.id.button); + Button button = view.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -42,7 +66,7 @@ public void onClick(View view) { multiImageView.setRectCorners(50); //Change shape of image - Button buttonShape = (Button) findViewById(R.id.buttonShape); + Button buttonShape = view.findViewById(R.id.buttonShape); buttonShape.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -56,6 +80,14 @@ public void onClick(View view) { } }); + Button recyclerExample = view.findViewById(R.id.recycler_view_example_button); + recyclerExample.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + navController.navigate(R.id.action_mainFragment_to_recyclerViewExample); + } + }); + multiImageView.clear(); } } diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/PreviewFragment.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/PreviewFragment.java new file mode 100644 index 0000000..4c9fc41 --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/PreviewFragment.java @@ -0,0 +1,87 @@ +package com.sealstudios.multiimageview.sample; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.transition.TransitionInflater; + +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; +import com.sealstudios.multiimageview.MultiImageView; +import com.sealstudios.multiimageview.sample.glide.GlideApp; +import com.sealstudios.multiimageview.sample.models.MultiImageViewModel; + +public class PreviewFragment extends Fragment { + + private MultiImageViewModel model; + private String TAG = "PreviewFragment"; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + getFragmentArguments(); + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + androidx.transition.Transition transition = TransitionInflater.from(getActivity()) + .inflateTransition(android.R.transition.move); + setSharedElementReturnTransition(transition); + setSharedElementEnterTransition(transition); + setExitTransition(transition); + return inflater.inflate(R.layout.preview_layout, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + final MultiImageView multiImageView = view.findViewById(R.id.iv); + if (model != null){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + multiImageView.setTransitionName("transitionName-" + model.getId()); + } + + setPreviewImages(view, multiImageView); + } + + } + + private void setPreviewImages(@NonNull View view, final MultiImageView multiImageView) { + + for (int d : model.getImages()){ + + GlideApp.with(view).asBitmap().load(d).into(new CustomTarget() { + + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + Log.d(TAG,"set image " + resource.hashCode()); + multiImageView.addImage(resource); + Log.d(TAG,"added " + multiImageView.getBitmapCount() + " image(s)"); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }); + } + } + + private void getFragmentArguments() { + if (getArguments() != null) { + PreviewFragmentArgs args = PreviewFragmentArgs.fromBundle(getArguments()); + model = args.getMultiImageViewModel(); + } + } + +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/RecyclerViewExample.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/RecyclerViewExample.java new file mode 100644 index 0000000..729202d --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/RecyclerViewExample.java @@ -0,0 +1,144 @@ +package com.sealstudios.multiimageview.sample; + +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.fragment.FragmentNavigator; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.sealstudios.multiimageview.MultiImageView; +import com.sealstudios.multiimageview.sample.adapters.MultiImageViewAdapter; +import com.sealstudios.multiimageview.sample.decorator.MultiImageViewItemDecorator; +import com.sealstudios.multiimageview.sample.glide.GlideApp; +import com.sealstudios.multiimageview.sample.helpers.ClickHelper; +import com.sealstudios.multiimageview.sample.models.MultiImageViewModel; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +public class RecyclerViewExample extends Fragment implements ClickHelper { + + private MultiImageViewAdapter multiImageViewAdapter; + private int[] avatars = new int[]{R.drawable.avatar1,R.drawable.avatar2,R.drawable.avatar3,R.drawable.avatar4}; + private int[] structuredImageListCount = new int[]{1,2,3,4,1,2,3,4,1,2}; + private String TAG = "RecyclerViewExample"; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.recycler_view_layout, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + multiImageViewAdapter = new MultiImageViewAdapter(getActivity(), new ArrayList(), this, GlideApp.with(this)); + RecyclerView recyclerView = view.findViewById(R.id.recycler_view); + GridLayoutManager gridLayoutManager = new GridLayoutManager(view.getContext(), 3, RecyclerView.VERTICAL, false); + recyclerView.setAdapter(multiImageViewAdapter); + recyclerView.setLayoutManager(gridLayoutManager); + recyclerView.addItemDecoration(new MultiImageViewItemDecorator(view.getResources().getDimensionPixelSize(R.dimen.grid_spacing))); + refreshAdapterWithStructure(); + } + + + private List createRandomModelList(){ + List modelList = new ArrayList<>(); + for (int i = 0; i < 10; i++){ + MultiImageViewModel model = new MultiImageViewModel(); + model.setId("id-" + i); + model.setImages(createRandomModelImageList()); + modelList.add(model); + } + return modelList; + } + + private LinkedList createRandomModelImageList(){ + Random rand = new Random(); + int n = rand.nextInt(4) + 1; + LinkedList drawables = new LinkedList<>(); + + for (int i = 0; i < n; i++){ + drawables.add(avatars[i]); + } + return drawables; + } + + private LinkedList createStructuredModelList(){ + LinkedList modelList = new LinkedList<>(); + for (int i = 0; i < 30; i++){ + MultiImageViewModel model = new MultiImageViewModel(); + model.setId("id-" + i); + model.setImages(createStructuredImageList(structuredImageListCount[i / 3])); + modelList.add(model); + } + return modelList; + } + + private LinkedList createStructuredImageList(int index){ + + LinkedList drawables = new LinkedList<>(); + + for (int i = 0; i < index; i++){ + drawables.add(avatars[i]); + } + return drawables; + } + + private void refreshAdapterWithStructure(){ + multiImageViewAdapter.refreshAdapter(createStructuredModelList()); + } + + private void refreshAdapterWithRandom(){ + multiImageViewAdapter.refreshAdapter(createRandomModelList()); + } + + private void navigateToPreview(MultiImageViewModel model, View v){ + NavController navController = Navigation.findNavController(v); + + RecyclerViewExampleDirections.ActionRecyclerViewExampleToPreviewFragment directions = RecyclerViewExampleDirections.actionRecyclerViewExampleToPreviewFragment(); + directions.setMultiImageViewModel(model); + + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + + FragmentNavigator.Extras.Builder extrasBuilder = + new FragmentNavigator.Extras.Builder(); + extrasBuilder.addSharedElement(v, v.getTransitionName()); + + navController.navigate(directions, extrasBuilder.build()); + + } else { + + navController.navigate(directions); + + } + + + + } + + @Override + public void click(View v, int position) { + MultiImageView multiImageView = v.findViewById(R.id.iv); + Log.d(TAG,"multiImageView bitmap count " + multiImageView.getBitmapCount()); + navigateToPreview(multiImageViewAdapter.getItem(position), v); + } +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/MultiImageViewAdapter.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/MultiImageViewAdapter.java new file mode 100644 index 0000000..703e0d7 --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/MultiImageViewAdapter.java @@ -0,0 +1,120 @@ +package com.sealstudios.multiimageview.sample.adapters; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.request.target.CustomTarget; +import com.sealstudios.multiimageview.sample.R; +import com.sealstudios.multiimageview.sample.adapters.view_holders.MyMultiImageViewHolder; +import com.sealstudios.multiimageview.sample.helpers.ClickHelper; +import com.sealstudios.multiimageview.sample.models.MultiImageViewModel; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class MultiImageViewAdapter extends RecyclerView.Adapter { + + private Context context; + private List multiImageViewModels; + private ClickHelper clickHelper; + private RequestManager glide; + private String TAG = "MultiImageViewAdapter"; + + public MultiImageViewAdapter(Context context, List multiImageViewModels, ClickHelper clickHelper, + RequestManager glide) { + this.context = context; + this.glide = glide; + this.clickHelper = clickHelper; + this.multiImageViewModels = multiImageViewModels; + } + + @NonNull + @Override + public MyMultiImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new MyMultiImageViewHolder(LayoutInflater.from(context).inflate(R.layout.view_holder, parent, false), clickHelper); + } + + @Override + public void onBindViewHolder(@NonNull final MyMultiImageViewHolder holder, int position) { + MultiImageViewModel model = multiImageViewModels.get(position); + holder.multiImageView.clear(); + holder.imageSetCounter.set(0); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + holder.multiImageView.setTransitionName("transitionName-" + model.getId()); + } + + if (!model.getImages().isEmpty()){ +// setImages(holder, model.getImages(), holder.imageSetCounter); + getAndSetImages(holder,model.getImages(), holder.imageSetCounter, new ArrayList()); + } + + } + + private void getAndSetImages(final MyMultiImageViewHolder holder, final List imageIdentifiers, final AtomicInteger imageSetCount, final List bitmaps){ + + glide.asBitmap().load(imageIdentifiers.get(imageSetCount.get())).into(new CustomTarget() { + + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable com.bumptech.glide.request.transition.Transition transition) { + bitmaps.add(resource); + if (imageSetCount.get() < imageIdentifiers.size() - 1){ + imageSetCount.getAndIncrement(); + getAndSetImages(holder,imageIdentifiers, imageSetCount, bitmaps); + } else { + holder.multiImageView.addAllImages(bitmaps); + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }); + } + + private void setImages(@NonNull final MyMultiImageViewHolder holder, final List imageIdentifiers, final AtomicInteger imageSetCount) { + + glide.asBitmap().load(imageIdentifiers.get(imageSetCount.get())).into(new CustomTarget() { + + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable com.bumptech.glide.request.transition.Transition transition) { + holder.multiImageView.addImage(resource); + if (imageSetCount.get() < imageIdentifiers.size() - 1){ + imageSetCount.getAndIncrement(); + setImages(holder, imageIdentifiers, imageSetCount); + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }); + } + + public MultiImageViewModel getItem(int position){ + return multiImageViewModels.get(position); + } + + public void refreshAdapter(List models){ + multiImageViewModels.clear(); + multiImageViewModels.addAll(models); + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return multiImageViewModels.size(); + } +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/view_holders/MyMultiImageViewHolder.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/view_holders/MyMultiImageViewHolder.java new file mode 100644 index 0000000..d2e53fe --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/adapters/view_holders/MyMultiImageViewHolder.java @@ -0,0 +1,33 @@ +package com.sealstudios.multiimageview.sample.adapters.view_holders; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.sealstudios.multiimageview.MultiImageView; +import com.sealstudios.multiimageview.sample.R; +import com.sealstudios.multiimageview.sample.helpers.ClickHelper; + +import java.util.concurrent.atomic.AtomicInteger; + +public class MyMultiImageViewHolder extends RecyclerView.ViewHolder { + + public MultiImageView multiImageView; + public AtomicInteger imageSetCounter = new AtomicInteger(0); + + public MyMultiImageViewHolder(@NonNull View itemView, final ClickHelper clickHelper) { + super(itemView); + multiImageView = itemView.findViewById(R.id.iv); + multiImageView.clear(); + multiImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + clickHelper.click(v, getAdapterPosition()); + } + }); + } + + + +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/decorator/MultiImageViewItemDecorator.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/decorator/MultiImageViewItemDecorator.java new file mode 100644 index 0000000..4f01c72 --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/decorator/MultiImageViewItemDecorator.java @@ -0,0 +1,31 @@ +package com.sealstudios.multiimageview.sample.decorator; + +import android.graphics.Rect; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +public class MultiImageViewItemDecorator extends RecyclerView.ItemDecoration { + + private final int spacing; + + public MultiImageViewItemDecorator(int spacing) { + this.spacing = spacing; + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + + int position = parent.getChildLayoutPosition(view); + + if (position % 2 != 0) { + outRect.right = spacing; + } + + outRect.left = spacing; + outRect.bottom = spacing; + + } +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/glide/MyGlideApp.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/glide/MyGlideApp.java new file mode 100644 index 0000000..db9a6da --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/glide/MyGlideApp.java @@ -0,0 +1,10 @@ +package com.sealstudios.multiimageview.sample.glide; + + +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.module.AppGlideModule; + +@GlideModule +public class MyGlideApp extends AppGlideModule { + +} \ No newline at end of file diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/helpers/ClickHelper.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/helpers/ClickHelper.java new file mode 100644 index 0000000..6b5e5d4 --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/helpers/ClickHelper.java @@ -0,0 +1,7 @@ +package com.sealstudios.multiimageview.sample.helpers; + +import android.view.View; + +public interface ClickHelper { + void click(View v, int position); +} diff --git a/sample/src/main/java/com/sealstudios/multiimageview/sample/models/MultiImageViewModel.java b/sample/src/main/java/com/sealstudios/multiimageview/sample/models/MultiImageViewModel.java new file mode 100644 index 0000000..aab353a --- /dev/null +++ b/sample/src/main/java/com/sealstudios/multiimageview/sample/models/MultiImageViewModel.java @@ -0,0 +1,62 @@ +package com.sealstudios.multiimageview.sample.models; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.LinkedList; + +public class MultiImageViewModel implements Parcelable { + + private String id; + private LinkedList images; + + public MultiImageViewModel() { + } + + public MultiImageViewModel(String id, LinkedList images) { + this.id = id; + this.images = images; + } + + protected MultiImageViewModel(Parcel in) { + id = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public MultiImageViewModel createFromParcel(Parcel in) { + return new MultiImageViewModel(in); + } + + @Override + public MultiImageViewModel[] newArray(int size) { + return new MultiImageViewModel[size]; + } + }; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public LinkedList getImages() { + return images; + } + + public void setImages(LinkedList images) { + this.images = images; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(id); + } +} diff --git a/sample/src/main/res/drawable/screenshot_1582383274.png b/sample/src/main/res/drawable/screenshot_1582383274.png new file mode 100644 index 0000000..10ec5f1 Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383274.png differ diff --git a/sample/src/main/res/drawable/screenshot_1582383276.png b/sample/src/main/res/drawable/screenshot_1582383276.png new file mode 100644 index 0000000..10ec5f1 Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383276.png differ diff --git a/sample/src/main/res/drawable/screenshot_1582383280.png b/sample/src/main/res/drawable/screenshot_1582383280.png new file mode 100644 index 0000000..c588471 Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383280.png differ diff --git a/sample/src/main/res/drawable/screenshot_1582383283.png b/sample/src/main/res/drawable/screenshot_1582383283.png new file mode 100644 index 0000000..b9fe763 Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383283.png differ diff --git a/sample/src/main/res/drawable/screenshot_1582383286.png b/sample/src/main/res/drawable/screenshot_1582383286.png new file mode 100644 index 0000000..ecaae2f Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383286.png differ diff --git a/sample/src/main/res/drawable/screenshot_1582383289.png b/sample/src/main/res/drawable/screenshot_1582383289.png new file mode 100644 index 0000000..020ebb2 Binary files /dev/null and b/sample/src/main/res/drawable/screenshot_1582383289.png differ diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 4acd1a5..0883197 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,33 +1,18 @@ + tools:context="com.sealstudios.multiimageview.sample.MainActivity"> - - -