diff --git a/.gitignore b/.gitignore index 39fb081..e02281d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,36 @@ +#built application files +*.apk +*.ap_ +*.aab + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ + +# Local configuration file (sdk path, etc) +local.properties + +# Windows thumbnail db +Thumbs.db + +# OSX files +.DS_Store + +# Android Studio *.iml +.idea .gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.externalNativeBuild +build/ +.navigation +captures/ +output.json + +#NDK +obj/ +.externalNativeBuild \ No newline at end of file diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser deleted file mode 100644 index 193855e..0000000 Binary files a/.idea/caches/build_file_checksums.ser and /dev/null differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 88ea3aa..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - -
- - - - 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/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index d085a30..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index e34606c..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml deleted file mode 100644 index 12fb99d..0000000 --- a/.idea/markdown-navigator-enh.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml deleted file mode 100644 index 4463382..0000000 --- a/.idea/markdown-navigator.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index e0d5b93..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 03f44ac..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 7845930..8e6b8c0 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,12 @@ A ViewPager and a PagerAdapter that can: 1. AutoScroll (On/Off able) 2. Infinite Loop (On/Off able) -3. ViewPager's height can be wrap_content / an aspect ratio -4. Adjustable auto scroll interval -5. Won't scroll nor loop if there is only 1 item -6. Works well with notifyDataSetChanged() -7. Supports page indicators -8. Supports different view types -9. **(New! 1.2.0)** Support peeking adjacent items (But first and last item will appear only after scroll state is idle) +3. Adjustable auto scroll interval +4. Won't scroll nor loop if there is only 1 item +5. Works well with notifyDataSetChanged() +6. Supports page indicators +7. Supports different view types +7. Support peeking adjacent items (But first and last item will appear only after scroll state is idle) ## Demo Effect @@ -29,22 +28,21 @@ I cannot find one that fits all of the below requirements: 2. Last updated in less than 3 years 3. Good infinite looping effect 4. Configurable auto-scroll -5. ViewPager that supports fixed aspect ratio -6. Good support with Page Indicators +5. Good support with Page Indicators -Especially for 6, even some of them supports, they provide built-in indicators only; or does not tell user how to implement their own indicator. +Especially for 5, even some of them supports, they provide built-in indicators only; or does not tell user how to implement their own indicator. I wrote this library to tackle all of these problems I faced after trying a whole day with other libraries. ## Usage ### Add to Project -First make sure `jcenter()` is included as a repository in your **project**'s build.gradle: +First make sure `mavenCentral()` is included as a repository in your **project**'s build.gradle: ```groovy allprojects { repositories { - jcenter() + mavenCentral() } } ``` @@ -52,7 +50,7 @@ allprojects { And then add the below to your app's build.gradle: ```groovy - implementation 'com.asksira.android:loopingviewpager:1.3.1' + implementation 'com.asksira.android:loopingviewpager:1.4.1' ``` ### Step 1: Create LoopingViewPager in XML @@ -60,37 +58,28 @@ And then add the below to your app's build.gradle: ```xml + app:layout_constraintDimensionRatio="1.78"/> ``` +(The above xml example is placed inside a ConstraintLayout.) + | Attribute Name | Default | Allowed Values | |:-------------------------|:--------|:------------------------------| | isInfinite | false | true / false | | autoScroll | false | true / false | -| viewpagerAspectRatio | 0 | any float (width / height) | -| wrap_content(deprecated) | true | true / false | | scrollInterval | 5000 | any integer (represents ms) | -| itemAspectRatio | 0 | any float (width / height) | - -viewpagerAspectRatio 0 means does not apply aspectRatio. -That means, default LoopingViewPager has no aspect ratio and wrap_content is true. -Once aspect ratio is set, wrap_content will be overridden (meaningless). -In most cases, you should set an aspect ratio. +Please note that the height of `LoopingViewPager` should be decided by its parent, like all other Views. +If you want it to have a specific aspect ratio, you can place it inside a `ConstraintLayout` and give it the attribute `layout_constraintDimensionRatio`. -`itemAspectRatio` is the aspectRatio of the the item. So, if itemAspectRatio is higher than viewpagerAspectRatio, you can use `clipToPadding="false"` to create a peek item effect. -You can refer to [#17](https://github.com/siralam/LoopingViewPager/issues/17). +(The old versions of this library uses an internal attribute to determine its height, which is completely wrong and can lead to bugs!) -If you wonder why you need to set `app:wrap_content="true"`, take a look at [this Stackoverflow post](https://stackoverflow.com/questions/8394681/android-i-am-unable-to-have-viewpager-wrap-content). - -**wrap_content is deprecated in v1.1.3**. It is still available but I think there can be unknown problems. I am not going to debug issues related to setting wrap_content to true as well. +Note: If you want the ViewPager to be able to peek adjacent items, make use of `clipToPadding=false` and set padding to the ViewPager. (`itemAspectRatio` has been removed in v1.4.1) ### Step 2: Create your PagerAdapter that extends LoopingPagerAdapter @@ -102,10 +91,9 @@ You should ```kotlin class DemoInfiniteAdapter( - context: Context, itemList: ArrayList, isInfinite: Boolean -) : LoopingPagerAdapter(context, itemList, isInfinite) { +) : LoopingPagerAdapter(itemList, isInfinite) { //This method will be triggered if the item View has not been inflated before. override fun inflateView( @@ -113,7 +101,7 @@ class DemoInfiniteAdapter( container: ViewGroup, listPosition: Int ): View { - return LayoutInflater.from(context).inflate(R.layout.item_pager, container, false) + return LayoutInflater.from(container.context).inflate(R.layout.item_pager, container, false) } //Bind your data with your item View here. @@ -125,7 +113,7 @@ class DemoInfiniteAdapter( listPosition: Int, viewType: Int ) { - convertView.findViewById(R.id.image).setBackgroundColor(context.resources.getColor(getBackgroundColor(listPosition))) + convertView.findViewById(R.id.image).setBackgroundColor(convertView.context.resources.getColor(getBackgroundColor(listPosition))) val description = convertView.findViewById(R.id.description) description.text = itemList?.get(listPosition).toString() } @@ -135,7 +123,7 @@ class DemoInfiniteAdapter( ### Step 3: Bind LoopingViewPager with your Adapter ```kotlin - adapter = DemoInfiniteAdapter(context, dataItems, true) + adapter = DemoInfiniteAdapter(dataItems, true) loopingViewPager.setAdapter(adapter) ``` @@ -252,6 +240,12 @@ customShapePagerIndicator.updateIndicatorCounts(loopingViewPager.indicatorCount) ## Release notes +v1.4.1 +- Removed setting aspect ratio inside LoopingViewPager. Let its parent to decide how to layout it (i.e. its height). +- Same as above, removed itemAspectRatio as well. You can actually simply set it through padding and `clipToPadding=false`. +- Removed unnecessary context as a param in LoopingPagerAdapter's constructor. +- Migrated from JCenter() to MavenCentral(). + v1.3.2 - Fixed crash due to getChildAt() returns null diff --git a/app/build.gradle b/app/build.gradle index f8ae5aa..b2d02c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,13 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { applicationId "com.asksira.loopingviewpagerdemo" minSdkVersion 16 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -22,13 +21,10 @@ android { } dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation project(':loopingviewpager') - implementation "androidx.core:core-ktx:+" +// implementation 'com.asksira.android:loopingviewpager:1.4.1' + implementation "androidx.core:core-ktx:1.3.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } -repositories { - mavenCentral() -} diff --git a/app/src/main/java/com/asksira/loopingviewpagerdemo/DemoInfiniteAdapter.kt b/app/src/main/java/com/asksira/loopingviewpagerdemo/DemoInfiniteAdapter.kt index 18584f1..ec2d629 100644 --- a/app/src/main/java/com/asksira/loopingviewpagerdemo/DemoInfiniteAdapter.kt +++ b/app/src/main/java/com/asksira/loopingviewpagerdemo/DemoInfiniteAdapter.kt @@ -1,6 +1,5 @@ package com.asksira.loopingviewpagerdemo -import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,10 +8,9 @@ import com.asksira.loopingviewpager.LoopingPagerAdapter import java.util.* class DemoInfiniteAdapter( - context: Context, itemList: ArrayList, isInfinite: Boolean -) : LoopingPagerAdapter(context, itemList, isInfinite) { +) : LoopingPagerAdapter(itemList, isInfinite) { override fun getItemViewType(listPosition: Int): Int { return if (itemList?.get(listPosition) == 0) VIEW_TYPE_SPECIAL else VIEW_TYPE_NORMAL @@ -24,8 +22,8 @@ class DemoInfiniteAdapter( listPosition: Int ): View { return if (viewType == VIEW_TYPE_SPECIAL) LayoutInflater.from( - context - ).inflate(R.layout.item_special, container, false) else LayoutInflater.from(context) + container.context + ).inflate(R.layout.item_special, container, false) else LayoutInflater.from(container.context) .inflate(R.layout.item_pager, container, false) } @@ -36,7 +34,7 @@ class DemoInfiniteAdapter( ) { if (viewType == VIEW_TYPE_SPECIAL) return convertView.findViewById(R.id.image) - .setBackgroundColor(context.resources.getColor(getBackgroundColor(listPosition))) + .setBackgroundColor(convertView.context.resources.getColor(getBackgroundColor(listPosition))) val description = convertView.findViewById(R.id.description) description.text = itemList?.get(listPosition).toString() } diff --git a/app/src/main/java/com/asksira/loopingviewpagerdemo/MainActivity.kt b/app/src/main/java/com/asksira/loopingviewpagerdemo/MainActivity.kt index 881b1b3..3a6bc8d 100644 --- a/app/src/main/java/com/asksira/loopingviewpagerdemo/MainActivity.kt +++ b/app/src/main/java/com/asksira/loopingviewpagerdemo/MainActivity.kt @@ -39,7 +39,8 @@ class MainActivity : AppCompatActivity() { page4 = findViewById(R.id.page4) page5 = findViewById(R.id.page5) page6 = findViewById(R.id.page6) - adapter = DemoInfiniteAdapter(this, createDummyItems(), true) + + adapter = DemoInfiniteAdapter(createDummyItems(), true) viewPager.adapter = adapter //Custom bind indicator diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c7c8ba1..fa20a17 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -9,15 +9,16 @@ ( - protected var context: Context, itemList: List, isInfinite: Boolean ) : PagerAdapter() { diff --git a/loopingviewpager/src/main/java/com/asksira/loopingviewpager/LoopingViewPager.kt b/loopingviewpager/src/main/java/com/asksira/loopingviewpager/LoopingViewPager.kt index 04b3d0a..6322220 100644 --- a/loopingviewpager/src/main/java/com/asksira/loopingviewpager/LoopingViewPager.kt +++ b/loopingviewpager/src/main/java/com/asksira/loopingviewpager/LoopingViewPager.kt @@ -2,11 +2,10 @@ package com.asksira.loopingviewpager import android.content.Context import android.os.Handler +import android.os.Looper import android.util.AttributeSet import androidx.viewpager.widget.PagerAdapter import androidx.viewpager.widget.ViewPager -import kotlin.math.floor -import kotlin.math.roundToInt /** * A ViewPager that auto-scrolls, and supports infinite scroll. @@ -16,14 +15,12 @@ class LoopingViewPager : ViewPager { protected var isInfinite = true protected var isAutoScroll = false protected var wrapContent = true - protected var aspectRatio = 0f - protected var itemAspectRatio = 0f //AutoScroll private var interval = 5000 private var currentPagePosition = 0 private var isAutoScrollResumed = false - private val autoScrollHandler = Handler() + private val autoScrollHandler = Handler(Looper.getMainLooper()) private val autoScrollRunnable = Runnable { if (adapter == null || !isAutoScroll || adapter?.count ?: 0 < 2) return@Runnable if (!isInfinite && adapter?.count ?: 0 - 1 == currentPagePosition) { @@ -54,8 +51,6 @@ class LoopingViewPager : ViewPager { isAutoScroll = a.getBoolean(R.styleable.LoopingViewPager_autoScroll, false) wrapContent = a.getBoolean(R.styleable.LoopingViewPager_wrap_content, true) interval = a.getInt(R.styleable.LoopingViewPager_scrollInterval, 5000) - aspectRatio = a.getFloat(R.styleable.LoopingViewPager_viewpagerAspectRatio, 0f) - itemAspectRatio = a.getFloat(R.styleable.LoopingViewPager_itemAspectRatio, 0f) isAutoScrollResumed = isAutoScroll } finally { a.recycle() @@ -63,96 +58,6 @@ class LoopingViewPager : ViewPager { init() } - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - var heightMeasureSpec = heightMeasureSpec - val width = MeasureSpec.getSize(widthMeasureSpec) - if (aspectRatio > 0) { - val height = - (MeasureSpec.getSize(widthMeasureSpec).toFloat() / aspectRatio).roundToInt() - val finalWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY) - val finalHeightMeasureSpec = - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) - - /* - * If child items can scale, fit inside their parent by increasing left/right padding. - * https://github.com/siralam/LoopingViewPager/issues/17 - */if (itemAspectRatio > 0 && itemAspectRatio != aspectRatio) { - // super has to be called in the beginning so the child views can be initialized. - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - // Remove padding from width - var childWidthSize = width - paddingLeft - paddingRight - // Make child width MeasureSpec - var childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY) - var i = 0 - while (i < childCount) { - val child = getChildAt(i) - child.measure( - childWidthMeasureSpec, - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) - ) - val w = child.measuredWidth - val h = child.measuredHeight - if (h > 0 && h > height) { - val ratio = w.toFloat() / h - // Round down largest width that fits. - val optimalWidth = - floor(height * ratio.toDouble()) - // Round up new padding size. - val newPadding = - ((width - optimalWidth) / 2).roundToInt() - // Set new padding values - setPadding(newPadding, paddingTop, newPadding, paddingBottom) - // Remove padding from width - childWidthSize = width - paddingLeft - paddingRight - // Make child width MeasureSpec - childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY) - child.measure( - childWidthMeasureSpec, - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) - ) - } else { - i++ - } - } - } - super.onMeasure(finalWidthMeasureSpec, finalHeightMeasureSpec) - } else { - //https://stackoverflow.com/a/24666987/7870874 - if (wrapContent) { - val mode = MeasureSpec.getMode(heightMeasureSpec) - // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT. - // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT. - if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) { - // super has to be called in the beginning so the child views can be initialized. - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - var height = 0 - // Remove padding from width - val childWidthSize = width - paddingLeft - paddingRight - // Make child width MeasureSpec - val childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY) - for (i in 0 until childCount) { - val child = getChildAt(i) - child.measure( - childWidthMeasureSpec, - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) - ) - val h = child.measuredHeight - if (h > height) { - height = h - } - } - // Add padding back to child height - height += paddingTop + paddingBottom - heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) - } - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - } - } - protected fun init() { addOnPageChangeListener(object : OnPageChangeListener { override fun onPageScrolled(