Added duration and loop completed callback
Added CustomAnimationDrawable that provides listener for callbacks. Added the duration for each frame which can override the png value Refactored some of the ApngAnimator to reuse code
This commit is contained in:
parent
da8753e2c5
commit
0a182b4194
|
@ -2,86 +2,160 @@ package oupson.apng
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.graphics.drawable.AnimationDrawable
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import org.jetbrains.anko.doAsync
|
import org.jetbrains.anko.doAsync
|
||||||
import org.jetbrains.anko.uiThread
|
import org.jetbrains.anko.uiThread
|
||||||
|
import oupson.apng.exceptions.NotApngException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to play APNG
|
* Class to play APNG
|
||||||
*/
|
*/
|
||||||
class ApngAnimator(val context: Context) {
|
class ApngAnimator {
|
||||||
var isPlaying = true
|
var isPlaying = true
|
||||||
private set(value) {field = value}
|
private set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
var Frames = ArrayList<Frame>()
|
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
|
||||||
|
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
|
||||||
|
|
||||||
var myHandler: Handler = Handler()
|
|
||||||
|
|
||||||
var counter = 0
|
|
||||||
|
|
||||||
val generatedFrame = ArrayList<Bitmap>()
|
|
||||||
|
|
||||||
var speed = 1
|
|
||||||
|
|
||||||
var lastFrame : Frame? = null
|
|
||||||
var bitmapBuffer : Bitmap? = null
|
|
||||||
|
|
||||||
var background : Bitmap? = null
|
|
||||||
|
|
||||||
var imageView : ImageView? = null
|
|
||||||
|
|
||||||
var anim : AnimationDrawable? = null
|
|
||||||
var activeAnimation : AnimationDrawable? = null
|
|
||||||
|
|
||||||
var currentDrawable = 0
|
|
||||||
/**
|
/**
|
||||||
* Load into an imageview
|
* Load into an imageview
|
||||||
* @param imageView Image view selected.
|
* @param imageView Image view selected.
|
||||||
*/
|
*/
|
||||||
fun loadInto(imageView: ImageView) : ApngAnimator {
|
fun loadInto(imageView: ImageView): ApngAnimator {
|
||||||
this.imageView = imageView
|
this.imageView = imageView
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load an APNG file
|
* Load an APNG file and starts playing the animation.
|
||||||
* @param file The file to load
|
* @param file The file to load
|
||||||
* @throws NotApngException
|
* @throws NotApngException
|
||||||
*/
|
*/
|
||||||
fun load(file: File) {
|
fun load(file: File, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||||
doAsync {
|
doAsync {
|
||||||
// Download PNG
|
// Download PNG
|
||||||
val extractedFrame = APNGDisassembler(file.readBytes()).pngList
|
APNGDisassembler(file.readBytes()).pngList.apply {
|
||||||
draw(extractedFrame)
|
draw(this)
|
||||||
anim = toAnimationDrawable()
|
}
|
||||||
|
|
||||||
|
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an APNG file and starts playing the animation.
|
||||||
|
* @param context The current context.
|
||||||
|
* @param url URL to load.
|
||||||
|
* @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.
|
||||||
|
* @throws NotApngException
|
||||||
|
*/
|
||||||
|
fun loadUrl(context: Context, url: URL, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||||
|
doAsync(exceptionHandler = { e -> e.printStackTrace() }) {
|
||||||
|
// Download PNG
|
||||||
|
APNGDisassembler(Loader().load(context, url)).pngList.apply {
|
||||||
|
draw(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an APNG file and starts playing the animation.
|
||||||
|
* @param byteArray ByteArray of the file
|
||||||
|
* @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.
|
||||||
|
* @throws NotApngException
|
||||||
|
*/
|
||||||
|
fun load(byteArray: ByteArray, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||||
|
doAsync {
|
||||||
|
APNGDisassembler(byteArray).pngList.apply {
|
||||||
|
draw(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupAnimationDrawableAndStart(frameDuration, animationListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the animation drawable and any required listeners. The animation will automatically start.
|
||||||
|
* @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 setupAnimationDrawableAndStart(frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||||
|
doAsync {
|
||||||
|
var innerAnimationListener: CustomAnimationDrawable.AnimationListener? = null
|
||||||
|
animationListener?.apply {
|
||||||
|
innerAnimationListener = object : CustomAnimationDrawable.AnimationListener {
|
||||||
|
override fun onAnimationLooped() {
|
||||||
|
animationListener.onAnimationLooped()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anim = toAnimationDrawable(innerAnimationListener, frameDuration)
|
||||||
activeAnimation = anim
|
activeAnimation = anim
|
||||||
uiThread {
|
uiThread {
|
||||||
imageView?.setImageBitmap(generatedFrame[0])
|
imageView?.apply {
|
||||||
imageView?.setImageDrawable(activeAnimation)
|
setImageBitmap(generatedFrame[0])
|
||||||
|
setImageDrawable(activeAnimation)
|
||||||
|
}
|
||||||
activeAnimation?.start()
|
activeAnimation?.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an APNG file
|
||||||
|
* @param context The current context.
|
||||||
|
* @param string Path of the file.
|
||||||
|
* @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.
|
||||||
|
* @throws NotApngException
|
||||||
|
*/
|
||||||
|
fun load(context: Context, string: String, frameDuration: Int? = null, animationListener: AnimationListener? = null) {
|
||||||
|
if (string.contains("http") || string.contains("https")) {
|
||||||
|
val url = URL(string)
|
||||||
|
loadUrl(context, url, frameDuration, animationListener)
|
||||||
|
} else if (File(string).exists()) {
|
||||||
|
load(File(string), frameDuration, animationListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw frames
|
* Draw frames
|
||||||
*/
|
*/
|
||||||
private fun draw(extractedFrame : ArrayList<Frame>) {
|
private fun draw(extractedFrame: ArrayList<Frame>) {
|
||||||
// Set last frame
|
// Set last frame
|
||||||
Frames = extractedFrame
|
frames = extractedFrame
|
||||||
bitmapBuffer = Bitmap.createBitmap(Frames[0].maxWidth!!, Frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
bitmapBuffer = Bitmap.createBitmap(frames[0].maxWidth!!, frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
||||||
for (i in 0 until Frames.size) {
|
for (i in 0 until frames.size) {
|
||||||
// Iterator
|
// Iterator
|
||||||
val it = Frames[i]
|
val it = frames[i]
|
||||||
// Current bitmap for the frame
|
// Current bitmap for the frame
|
||||||
val btm = Bitmap.createBitmap(Frames[0].maxWidth!!, Frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
val btm = Bitmap.createBitmap(frames[0].maxWidth!!, frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
||||||
val canvas = Canvas(btm)
|
val canvas = Canvas(btm)
|
||||||
val current = BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size).copy(Bitmap.Config.ARGB_8888, true)
|
val current = BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size).copy(Bitmap.Config.ARGB_8888, true)
|
||||||
// Write buffer to canvas
|
// Write buffer to canvas
|
||||||
|
@ -95,13 +169,13 @@ class ApngAnimator(val context: Context) {
|
||||||
canvas.drawBitmap(current, it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null)
|
canvas.drawBitmap(current, it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null)
|
||||||
generatedFrame.add(btm)
|
generatedFrame.add(btm)
|
||||||
// Don't add current frame to bitmap buffer
|
// Don't add current frame to bitmap buffer
|
||||||
if (Frames[i].dispose_op == Utils.Companion.dispose_op.APNG_DISPOSE_OP_PREVIOUS) {
|
if (frames[i].dispose_op == Utils.Companion.dispose_op.APNG_DISPOSE_OP_PREVIOUS) {
|
||||||
//Do nothings
|
//Do nothings
|
||||||
}
|
}
|
||||||
// Add current frame to bitmap buffer
|
// Add current frame to bitmap buffer
|
||||||
// APNG_DISPOSE_OP_BACKGROUND: the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
|
// APNG_DISPOSE_OP_BACKGROUND: the frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
|
||||||
else if (it.dispose_op == Utils.Companion.dispose_op.APNG_DISPOSE_OP_BACKGROUND) {
|
else if (it.dispose_op == Utils.Companion.dispose_op.APNG_DISPOSE_OP_BACKGROUND) {
|
||||||
val res = Bitmap.createBitmap(Frames[0].maxWidth!!, Frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
val res = Bitmap.createBitmap(frames[0].maxWidth!!, frames[0].maxHeight!!, Bitmap.Config.ARGB_8888)
|
||||||
val can = Canvas(res)
|
val can = Canvas(res)
|
||||||
can.drawBitmap(btm, 0f, 0f, null)
|
can.drawBitmap(btm, 0f, 0f, null)
|
||||||
can.drawRect(it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), it.x_offsets!! + it.width.toFloat(), it.y_offsets!! + it.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }())
|
can.drawRect(it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), it.x_offsets!! + it.width.toFloat(), it.y_offsets!! + it.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }())
|
||||||
|
@ -113,65 +187,9 @@ class ApngAnimator(val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load an APNG file
|
|
||||||
* @param url URL to load
|
|
||||||
* @throws NotApngException
|
|
||||||
*/
|
|
||||||
fun loadUrl(url : URL) {
|
|
||||||
doAsync(exceptionHandler = {e -> e.printStackTrace()}) {
|
|
||||||
// Download PNG
|
|
||||||
val extractedFrame = APNGDisassembler(Loader().load(context, url)).pngList
|
|
||||||
draw(extractedFrame)
|
|
||||||
anim = toAnimationDrawable()
|
|
||||||
activeAnimation = anim
|
|
||||||
uiThread {
|
|
||||||
imageView?.setImageBitmap(generatedFrame[0])
|
|
||||||
imageView?.setImageDrawable(activeAnimation)
|
|
||||||
|
|
||||||
activeAnimation?.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load an APNG file
|
|
||||||
* @param byteArray ByteArray of the file
|
|
||||||
* @throws NotApngException
|
|
||||||
*/
|
|
||||||
fun load(byteArray: ByteArray) {
|
|
||||||
doAsync {
|
|
||||||
// Download PNG
|
|
||||||
val extractedFrame = APNGDisassembler(byteArray).pngList
|
|
||||||
draw(extractedFrame)
|
|
||||||
anim = toAnimationDrawable()
|
|
||||||
activeAnimation = anim
|
|
||||||
uiThread {
|
|
||||||
imageView?.setImageBitmap(generatedFrame[0])
|
|
||||||
imageView?.setImageDrawable(activeAnimation)
|
|
||||||
activeAnimation?.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load an APNG file
|
|
||||||
* @param string Path of the file
|
|
||||||
* @throws NotApngException
|
|
||||||
*/
|
|
||||||
fun load(string: String) {
|
|
||||||
if (string.contains("http") || string.contains("https")) {
|
|
||||||
val url = URL(string)
|
|
||||||
loadUrl(url)
|
|
||||||
} else if (File(string).exists()) {
|
|
||||||
load(File(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pause() {
|
fun pause() {
|
||||||
isPlaying = false
|
isPlaying = false
|
||||||
val animResume = AnimationDrawable()
|
val animResume = CustomAnimationDrawable()
|
||||||
activeAnimation?.stop()
|
activeAnimation?.stop()
|
||||||
val currentFrame = activeAnimation!!.current
|
val currentFrame = activeAnimation!!.current
|
||||||
|
|
||||||
|
@ -195,25 +213,42 @@ class ApngAnimator(val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play() {
|
fun play() {
|
||||||
isPlaying = true
|
isPlaying = true
|
||||||
activeAnimation?.start();
|
activeAnimation?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return animation drawable of the APNG
|
* 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.
|
||||||
*/
|
*/
|
||||||
fun toAnimationDrawable() : AnimationDrawable {
|
private fun toAnimationDrawable(animationListener: CustomAnimationDrawable.AnimationListener? = null,
|
||||||
val animDrawable = AnimationDrawable()
|
frameDuration: Int? = null): CustomAnimationDrawable {
|
||||||
for (i in 0 until generatedFrame.size) {
|
|
||||||
animDrawable.addFrame(BitmapDrawable(generatedFrame[i]), Frames[i].delay.toInt())
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return animDrawable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFrameSpeed(speed : Int) {
|
/**
|
||||||
this.speed = speed
|
* Interface that exposes callbacks for events during the animation.
|
||||||
|
*/
|
||||||
|
interface AnimationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation has performed a loop.
|
||||||
|
*/
|
||||||
|
fun onAnimationLooped()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
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() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun selectDrawable(index: Int): Boolean {
|
||||||
|
val drawableChanged = super.selectDrawable(index)
|
||||||
|
|
||||||
|
if (index != 0 && index == numberOfFrames - 1) {
|
||||||
|
animationListener?.onAnimationLooped()
|
||||||
|
}
|
||||||
|
|
||||||
|
return drawableChanged
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package oupson.apng.exceptions
|
package oupson.apng.exceptions
|
||||||
|
|
||||||
class NoFrameException() : Exception()
|
class NoFrameException : Exception()
|
||||||
class NotPngException() : Exception()
|
class NotPngException : Exception()
|
||||||
class NotApngException() : Exception()
|
class NotApngException : Exception()
|
||||||
class NoFcTL() : Exception()
|
class NoFcTL : Exception()
|
|
@ -46,24 +46,24 @@ class Main2Activity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
val animator = ApngAnimator(this).loadInto(imageView3)
|
val animator = ApngAnimator().loadInto(imageView3)
|
||||||
val uri = intent.data
|
val uri = intent.data
|
||||||
if (uri.toString().contains("file:///")) {
|
if (uri.toString().contains("file:///")) {
|
||||||
try {
|
try {
|
||||||
if (isApng(File(uri.path).readBytes())) {
|
if (isApng(File(uri.path).readBytes())) {
|
||||||
animator.load(uri.path)
|
animator.load(this, uri.path)
|
||||||
} else {
|
} else {
|
||||||
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
|
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
|
||||||
Snackbar.make(constraint, "Not an APNG, and verified !", Snackbar.LENGTH_LONG).show()
|
Snackbar.make(constraint, "Not an APNG, and verified !", Snackbar.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
} catch (e : NotApngException) {
|
} catch (e: NotApngException) {
|
||||||
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
|
imageView3.setImageBitmap(BitmapFactory.decodeFile(uri.path))
|
||||||
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
|
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
animator.load(getImageRealPath(contentResolver, uri, null))
|
animator.load(this, getImageRealPath(contentResolver, uri, null))
|
||||||
} catch (e : NotApngException) {
|
} catch (e: NotApngException) {
|
||||||
imageView3.setImageBitmap(BitmapFactory.decodeFile(getImageRealPath(contentResolver, uri, null)))
|
imageView3.setImageBitmap(BitmapFactory.decodeFile(getImageRealPath(contentResolver, uri, null)))
|
||||||
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
|
Snackbar.make(constraint, "Not an APNG", Snackbar.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class Main2Activity : AppCompatActivity() {
|
||||||
} else {
|
} else {
|
||||||
animator.play()
|
animator.play()
|
||||||
}
|
}
|
||||||
} catch (e : Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,30 @@ package oupson.apngcreator
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
|
import android.util.Log
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import oupson.apng.ApngAnimator
|
import oupson.apng.ApngAnimator
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
lateinit var animator : ApngAnimator
|
lateinit var animator: ApngAnimator
|
||||||
|
|
||||||
val imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"
|
//val imageUrl = "https://metagif.files.wordpress.com/2015/01/bugbuckbunny.png"
|
||||||
//val imageUrl = "https://raw.githubusercontent.com/tinify/iMessage-Panda-sticker/master/StickerPackExtension/Stickers.xcstickers/Sticker%20Pack.stickerpack/panda.sticker/panda.png"
|
val imageUrl = "https://raw.githubusercontent.com/tinify/iMessage-Panda-sticker/master/StickerPackExtension/Stickers.xcstickers/Sticker%20Pack.stickerpack/panda.sticker/panda.png"
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
animator = ApngAnimator(this).loadInto(imageView)
|
val animationListener = object : ApngAnimator.AnimationListener {
|
||||||
animator.load(imageUrl)
|
override fun onAnimationLooped() {
|
||||||
|
Log.d("TEST", "Animation LOOPED!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animator = ApngAnimator().loadInto(imageView).apply {
|
||||||
|
load(this@MainActivity, imageUrl, null, animationListener)
|
||||||
|
}
|
||||||
|
|
||||||
Picasso.get().load(imageUrl).into(imageView2)
|
Picasso.get().load(imageUrl).into(imageView2)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue