diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..07fccd2
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,38 @@
+apply plugin: 'com.android.application'
+
+apply plugin: 'kotlin-android'
+
+apply plugin: 'kotlin-android-extensions'
+
+
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.1"
+ defaultConfig {
+ applicationId "com.github.islamkhsh.cardslider_example"
+ minSdkVersion 17
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.core:core-ktx:1.0.2'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ implementation project(path: ':cardslider')
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..35ece30
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/github/islamkhsh/cardslider_example/MainActivity.kt b/app/src/main/java/com/github/islamkhsh/cardslider_example/MainActivity.kt
new file mode 100644
index 0000000..9f41601
--- /dev/null
+++ b/app/src/main/java/com/github/islamkhsh/cardslider_example/MainActivity.kt
@@ -0,0 +1,38 @@
+package com.github.islamkhsh.cardslider_example
+
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import kotlinx.android.synthetic.main.activity_main.*
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ val movies = arrayListOf()
+
+ movies.add(
+ Movie(R.drawable.harry_potter,"Harry Potter",
+ "An orphaned boy enrolls in a school of wizardry, where he learns the truth about himself, his family and the terrible evil that haunts the magical world."
+ ))
+
+ movies.add(
+ Movie(R.drawable.lord_of_rings,"The Lord of the Rings",
+ "A meek Hobbit from the Shire and eight companions set out on a journey to destroy the powerful One Ring and save Middle-earth from the Dark Lord Sauron."
+ ))
+
+ movies.add(
+ Movie(R.drawable.the_matrix,"The Matrix",
+ "A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers."
+ ))
+
+ movies.add(
+ Movie(R.drawable.avengers,"Avengers Assemble",
+ "Earth's mightiest heroes must come together and learn to fight as a team if they are going to stop the mischievous Loki and his alien army from enslaving humanity."
+ ))
+
+ viewPager.adapter = MovieAdapter(movies)
+ }
+}
diff --git a/app/src/main/java/com/github/islamkhsh/cardslider_example/Movie.kt b/app/src/main/java/com/github/islamkhsh/cardslider_example/Movie.kt
new file mode 100644
index 0000000..73b1e37
--- /dev/null
+++ b/app/src/main/java/com/github/islamkhsh/cardslider_example/Movie.kt
@@ -0,0 +1,7 @@
+package com.github.islamkhsh.cardslider_example
+
+data class Movie (
+ val poster : Int,
+ val title : String,
+ val overview : String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/github/islamkhsh/cardslider_example/MovieAdapter.kt b/app/src/main/java/com/github/islamkhsh/cardslider_example/MovieAdapter.kt
new file mode 100644
index 0000000..9d53645
--- /dev/null
+++ b/app/src/main/java/com/github/islamkhsh/cardslider_example/MovieAdapter.kt
@@ -0,0 +1,20 @@
+package com.github.islamkhsh.cardslider_example
+
+import android.view.View
+import android.widget.TextView
+import com.github.islamkhsh.CardSliderAdapter
+import kotlinx.android.synthetic.main.item_card_content.view.*
+
+class MovieAdapter(items : ArrayList) : CardSliderAdapter(items) {
+
+ override fun bindView(position: Int, itemContentView: View, item: Movie?) {
+
+ item?.run {
+ itemContentView.movie_poster.setImageResource(poster)
+ itemContentView.movie_title.text = title
+ itemContentView.movie_overview.text = overview
+ }
+ }
+
+ override fun getItemContentLayout(position: Int) = R.layout.item_card_content
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_bookmark_24dp.xml b/app/src/main/res/drawable-v24/ic_bookmark_24dp.xml
new file mode 100755
index 0000000..c1f2c13
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_bookmark_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..6348baa
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/avengers.jpg b/app/src/main/res/drawable/avengers.jpg
new file mode 100644
index 0000000..111eed8
Binary files /dev/null and b/app/src/main/res/drawable/avengers.jpg differ
diff --git a/app/src/main/res/drawable/harry_potter.jpg b/app/src/main/res/drawable/harry_potter.jpg
new file mode 100644
index 0000000..ce77a21
Binary files /dev/null and b/app/src/main/res/drawable/harry_potter.jpg differ
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..a0ad202
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/lord_of_rings.jpg b/app/src/main/res/drawable/lord_of_rings.jpg
new file mode 100644
index 0000000..8e505b5
Binary files /dev/null and b/app/src/main/res/drawable/lord_of_rings.jpg differ
diff --git a/app/src/main/res/drawable/the_matrix.jpg b/app/src/main/res/drawable/the_matrix.jpg
new file mode 100644
index 0000000..0bf5b9b
Binary files /dev/null and b/app/src/main/res/drawable/the_matrix.jpg differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..a33234c
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_card_content.xml b/app/src/main/res/layout/item_card_content.xml
new file mode 100755
index 0000000..37fc5d7
--- /dev/null
+++ b/app/src/main/res/layout/item_card_content.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..69b2233
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..db7b9d8
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ CardSlider
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cab82d7
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
diff --git a/cardslider/.gitignore b/cardslider/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/cardslider/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/cardslider/build.gradle b/cardslider/build.gradle
new file mode 100644
index 0000000..bee1d34
--- /dev/null
+++ b/cardslider/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.library'
+
+apply plugin: 'kotlin-android'
+
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.1"
+
+
+ defaultConfig {
+ minSdkVersion 17
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.0.2'
+
+ implementation 'androidx.viewpager:viewpager:1.0.0'
+ implementation 'androidx.cardview:cardview:1.0.0'
+
+ api 'com.duolingo.open:rtl-viewpager:2.0.0'
+}
diff --git a/cardslider/proguard-rules.pro b/cardslider/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/cardslider/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/cardslider/publish.gradle b/cardslider/publish.gradle
new file mode 100644
index 0000000..ea9aa54
--- /dev/null
+++ b/cardslider/publish.gradle
@@ -0,0 +1,50 @@
+apply plugin: 'maven-publish'
+
+apply plugin: 'com.jfrog.bintray'
+
+version '1.0'
+group 'com.github.islamkhsh'
+
+
+publishing {
+ publications {
+ Production(MavenPublication) {
+ artifact("$buildDir/outputs/aar/cardslider-release.aar")
+ groupId this.group
+ artifactId 'cardslider'
+ version this.version
+
+ pom.withXml {
+ def dependenciesNode = asNode().appendNode('dependencies')
+
+ configurations.implementation.allDependencies.each {
+
+ if (it.name != 'unspecified') {
+ def dependencyNode = dependenciesNode.appendNode('dependency')
+ dependencyNode.appendNode('groupId', it.group)
+ dependencyNode.appendNode('artifactId', it.name)
+ dependencyNode.appendNode('version', it.version)
+ }
+ }
+ }
+ }
+ }
+}
+
+bintray {
+ user = project.hasProperty('user') ?: System.getenv('BINTRAY_USER')
+ key = project.hasProperty('apiKey') ?: System.getenv('BINTRAY_API_KEY')
+ publications = ['Production']
+ configurations = ['archives']
+
+ publish = true
+ pkg {
+ repo = 'Card-Slider'
+ name = 'com.github.islamkhsh.cardslider'
+ version {
+ name = this.version
+ released = new Date()
+ vcsTag = this.version
+ }
+ }
+}
diff --git a/cardslider/src/main/AndroidManifest.xml b/cardslider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9cb9c7f
--- /dev/null
+++ b/cardslider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/cardslider/src/main/java/com/github/islamkhsh/CardSliderAdapter.kt b/cardslider/src/main/java/com/github/islamkhsh/CardSliderAdapter.kt
new file mode 100644
index 0000000..1df6df1
--- /dev/null
+++ b/cardslider/src/main/java/com/github/islamkhsh/CardSliderAdapter.kt
@@ -0,0 +1,86 @@
+package com.github.islamkhsh
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout.LayoutParams
+import androidx.annotation.CallSuper
+import androidx.annotation.LayoutRes
+import androidx.cardview.widget.CardView
+import androidx.viewpager.widget.PagerAdapter
+
+abstract class CardSliderAdapter(private val items: ArrayList) : PagerAdapter() {
+
+ private val cards = arrayOfNulls(count)
+
+ private lateinit var cardSliderViewPager : CardSliderViewPager
+
+ internal fun setViewPager(cardSliderViewPager : CardSliderViewPager) {
+ this.cardSliderViewPager = cardSliderViewPager
+ }
+
+ override fun isViewFromObject(view: View, `object`: Any) = view === `object`
+
+ override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
+ container.removeView(`object` as View)
+ cards[position] = null
+ }
+
+ @CallSuper
+ override fun instantiateItem(container: ViewGroup, position: Int): Any {
+
+ // create card view as a root to item view
+ val cardView = CardView(container.context)
+ val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
+ cardView.layoutParams = params
+
+ // set card attrs
+ cardView.maxCardElevation = cardSliderViewPager.baseShadow
+ cardView.radius = cardSliderViewPager.cardCornerRadius
+ cardView.setCardBackgroundColor(cardSliderViewPager.cardBackgroundColor)
+
+ // get item view
+ val cardContent = LayoutInflater.from(container.context)
+ .inflate(getItemContentLayout(position), cardView, false)
+
+ bindView(position, cardContent, getItem(position))
+
+ cardView.addView(cardContent)
+ container.addView(cardView)
+ cards[position] = cardView
+
+ return cardView
+ }
+
+ /**
+ * @return Int The total number of pages
+ */
+ override fun getCount() = items.size
+
+ /**
+ * Call this method to get the item at specific position
+ * @param position Int the position of the item
+ * @return T? the nullable item of the passed position
+ */
+ open fun getItem(position: Int): T? = items[position]
+
+ /**
+ * Override it to bind the #item with the inflated view of the page layout #itemContentView
+ * @param position Int the current position
+ * @param itemContentView View the inflated view of #getItemContentLayout with #position
+ * @param item T? the current item #getItem with #position
+ */
+ abstract fun bindView(position: Int, itemContentView: View, item: T?)
+
+ /**
+ * Override it to provide the page layout for every position,
+ * - this layout will be added as a child of CardView.
+ * - this layout will be inflated and passed as a View instance to bindView()
+ * @param position Int the position of page
+ * @return Int layout resource of the page
+ */
+ @LayoutRes
+ abstract fun getItemContentLayout(position: Int): Int
+
+
+}
\ No newline at end of file
diff --git a/cardslider/src/main/java/com/github/islamkhsh/CardSliderIndicator.kt b/cardslider/src/main/java/com/github/islamkhsh/CardSliderIndicator.kt
new file mode 100644
index 0000000..446ba2c
--- /dev/null
+++ b/cardslider/src/main/java/com/github/islamkhsh/CardSliderIndicator.kt
@@ -0,0 +1,117 @@
+package com.github.islamkhsh
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.LinearLayout
+import androidx.core.content.ContextCompat
+import androidx.viewpager.widget.ViewPager
+import kotlin.math.min
+
+
+class CardSliderIndicator : LinearLayout {
+
+ /**
+ * default indicator drawable, the background of the view if not selected
+ */
+ var defaultIndicator: Drawable? = null
+ set(value) {
+ field = value ?: ContextCompat.getDrawable(context, R.drawable.default_dot)
+ }
+
+ /**
+ * selected indicator drawable, the background of the view if selected
+ */
+ var selectedIndicator: Drawable? = null
+ set(value) {
+ field = value ?: ContextCompat.getDrawable(context, R.drawable.selected_dot)
+ }
+
+ /**
+ * space between one indicator and the next one
+ */
+ var indicatorMargin = 0f
+
+ constructor(context: Context) : super(context) {
+ initIndicatorGroup(null)
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ initIndicatorGroup(attrs)
+ }
+
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
+ initIndicatorGroup(attrs)
+ }
+
+
+ private fun initIndicatorGroup(attrs: AttributeSet?) {
+
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CardSliderIndicator)
+
+ defaultIndicator = typedArray.getDrawable(R.styleable.CardSliderIndicator_default_indicator)
+ selectedIndicator = typedArray.getDrawable(R.styleable.CardSliderIndicator_selected_indicator)
+
+ indicatorMargin = typedArray.getDimension(
+ R.styleable.CardSliderIndicator_indicator_margin,
+ min(defaultIndicator!!.intrinsicWidth, selectedIndicator!!.intrinsicWidth).toFloat()
+ )
+
+ typedArray.recycle()
+
+ orientation = HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL
+ }
+
+
+ internal fun setupWithViewCardSliderViewPager(viewPager: CardSliderViewPager) {
+
+ viewPager.adapter?.run {
+
+ // create indicators
+ for (i in 0 until count) {
+
+ val indicator = View(context)
+ addView(indicator, i)
+ changeIndicatorState(i, defaultIndicator!!, count - 1)
+ }
+
+ // set selected
+ changeIndicatorState(viewPager.currentItem, selectedIndicator!!)
+
+ viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
+
+ override fun onPageScrollStateChanged(state: Int) {}
+ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
+
+ override fun onPageSelected(position: Int) {
+
+ for (i in 0 until childCount) {
+
+ if (i == position)
+ changeIndicatorState(i, selectedIndicator!!)
+ else
+ changeIndicatorState(i, defaultIndicator!!)
+ }
+ }
+ })
+ }
+
+ }
+
+ private fun changeIndicatorState(position: Int, drawableState: Drawable, lastPosition: Int = childCount - 1) {
+
+ getChildAt(position).apply {
+
+ background = drawableState
+
+ val parms = LayoutParams(drawableState.intrinsicWidth, drawableState.intrinsicHeight)
+ if (position < lastPosition)
+ parms.marginEnd = indicatorMargin.toInt()
+
+ layoutParams = parms
+ }
+ }
+}
\ No newline at end of file
diff --git a/cardslider/src/main/java/com/github/islamkhsh/CardSliderTransformer.kt b/cardslider/src/main/java/com/github/islamkhsh/CardSliderTransformer.kt
new file mode 100755
index 0000000..2c92037
--- /dev/null
+++ b/cardslider/src/main/java/com/github/islamkhsh/CardSliderTransformer.kt
@@ -0,0 +1,49 @@
+package com.github.islamkhsh
+
+import android.content.Context
+import android.graphics.Point
+import android.view.View
+import android.view.WindowManager
+import androidx.cardview.widget.CardView
+import androidx.viewpager.widget.ViewPager
+import kotlin.math.absoluteValue
+
+
+internal class CardSliderTransformer(private val viewPager: CardSliderViewPager) : ViewPager.PageTransformer {
+
+ private val startOffset: Float
+
+ init {
+ val windowManager = viewPager.context
+ .getSystemService(Context.WINDOW_SERVICE) as WindowManager
+
+ val screen = Point()
+ windowManager.defaultDisplay.getSize(screen)
+
+ val horizontalPadding = viewPager.paddingEnd + viewPager.paddingStart
+ startOffset = ((horizontalPadding / 2).toFloat() / (screen.x - horizontalPadding).toFloat())
+ }
+
+ override fun transformPage(page: View, position: Float) {
+
+ val absPosition = (position - startOffset).absoluteValue
+
+ if (absPosition >= 1) {
+
+ (page as CardView).cardElevation = viewPager.minShadow
+ page.scaleY = viewPager.smallScaleFactor
+
+ } else {
+ // This will be during transformation
+ (page as CardView).cardElevation =
+ scalingEquation(viewPager.minShadow, viewPager.baseShadow, absPosition)
+
+ page.scaleY = scalingEquation(viewPager.smallScaleFactor,1f, absPosition)
+ }
+ }
+
+
+ private fun scalingEquation(minValue: Float, maxValue: Float, absPosition: Float) =
+ (minValue - maxValue) * absPosition + maxValue
+
+}
diff --git a/cardslider/src/main/java/com/github/islamkhsh/CardSliderViewPager.kt b/cardslider/src/main/java/com/github/islamkhsh/CardSliderViewPager.kt
new file mode 100644
index 0000000..cfe030f
--- /dev/null
+++ b/cardslider/src/main/java/com/github/islamkhsh/CardSliderViewPager.kt
@@ -0,0 +1,171 @@
+package com.github.islamkhsh
+
+import android.content.Context
+import android.graphics.Color
+import android.util.AttributeSet
+import android.view.View
+import androidx.viewpager.widget.PagerAdapter
+import com.duolingo.open.rtlviewpager.RtlViewPager
+import kotlin.math.max
+
+class CardSliderViewPager : RtlViewPager {
+
+ private var indicatorId = -1
+
+ /**
+ * The small scale factor, height of cards in right and left (previous and next cards) will be scaled to this factor
+ */
+ var smallScaleFactor = 1f
+
+ /**
+ * The card shadow in case of current card
+ */
+ var baseShadow = 0.0f
+ set(value) {
+ field = value
+ setPageMargin()
+ adapter?.notifyDataSetChanged()
+ }
+
+ /**
+ * The card shadow in case of previous and next cards
+ */
+ var minShadow = baseShadow * smallScaleFactor
+
+ /**
+ * Space between pages
+ */
+ var sliderPageMargin = baseShadow
+ set(value) {
+ field = value
+ setPageMargin()
+ }
+
+ /**
+ * The width of displayed part from previous and next cards
+ */
+ var otherPagesWidth = 0f
+ set(value) {
+ field = value
+ setPagePadding()
+ }
+
+ /**
+ * The background color of the CardView
+ */
+ var cardBackgroundColor = Color.WHITE
+ set(value) {
+ field = value
+ adapter?.notifyDataSetChanged()
+ }
+
+ /**
+ * The corner radius of the CardView
+ */
+ var cardCornerRadius = 0f
+ set(value) {
+ field = value
+ adapter?.notifyDataSetChanged()
+ }
+
+
+ constructor(context: Context) : super(context) {
+ init(null)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs)
+ }
+
+
+ private fun init(attrs: AttributeSet?) {
+
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CardSliderViewPager)
+
+ smallScaleFactor = typedArray.getFloat(R.styleable.CardSliderViewPager_cardSlider_smallScaleFactor, 1f)
+
+ baseShadow = typedArray.getDimension(
+ R.styleable.CardSliderViewPager_cardSlider_baseShadow,
+ context.resources.getDimension(R.dimen.baseCardElevation)
+ )
+ minShadow =
+ typedArray.getDimension(R.styleable.CardSliderViewPager_cardSlider_minShadow, baseShadow * smallScaleFactor)
+
+ cardBackgroundColor =
+ typedArray.getColor(R.styleable.CardSliderViewPager_cardSlider_cardBackgroundColor, Color.WHITE)
+ cardCornerRadius = typedArray.getDimension(R.styleable.CardSliderViewPager_cardSlider_cardCornerRadius, 0f)
+
+ sliderPageMargin =
+ typedArray.getDimension(R.styleable.CardSliderViewPager_cardSlider_pageMargin, baseShadow)
+
+ otherPagesWidth =
+ typedArray.getDimension(R.styleable.CardSliderViewPager_cardSlider_otherPagesWidth, 0f)
+
+ indicatorId = typedArray.getResourceId(R.styleable.CardSliderViewPager_cardSlider_indicator, -1)
+
+ typedArray.recycle()
+
+ clipToPadding = false
+ overScrollMode = View.OVER_SCROLL_NEVER
+ offscreenPageLimit = 3
+ }
+
+ private fun setPageMargin() {
+ pageMargin = max(sliderPageMargin, baseShadow).toInt()
+ setPagePadding()
+ }
+
+ private fun setPagePadding() {
+ setPadding(
+ otherPagesWidth.toInt() + pageMargin, max(paddingTop, baseShadow.toInt()),
+ otherPagesWidth.toInt() + pageMargin, max(paddingBottom, baseShadow.toInt())
+ )
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+
+ var heightMeasure = heightMeasureSpec
+ val mode = MeasureSpec.getMode(heightMeasureSpec)
+
+
+ if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ var height = 0
+ for (i in 0 until childCount) {
+ val child = getChildAt(i)
+ child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
+ val h = child.measuredHeight
+ if (h > height) height = h
+ }
+ heightMeasure = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasure + paddingTop + paddingBottom)
+ }
+
+
+ /**
+ * Set a CardSliderAdapter that will supply views for this pager.
+ * @param adapter PagerAdapter? instance of CardSliderAdapter
+ * @throws IllegalArgumentException if adapter passed isn't a CardSliderAdapter
+ */
+ @Throws(IllegalArgumentException::class)
+ override fun setAdapter(adapter: PagerAdapter?) {
+
+ if (adapter !is CardSliderAdapter<*>)
+ throw IllegalArgumentException("adapter must be CardSliderAdapter")
+
+ adapter.setViewPager(this)
+ super.setAdapter(adapter)
+
+ setPageTransformer(false, CardSliderTransformer(this))
+
+ if (indicatorId != -1)
+ rootView.findViewById(indicatorId)?.run {
+ setupWithViewCardSliderViewPager(this@CardSliderViewPager)
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/cardslider/src/main/res/drawable/default_dot.xml b/cardslider/src/main/res/drawable/default_dot.xml
new file mode 100644
index 0000000..49edf9a
--- /dev/null
+++ b/cardslider/src/main/res/drawable/default_dot.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/cardslider/src/main/res/drawable/selected_dot.xml b/cardslider/src/main/res/drawable/selected_dot.xml
new file mode 100644
index 0000000..7e09557
--- /dev/null
+++ b/cardslider/src/main/res/drawable/selected_dot.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/cardslider/src/main/res/values/attrs.xml b/cardslider/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..25d7d72
--- /dev/null
+++ b/cardslider/src/main/res/values/attrs.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cardslider/src/main/res/values/dimens.xml b/cardslider/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..1425ef6
--- /dev/null
+++ b/cardslider/src/main/res/values/dimens.xml
@@ -0,0 +1,4 @@
+
+
+ 2dp
+
\ No newline at end of file