I\'m just starting to play with MotionLayout
. I have defined an activity layout using MotionLayout
that uses a MotionScene
to hide and sh
I found a cleaner and more correct way to do it, you can do this .... OnClick directly from view ..
Note: It does not work with: <OnSwipe/>
only <OnClick/>
PD. I'm sorry, I'm from Mexico and I'm using the translator
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/play_pause_button_collapsed"
android:layout_width="30dp"
android:layout_height="50dp"
app:srcCompat="@drawable/ic_play_arrow_black_48dp"
android:layout_marginTop="25dp"
android:elevation="2dp"
android:alpha="0"
android:onClick="handleAction"
tools:ignore="ContentDescription" />
fun handleAction(view: View) {
//handle click
}
You can implement MotionLayout.TransitionListener to handler event when transition.
public class LoginActivity extends AppCompatActivity implements MotionLayout.TransitionListener {
private static final String TAG = "LoginActivity";
private FirebaseAuth mAuth;
private LoginLayoutBinding binding;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = LoginLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// initialize the FirebaseAuth instance.
mAuth = FirebaseAuth.getInstance();
binding.getRoot().addTransitionListener(this);
}
@Override
public void onStart() {
super.onStart();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseUser currentUser = mAuth.getCurrentUser();
updateUI(currentUser);
}
private void updateUI(FirebaseUser currentUser) {
hideProgressBar();
if (currentUser != null) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}
private void hideProgressBar() {
binding.progressBar2.setVisibility(View.GONE);
}
private void createAccount(String email, String password) {
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "createUserWithEmail:success");
FirebaseUser user = mAuth.getCurrentUser();
updateUI(user);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "createUserWithEmail:failure", task.getException());
Toast.makeText(LoginActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
updateUI(null);
}
}
});
}
private void signIn(String email, String password) {
mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithEmail:success");
FirebaseUser user = mAuth.getCurrentUser();
updateUI(user);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithEmail:failure", task.getException());
Toast.makeText(LoginActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
updateUI(null);
}
}
});
}
@Override
public void onTransitionStarted(MotionLayout motionLayout, int startId, int endId) {
}
@Override
public void onTransitionChange(MotionLayout motionLayout, int startId, int endId, float progress) {
}
@Override
public void onTransitionCompleted(MotionLayout motionLayout, int currentId) {
if (currentId==R.id.end){
binding.btnLogin.setText(R.string.sign_up);
binding.textView3.setEnabled(false);
binding.textView2.setEnabled(true);
}else {
binding.btnLogin.setText(R.string.login);
binding.textView2.setEnabled(false);
binding.textView3.setEnabled(true);
}
}
@Override
public void onTransitionTrigger(MotionLayout motionLayout, int triggerId, boolean positive, float progress) {
}
}
<OnClick
motion:targetId="@+id/rateUsButton"
motion:clickAction="transitionToEnd"/>
Just ran into the same issue today. I was able to intercept the click by using setOnTouchListener
instead of setOnClickListener
in my code.
rateUsButton.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
// handle the click
}
false
}
I know this solution isn't the best but I didn't find another option. Returning false means the touch was not handled here and thus will be handled by the motion layout.
Here is simple solution:
Just add this fun:
@SuppressLint("ClickableViewAccessibility")
fun View.setOnClick(clickEvent: () -> Unit) {
this.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_UP) {
clickEvent.invoke()
}
false
}
}
This is how you use it:
nextButton.setOnClick {
//Do something
}
In general if you need a callback you probably will want to control the animation yourself. So if you are adding an onClick you should call the transition yourself.
public void onClick(View v) {
((MotionLayout)v.getParent()).transitionToEnd());
// you can decide all the actions and conditionals.
}
The intent was is useful what the developer does not care. hide/reveal of ui elements etc. or for testing before you get to wiring up the callbacks.
You can also just handle the click programmatically from the beginning by removing
<OnClick app:target="@id/nextButton" />
altogether. Also it is easy to see whether or not your view is expanded by checking the progress of your transition. So you can programmatically handle it in your java/kotlin file with
yourButton.setOnClickListener {
if (yourMotionLayoutId.progress == 0.0)
yourMotionLayoutId.transitionToEnd
}
This way, it will check if the transition is in the state where it has not occurred (progress will be 0.0) and transition, otherwise, it will do nothing.