Better SetSpeed() function
Better onAnimationLoopListener Add onLoaded function
This commit is contained in:
parent
208625b35e
commit
d74effa341
|
@ -13,26 +13,31 @@ import java.net.URL
|
|||
/**
|
||||
* Class to play APNG
|
||||
*/
|
||||
class ApngAnimator {
|
||||
class ApngAnimator(val context: Context) {
|
||||
var isPlaying = true
|
||||
private set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
private var frames = ArrayList<Frame>()
|
||||
private var myHandler: Handler = Handler()
|
||||
private var counter = 0
|
||||
private val generatedFrame = ArrayList<Bitmap>()
|
||||
private var speed: Int? = null
|
||||
private var lastFrame: Frame? = null
|
||||
var speed: Float? = null
|
||||
set(value) {
|
||||
field = value
|
||||
try {
|
||||
pause()
|
||||
play()
|
||||
} catch ( e : Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
private var bitmapBuffer: Bitmap? = null
|
||||
private var background: Bitmap? = null
|
||||
private var imageView: ImageView? = null
|
||||
private var anim: CustomAnimationDrawable? = null
|
||||
private var activeAnimation: CustomAnimationDrawable? = null
|
||||
private var currentDrawable = 0
|
||||
private var animationLoopListener: AnimationListener? = null
|
||||
|
||||
private var doOnLoaded : (ApngAnimator) -> Unit = {}
|
||||
private var AnimationLoopListener : () -> Unit = {}
|
||||
private var duration : ArrayList<Float>? = null
|
||||
/**
|
||||
* Load into an imageview
|
||||
* @param imageView Image view selected.
|
||||
|
@ -47,14 +52,14 @@ class ApngAnimator {
|
|||
* @param file The file to load
|
||||
* @throws NotApngException
|
||||
*/
|
||||
fun load(file: File, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||
fun load(file: File, speed: Float? = null) {
|
||||
doAsync {
|
||||
this@ApngAnimator.speed = speed
|
||||
// Download PNG
|
||||
APNGDisassembler(file.readBytes()).pngList.apply {
|
||||
draw(this)
|
||||
}
|
||||
|
||||
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||
setupAnimationDrawableAndStart()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,14 +72,15 @@ class ApngAnimator {
|
|||
* in the APNG will be used instead.
|
||||
* @throws NotApngException
|
||||
*/
|
||||
fun loadUrl(context: Context, url: URL, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||
fun loadUrl(url: URL, speed: Float? = null) {
|
||||
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
||||
this@ApngAnimator.speed = speed
|
||||
// Download PNG
|
||||
APNGDisassembler(Loader().load(context, url)).pngList.apply {
|
||||
draw(this)
|
||||
}
|
||||
|
||||
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||
setupAnimationDrawableAndStart()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,13 +93,13 @@ class ApngAnimator {
|
|||
* in the APNG will be used instead.
|
||||
* @throws NotApngException
|
||||
*/
|
||||
fun load(byteArray: ByteArray, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||
fun load(byteArray: ByteArray, speed: Float? = null) {
|
||||
doAsync {
|
||||
this@ApngAnimator.speed = speed
|
||||
APNGDisassembler(byteArray).pngList.apply {
|
||||
draw(this)
|
||||
}
|
||||
|
||||
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||
setupAnimationDrawableAndStart()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,18 +109,9 @@ class ApngAnimator {
|
|||
* @param frameDuration The duration to show each frame. If this is null then the duration specified
|
||||
* in the APNG will be used instead.
|
||||
*/
|
||||
private fun setupAnimationDrawableAndStart(frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||
private fun setupAnimationDrawableAndStart() {
|
||||
doAsync {
|
||||
var innerAnimationListener: CustomAnimationDrawable.AnimationListener? = null
|
||||
animationListener?.apply {
|
||||
innerAnimationListener = object : CustomAnimationDrawable.AnimationListener {
|
||||
override fun onAnimationLooped() {
|
||||
animationListener.onAnimationLooped()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anim = toAnimationDrawable(innerAnimationListener, frameDuration)
|
||||
anim = toAnimationDrawable()
|
||||
activeAnimation = anim
|
||||
uiThread {
|
||||
imageView?.apply {
|
||||
|
@ -122,6 +119,7 @@ class ApngAnimator {
|
|||
setImageDrawable(activeAnimation)
|
||||
}
|
||||
activeAnimation?.start()
|
||||
doOnLoaded(this@ApngAnimator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,12 +133,13 @@ class ApngAnimator {
|
|||
* in the APNG will be used instead.
|
||||
* @throws NotApngException
|
||||
*/
|
||||
fun load(context: Context, string: String, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||
fun load(string: String, speed : Float? = null) {
|
||||
this.speed = speed
|
||||
if (string.contains("http") || string.contains("https")) {
|
||||
val url = URL(string)
|
||||
loadUrl(context, url, frameDuration, animationListener)
|
||||
loadUrl(url, speed)
|
||||
} else if (File(string).exists()) {
|
||||
load(File(string), frameDuration, animationListener)
|
||||
load(File(string), speed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,6 +148,7 @@ class ApngAnimator {
|
|||
*/
|
||||
private fun draw(extractedFrame: ArrayList<Frame>) {
|
||||
// Set last frame
|
||||
duration = ArrayList()
|
||||
frames = extractedFrame
|
||||
bitmapBuffer = Bitmap.createBitmap(frames[0].maxWidth!!, frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
||||
for (i in 0 until frames.size) {
|
||||
|
@ -183,6 +183,7 @@ class ApngAnimator {
|
|||
} else {
|
||||
bitmapBuffer = btm
|
||||
}
|
||||
duration?.add(it.delay / (speed ?: 1f))
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -192,23 +193,26 @@ class ApngAnimator {
|
|||
val animResume = CustomAnimationDrawable()
|
||||
activeAnimation?.stop()
|
||||
val currentFrame = activeAnimation!!.current
|
||||
|
||||
val dura = ArrayList<Float>()
|
||||
frameLoop@ for (i in 0 until anim?.numberOfFrames!!) {
|
||||
val checkFrame = activeAnimation!!.getFrame(i)
|
||||
|
||||
if (checkFrame === currentFrame) {
|
||||
val frameIndex = i
|
||||
for (k in frameIndex until activeAnimation!!.numberOfFrames) {
|
||||
val frame = activeAnimation!!.getFrame(k)
|
||||
animResume.addFrame(frame, activeAnimation?.getDuration(k)!!)
|
||||
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
|
||||
dura.add(duration!![k])
|
||||
}
|
||||
for (k in 0 until frameIndex) {
|
||||
val frame = activeAnimation!!.getFrame(k)
|
||||
animResume.addFrame(frame, activeAnimation?.getDuration(k)!!)
|
||||
animResume.addFrame(frame, (duration!![k] / (speed ?: 1f)).toInt())
|
||||
dura.add(duration!![k])
|
||||
}
|
||||
activeAnimation = animResume
|
||||
imageView?.setImageDrawable(activeAnimation)
|
||||
activeAnimation?.setOnAnimationLoopListener(AnimationLoopListener)
|
||||
imageView?.invalidate()
|
||||
duration = dura
|
||||
break@frameLoop
|
||||
}
|
||||
}
|
||||
|
@ -219,36 +223,25 @@ class ApngAnimator {
|
|||
activeAnimation?.start()
|
||||
}
|
||||
|
||||
fun setOnAnimationLoopListener(animationLoopListener : () -> Unit) {
|
||||
AnimationLoopListener = animationLoopListener
|
||||
anim?.setOnAnimationLoopListener(animationLoopListener)
|
||||
}
|
||||
|
||||
fun onLoaded(f : (ApngAnimator) -> Unit) {
|
||||
doOnLoaded = f
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the generated frames into an animation drawable ([CustomAnimationDrawable])
|
||||
*
|
||||
* @param animationListener The listener that will be invoked when there are specific animation events.
|
||||
* @param frameDuration The duration to show each frame. If this is null then the duration specified
|
||||
* in the APNG will be used instead.
|
||||
*/
|
||||
private fun toAnimationDrawable(animationListener: CustomAnimationDrawable.AnimationListener? = null,
|
||||
frameDuration: Int? = null): CustomAnimationDrawable {
|
||||
private fun toAnimationDrawable( ): CustomAnimationDrawable {
|
||||
|
||||
return CustomAnimationDrawable().apply {
|
||||
for (i in 0 until generatedFrame.size) {
|
||||
addFrame(BitmapDrawable(generatedFrame[i]), frameDuration
|
||||
?: frames[i].delay.toInt())
|
||||
}
|
||||
|
||||
animationListener?.let { listener ->
|
||||
this.setAnimationListener(listener)
|
||||
addFrame(BitmapDrawable(generatedFrame[i]), ((frames[i].delay).toInt() / (speed ?: 1f)).toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that exposes callbacks for events during the animation.
|
||||
*/
|
||||
interface AnimationListener {
|
||||
|
||||
/**
|
||||
* The animation has performed a loop.
|
||||
*/
|
||||
fun onAnimationLooped()
|
||||
}
|
||||
}
|
|
@ -1,36 +1,23 @@
|
|||
package oupson.apng
|
||||
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import oupson.apng.CustomAnimationDrawable.AnimationListener
|
||||
|
||||
/**
|
||||
* Extension of the [AnimationDrawable] that provides an [AnimationListener]. This will allow
|
||||
* for the caller to listen for specific animation related events.
|
||||
*/
|
||||
internal class CustomAnimationDrawable : AnimationDrawable() {
|
||||
private var onAnimationLoop : () -> Unit = {}
|
||||
|
||||
/**
|
||||
* Interface that exposes callbacks for events during the animation.
|
||||
*/
|
||||
interface AnimationListener {
|
||||
|
||||
/**
|
||||
* The animation has performed a loop.
|
||||
*/
|
||||
fun onAnimationLooped()
|
||||
}
|
||||
|
||||
private var animationListener: AnimationListener? = null
|
||||
|
||||
fun setAnimationListener(animationListener: AnimationListener) {
|
||||
this.animationListener = animationListener
|
||||
fun setOnAnimationLoopListener( f : () -> Unit) {
|
||||
onAnimationLoop = f
|
||||
}
|
||||
|
||||
override fun selectDrawable(index: Int): Boolean {
|
||||
val drawableChanged = super.selectDrawable(index)
|
||||
|
||||
if (index != 0 && index == numberOfFrames - 1) {
|
||||
animationListener?.onAnimationLooped()
|
||||
onAnimationLoop()
|
||||
}
|
||||
|
||||
return drawableChanged
|
||||
|
|
|
@ -46,12 +46,12 @@ class Main2Activity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
fun load() {
|
||||
val animator = ApngAnimator().loadInto(imageView3)
|
||||
val animator = ApngAnimator(applicationContext).loadInto(imageView3)
|
||||
val uri = intent.data
|
||||
if (uri.toString().contains("file:///")) {
|
||||
try {
|
||||
if (isApng(File(uri.path).readBytes())) {
|
||||
animator.load(this, uri.path)
|
||||
animator.load(uri.path)
|
||||
} else {
|
||||
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
|
||||
Snackbar.make(constraint, "Not an APNG, and verified !", Snackbar.LENGTH_LONG).show()
|
||||
|
@ -62,7 +62,7 @@ class Main2Activity : AppCompatActivity() {
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
animator.load(this, getImageRealPath(contentResolver, uri, null))
|
||||
animator.load(getImageRealPath(contentResolver, uri, null))
|
||||
} catch (e: NotApngException) {
|
||||
imageView3.setImageBitmap(BitmapFactory.decodeFile(getImageRealPath(contentResolver, uri, null)))
|
||||
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
|
||||
|
|
|
@ -3,9 +3,13 @@ package oupson.apngcreator
|
|||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import android.widget.SeekBar
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import oupson.apng.ApngAnimator
|
||||
import android.widget.Toast
|
||||
|
||||
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
@ -17,15 +21,35 @@ class MainActivity : AppCompatActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val animationListener = object : ApngAnimator.AnimationListener {
|
||||
override fun onAnimationLooped() {
|
||||
Log.d("TEST", "Animation LOOPED!")
|
||||
|
||||
animator = ApngAnimator(this).loadInto(imageView).apply {
|
||||
load(imageUrl)
|
||||
onLoaded {
|
||||
setOnAnimationLoopListener {
|
||||
Log.e("app-test", "onLoop")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animator = ApngAnimator().loadInto(imageView).apply {
|
||||
load(this@MainActivity, imageUrl, null, animationListener)
|
||||
}
|
||||
this.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
internal var progress = 0
|
||||
|
||||
// When Progress value changed.
|
||||
override fun onProgressChanged(seekBar: SeekBar, progressValue: Int, fromUser: Boolean) {
|
||||
progress = progressValue
|
||||
}
|
||||
|
||||
// Notification that the user has started a touch gesture.
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
// Notification that the user has finished a touch gesture
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
Log.e("TAG" , (seekBar.progress.toFloat() / 100f).toString())
|
||||
animator.speed = (seekBar.progress.toFloat() / 100f)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
Picasso.get().load(imageUrl).into(imageView2)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="pause"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/seekBar"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<Button
|
||||
|
@ -23,7 +23,21 @@
|
|||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="play"
|
||||
app:layout_constraintBottom_toTopOf="@+id/seekBar"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekBar"
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:max="200"
|
||||
android:progress="10"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
|
@ -50,4 +64,5 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@android:color/transparent" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
Loading…
Reference in New Issue