Last active
December 3, 2021 04:53
-
-
Save fanjavaid/38d52ce6d16072f58923a623b60f26cb to your computer and use it in GitHub Desktop.
Swipe Button using Motion Layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.tempercube.motionlayout.motions | |
import android.animation.ObjectAnimator | |
import android.animation.ValueAnimator.REVERSE | |
import android.graphics.Color | |
import android.os.Build | |
import android.os.Bundle | |
import android.os.Handler | |
import android.os.Looper | |
import android.view.Gravity | |
import android.view.View | |
import android.view.animation.AccelerateDecelerateInterpolator | |
import android.view.animation.AccelerateInterpolator | |
import android.view.animation.BounceInterpolator | |
import android.view.animation.LinearInterpolator | |
import android.widget.* | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.appcompat.widget.AppCompatImageView | |
import androidx.constraintlayout.motion.widget.* | |
import androidx.constraintlayout.motion.widget.MotionLayout.DEBUG_SHOW_PATH | |
import androidx.constraintlayout.motion.widget.MotionLayout.DEBUG_SHOW_PROGRESS | |
import androidx.constraintlayout.motion.widget.OnSwipe.* | |
import androidx.constraintlayout.widget.ConstraintLayout | |
import androidx.constraintlayout.widget.ConstraintSet | |
import androidx.constraintlayout.widget.ConstraintSet.* | |
import com.google.android.material.snackbar.Snackbar | |
import com.google.android.material.textview.MaterialTextView | |
import com.tempercube.motionlayout.R | |
import kotlinx.android.synthetic.main.activity_sample7.* | |
class SampleActivity7 : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_sample7) | |
val constraintLayout = createMotionLayout() | |
activity7_root.addView( | |
constraintLayout, | |
RelativeLayout.LayoutParams( | |
RelativeLayout.LayoutParams.MATCH_PARENT, | |
RelativeLayout.LayoutParams.WRAP_CONTENT | |
).apply { | |
addRule(RelativeLayout.ALIGN_PARENT_START) | |
addRule(RelativeLayout.ALIGN_PARENT_TOP) | |
addRule(RelativeLayout.ALIGN_PARENT_END) | |
} | |
) | |
} | |
private fun createMotionLayout(): MotionLayout { | |
val label = createLabel() | |
val swipeButton = createButtonView() | |
val main = MotionLayout(this).apply { | |
id = R.id.constraint_container | |
layoutParams = ConstraintLayout.LayoutParams( | |
ConstraintLayout.LayoutParams.MATCH_PARENT, | |
ConstraintLayout.LayoutParams.WRAP_CONTENT | |
) | |
setBackgroundResource(R.drawable.shape_circle_black) | |
setDebugMode(DEBUG_SHOW_PATH and DEBUG_SHOW_PROGRESS) | |
} | |
main.setTransitionListener(object : MotionLayout.TransitionListener { | |
override fun onTransitionStarted( | |
motionLayout: MotionLayout?, | |
startId: Int, | |
endId: Int | |
) { | |
} | |
override fun onTransitionChange( | |
motionLayout: MotionLayout?, | |
startId: Int, | |
endId: Int, | |
progress: Float | |
) { | |
} | |
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { | |
if (currentId == motionLayout?.endState) { | |
motionLayout.progress = 1f | |
motionLayout.isInteractionEnabled = false | |
Snackbar.make( | |
this@SampleActivity7, | |
main, | |
"Creating transaction", | |
Snackbar.LENGTH_SHORT | |
).show() | |
} | |
} | |
override fun onTransitionTrigger( | |
motionLayout: MotionLayout?, | |
triggerId: Int, | |
positive: Boolean, | |
progress: Float | |
) { | |
} | |
}) | |
main.addView(label) | |
main.addView( | |
swipeButton, | |
ConstraintLayout.LayoutParams( | |
150, | |
150 | |
) | |
) | |
// Initial Layout | |
createConstraint(label, swipeButton, main) | |
// Start | |
val startSet = createStartSet(label, swipeButton, main) | |
// End | |
val endSet = createEndSet(label, swipeButton, main) | |
// Create motion transition | |
val motionScene = MotionScene(main) | |
val transition = createTransition(motionScene, startSet, endSet) | |
transition.addOnSwipeEvent(swipeButton) | |
// Update alpha of the label on Progress position 70% | |
val key = KeyAttributes().apply { | |
this.framePosition = 70 | |
this.setValue(Key.ALPHA, 0f) | |
this.setViewId(label.id) | |
}.clone() | |
transition.addKeyFrame( | |
KeyFrames().apply { | |
addKey(key) | |
} | |
) | |
motionScene.addTransition(transition) | |
main.scene = motionScene | |
main.setTransition(transition.id) | |
// Additional animation when user open screen bounce the circle image | |
Handler(Looper.getMainLooper()).postDelayed({ | |
ObjectAnimator.ofFloat(swipeButton, View.TRANSLATION_X, 100f).apply { | |
repeatMode = REVERSE | |
repeatCount = 1 | |
duration = 200 | |
interpolator = AccelerateDecelerateInterpolator() | |
}.start() | |
}, 500) | |
return main | |
} | |
private fun createLabel(): MaterialTextView { | |
return MaterialTextView(this).apply { | |
id = R.id.default_text | |
text = "Setuju dan Bayar" | |
gravity = Gravity.CENTER_HORIZONTAL | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |
setTextAppearance(R.style.TextAppearance_MaterialComponents_Headline6) | |
} else { | |
textSize = 20f | |
} | |
setTextColor(Color.parseColor("#EEEEEE")) | |
} | |
} | |
private fun createButtonView(): ImageView { | |
return AppCompatImageView(this).apply { | |
id = R.id.default_button | |
setImageResource(R.drawable.ic_right_arrow_circle) | |
} | |
} | |
private fun createConstraint(text: TextView, button: ImageView, layout: ConstraintLayout) { | |
createStartSet(text, button, layout) | |
} | |
private fun createStartSet( | |
text: TextView, | |
button: ImageView, | |
layout: ConstraintLayout | |
): ConstraintSet { | |
return ConstraintSet().apply { | |
clone(layout) | |
connect(text.id, START, layout.id, START) | |
connect(text.id, END, layout.id, END) | |
connect(text.id, TOP, layout.id, TOP) | |
connect(text.id, BOTTOM, layout.id, BOTTOM) | |
connect(button.id, START, layout.id, START) | |
connect(button.id, TOP, layout.id, TOP) | |
connect(button.id, BOTTOM, layout.id, BOTTOM) | |
applyTo(layout) | |
} | |
} | |
private fun createEndSet( | |
text: TextView, | |
button: ImageView, | |
layout: ConstraintLayout | |
): ConstraintSet { | |
return ConstraintSet().apply { | |
clone(layout) | |
clear(button.id, START) | |
connect(button.id, END, layout.id, END) | |
connect(button.id, TOP, layout.id, TOP) | |
connect(button.id, BOTTOM, layout.id, BOTTOM) | |
setAlpha(text.id, 0f) | |
applyTo(layout) | |
} | |
} | |
private fun createTransition( | |
scene: MotionScene, | |
startSet: ConstraintSet, | |
endSet: ConstraintSet | |
): MotionScene.Transition { | |
val startSetId = R.id.start_id | |
val endSetId = R.id.end_id | |
val transitionId = View.generateViewId() | |
return TransitionBuilder.buildTransition( | |
scene, | |
transitionId, | |
startSetId, | |
startSet, | |
endSetId, | |
endSet | |
) | |
} | |
} | |
private fun MotionScene.Transition.addOnSwipeEvent(view: View) { | |
setOnSwipe( | |
OnSwipe().apply { | |
dragDirection = DRAG_RIGHT | |
touchAnchorId = view.id | |
touchAnchorSide = SIDE_RIGHT | |
onTouchUp = ON_UP_AUTOCOMPLETE_TO_START | |
setMaxAcceleration(20) | |
} | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment