Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 28
Expand All @@ -9,7 +11,6 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
Expand All @@ -23,9 +24,10 @@ dependencies {
implementation project(':library')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:2.0.24'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.jakewharton.timber:timber:4.7.1'
}
repositories {
mavenCentral()
}
8 changes: 5 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="jp.shts.android.storyprogressbar">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -20,4 +20,6 @@
</activity>
</application>

<uses-permission android:name="android.permission.INTERNET" />

</manifest>
114 changes: 0 additions & 114 deletions app/src/main/java/jp/shts/android/storyprogressbar/MainActivity.java

This file was deleted.

141 changes: 141 additions & 0 deletions app/src/main/java/jp/shts/android/storyprogressbar/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package jp.shts.android.storyprogressbar

import android.animation.Animator
import android.animation.ValueAnimator
import android.os.Bundle
import android.util.SparseIntArray
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.viewpager.widget.ViewPager
import com.ToxicBakery.viewpager.transforms.CubeOutTransformer
import timber.log.Timber

interface PageViewOperator {
fun backPageView()
fun nextPageView()
}

class MainActivity : AppCompatActivity(), PageViewOperator {

companion object {
private const val PAGE_COUNT = 5
/* key: page-count, value: story-count */
val progressState = SparseIntArray()
}

private lateinit var viewPager: ViewPager
private lateinit var pageAdapter: PageAdapter

private var currentPage: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
setContentView(R.layout.activity_main)

Timber.plant(Timber.DebugTree())

pageAdapter = PageAdapter(supportFragmentManager)
viewPager = findViewById(R.id.viewpager)
viewPager.adapter = pageAdapter
viewPager.setPageTransformer(true, CubeOutTransformer())
viewPager.addOnPageChangeListener(object : PageChangeListener() {
override fun onPageScrollCanceled() {
Timber.d("onPageScrollCanceled()")
currentFragment()?.resume()
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
currentPage = position
// currentFragment()?.resume()
}
})
}

private fun currentFragment(): StoryFragment? {
return pageAdapter.findFragmentByPosition(viewPager, currentPage) as StoryFragment
}

override fun backPageView() {
if (viewPager.currentItem > 0) {
fakeDrag(false)
}
}

override fun nextPageView() {
if (viewPager.currentItem + 1 < viewPager.adapter?.count ?: 0) {
fakeDrag(true)
}
}

private var prevDragPosition = 0

/**
* Change ViewPage sliding programmatically(not using reflection).
* https://tech.dely.jp/entry/2018/12/13/110000
* What for?
* setCurrentItem(int, boolean) changes too fast. And it cannot set animation duration.
*/
private fun fakeDrag(forward: Boolean) {
if (prevDragPosition == 0 && viewPager.beginFakeDrag()) {
ValueAnimator.ofInt(0, viewPager.width).apply {
duration = 500L
interpolator = FastOutSlowInInterpolator()
addListener(object : Animator.AnimatorListener {

override fun onAnimationStart(animation: Animator?) = Unit

override fun onAnimationEnd(animation: Animator?) {
removeAllUpdateListeners()
if (viewPager.isFakeDragging) {
viewPager.endFakeDrag()
}
prevDragPosition = 0
}

override fun onAnimationCancel(animation: Animator?) {
removeAllUpdateListeners()
if (viewPager.isFakeDragging) {
viewPager.endFakeDrag()
}
prevDragPosition = 0
}

override fun onAnimationRepeat(animation: Animator?) = Unit
})
addUpdateListener {
if (!viewPager.isFakeDragging) {
return@addUpdateListener
}
val dragPosition: Int = it.animatedValue as Int
val dragOffset: Float = ((dragPosition - prevDragPosition) * if (forward) -1 else 1).toFloat()
prevDragPosition = dragPosition
viewPager.fakeDragBy(dragOffset)
}
}.start()
}
}

private class PageAdapter internal constructor(fragmentManager: FragmentManager) :
FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment = StoryFragment.createIntent(position)
override fun getCount(): Int {
return PAGE_COUNT
}
/**
* https://qiita.com/chooblarin/items/88b4accac0cbb6944d4b#%E6%96%B9%E6%B3%953-instantiateitem%E3%82%92%E4%BD%BF%E3%81%86
*/
fun findFragmentByPosition(viewPager: ViewPager, position: Int): Fragment? {
try {
val f = instantiateItem(viewPager, position)
return f as? Fragment
} finally {
finishUpdate(viewPager)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package jp.shts.android.storyprogressbar

import android.os.Handler
import android.os.HandlerThread
import android.os.Message
import androidx.viewpager.widget.ViewPager.*
import timber.log.Timber

abstract class PageChangeListener : OnPageChangeListener {

companion object {
private const val DEBOUNCE_TIMES = 500L
}

private var pageBeforeDragging = 0
private var currentPage = 0
private var lastTime = DEBOUNCE_TIMES + 1L

override fun onPageScrollStateChanged(state: Int) {
when (state) {
SCROLL_STATE_IDLE -> {
Timber.d("onPageScrollStateChanged(): SCROLL_STATE_IDLE")
// 500ms 以下間隔のリクエストは破棄する
val now = System.currentTimeMillis()
if (now - lastTime < DEBOUNCE_TIMES) {
return
}
lastTime = now
Handler().postDelayed({
if (pageBeforeDragging == currentPage) {
onPageScrollCanceled()
}
}, 300L)
}
SCROLL_STATE_DRAGGING -> {
Timber.d("onPageScrollStateChanged(): SCROLL_STATE_DRAGGING")
pageBeforeDragging = currentPage
}
SCROLL_STATE_SETTLING -> {
Timber.d("onPageScrollStateChanged(): SCROLL_STATE_SETTLING")
}
}
}

override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}

override fun onPageSelected(position: Int) {
Timber.d("onPageSelected(): position($position)")
currentPage = position
}

abstract fun onPageScrollCanceled()
}
Loading