I have used this repo as i want to implement stickerview functionality with customization
needs to implement the swap the sticker layer functionality
for example i have 10 stickers on a canvas and want to swap sticker from lets say index 5 to 10
needs to swap the sticker with proper validating ui
i have stickerview class with customization as follow
internal class CanvasEditorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : RelativeLayout(context, attrs, defStyleAttr) {
private val mUndoList = mutableListOf<DrawObject>()
private val mRedoList = mutableListOf<DrawObject>()
private val stickerViewListener = object : StickerViewListener {
override fun onRemove() {
mListener?.onStickerRemove()
mListener?.onEnableUndo(mUndoList.isNotEmpty())
}
override fun onClone() {
}
override fun onDone(obj: DrawObject) {
addStickerToPaint(obj)
mListener?.onStickerDone()
}
override fun onZoomAndRotate() {
mListener?.onStickerZoomAndRotate()
}
override fun onFlip() {
mListener?.onStickerFlip()
}
override fun onReplace(sticker: Sticker) {
}
override fun onClickStickerOutside(x: Float, y: Float) {
val pos = findTapedSticker(x, y)
if (pos > -1) {
enableEditModeSticker(pos)
}
}
override fun onTouchEvent(event: MotionEvent) {
mListener?.onTouchEvent(event)
}
}
private val paintViewListener = object : PaintViewListener {
override fun onTouchUp(obj: DrawObject) {
// mUndoList.add(obj)
mRedoList.clear()
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
}
override fun onClick(x: Float, y: Float) {
val pos = findTapedSticker(x, y)
if (pos > -1) enableEditModeSticker(pos)
}
override fun onTouchEvent(event: MotionEvent) {
mListener?.onTouchEvent(event)
mStickerView.onTouchEvent(MotionEvent.obtain(event))
}
}
private val mStickerView: StickerView = StickerView(context, stickerViewListener)
private val mPaintView: PaintView = PaintView(context, paintViewListener)
private var mListener: CanvasEditorListener? = null
init {
val params = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
mPaintView.layoutParams = params
mPaintView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white))
addView(mPaintView)
mStickerView.layoutParams = params
mStickerView.setBackgroundColor(
ContextCompat.getColor(
context, android.R.color.transparent
)
)
addView(mStickerView)
mStickerView.visibility = View.GONE
}
fun setListener(listener: CanvasEditorListener) {
mListener = listener
}
fun setPaintColor(color: Int) {
doneStickerEdit()
mPaintView.paint.color = color
}
fun setStrokeWidth(strokeWidth: Float) {
doneStickerEdit()
mPaintView.paint.strokeWidth = strokeWidth
}
fun setStrokeCap(strokeCap: Paint.Cap) {
doneStickerEdit()
mPaintView.paint.strokeCap = strokeCap
}
//region add sticker
fun addDrawableSticker(drawable: Drawable) {
doneStickerEdit()
mStickerView.visibility = View.VISIBLE
val sticker = DrawableSticker(drawable)
mStickerView.addSticker(sticker)
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
mListener?.onStickerActive()
}
fun addBitmapSticker(bitmap: Bitmap) {
doneStickerEdit()
mStickerView.visibility = View.VISIBLE
val sticker = BitmapSticker(context, bitmap)
mStickerView.addSticker(sticker)
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
mListener?.onStickerActive()
}
fun addTextSticker(text: String, textColor: Int, typeface: Typeface?) {
doneStickerEdit()
mStickerView.visibility = View.VISIBLE
val sticker = TextSticker(context, null)
sticker.setText(text)
sticker.setTextColor(textColor)
typeface?.let {
sticker.setTypeface(it)
}
sticker.setAlpha(255)
sticker.resizeText()
mStickerView.addSticker(sticker)
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
mListener?.onStickerActive()
}
fun addDrawableTextSticker(
drawable: Drawable,
text: String,
textColor: Int,
typeface: Typeface?,
) {
doneStickerEdit()
mStickerView.visibility = View.VISIBLE
val sticker = TextSticker(context, drawable)
sticker.setText(text)
sticker.setTextColor(textColor)
typeface?.let {
sticker.setTypeface(it)
}
sticker.resizeText()
mStickerView.addSticker(sticker)
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
mListener?.onStickerActive()
}
fun doneActiveSticker() {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.done()
}
}
fun removeActiveSticker() {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.remove()
}
}
fun zoomAndRotateActiveSticker(motionEvent: MotionEvent) {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.zoomAndRotate(motionEvent)
}
}
fun flipActiveSticker() {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.flip()
}
}
fun swapLayer(position: Int, newPosition: Int) {
mStickerView.sendToLayer(position, newPosition)
}
fun setLocked(locked: Boolean) {
mStickerView.setLocked(locked)
mListener?.onStickerActive()
}
fun getStickers(): List<Sticker> {
return mStickerView.getStickers()
}
fun isLocked(): Boolean {
return mStickerView.isLocked()
}
//endregion
fun undo() {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.remove()
return
}
if (mUndoList.isNotEmpty()) {
mRedoList.add(mUndoList.last())
mUndoList.removeAt(mUndoList.lastIndex)
mPaintView.initCanvas()
mUndoList.forEach {
drawObject(it)
}
mListener?.onEnableUndo(mUndoList.isNotEmpty())
mListener?.onEnableRedo(mRedoList.isNotEmpty())
}
}
fun redo() {
if (mRedoList.isNotEmpty()) {
val obj = mRedoList.last()
mUndoList.add(obj)
mRedoList.removeAt(mRedoList.lastIndex)
drawObject(obj)
mListener?.onEnableUndo(mUndoList.isNotEmpty())
mListener?.onEnableRedo(mRedoList.isNotEmpty())
}
}
fun removeAll() {
mUndoList.clear()
mRedoList.clear()
mStickerView.remove()
mPaintView.initCanvas()
mListener?.onEnableUndo(false)
mListener?.onEnableRedo(false)
}
fun downloadBitmap(): Bitmap {
doneStickerEdit()
return mPaintView.extraBitmap
}
suspend fun saveStickerViewToFile(fileName: String, viewGroup: ViewGroup): String {
return withContext(IO) {
val bitmap = Bitmap.createBitmap(
viewGroup.width, viewGroup.height, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
viewGroup.draw(canvas)
// val dir = File(context.cacheDir, "sticker")
// if (dir.exists().not()) dir.mkdirs()
// val file = File(dir, fileName)
val publicDirectory =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val funnyEmojiDir = File(publicDirectory, "FunnyEmoji")
if (funnyEmojiDir.exists().not()) funnyEmojiDir.mkdirs()
val file = File(funnyEmojiDir, fileName)
val outStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream)
outStream.flush()
outStream.close()
file.path
}
}
private fun drawObject(obj: DrawObject) {
when (obj.drawType) {
DrawType.PATH -> {
// mPaintView.drawPath(obj.pathAndPaint!!)
}
DrawType.STICKER -> {
mPaintView.drawSticker(obj.sticker!!)
}
}
}
//region find double tap inside sticker
private fun findTapedSticker(x: Float, y: Float): Int {
for (i in mUndoList.size - 1 downTo 0) {
val obj = mUndoList[i]
if (obj.drawType == DrawType.STICKER) {
val sticker = obj.sticker!!
if (sticker.contains(x, y)) {
return i
}
}
}
return -1
}
private fun enableEditModeSticker(pos: Int) {
val obj = mUndoList[pos]
val sticker = obj.sticker!!
mStickerView.visibility = View.VISIBLE
mStickerView.currentSticker = sticker
mUndoList.removeAt(pos)
mPaintView.initCanvas()
mUndoList.forEach {
drawObject(it)
}
mRedoList.clear()
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(mRedoList.isNotEmpty())
mListener?.onStickerActive()
mStickerView.setIsTouchInsideSticker()
}
private fun addStickerToPaint(obj: DrawObject) {
mPaintView.drawSticker(obj.sticker!!)
mUndoList.add(obj)
// mRedoList.clear()
mListener?.onEnableUndo(true)
mListener?.onEnableRedo(false)
}
private fun doneStickerEdit() {
if (mStickerView.visibility == View.VISIBLE) {
mStickerView.done()
}
}
}
needs to tweak the swap layer function code in given class not work
needs to implementing swap layer of the sticker so that selected sticker came to top of the stack of the sticker.