【问题标题】:MotionLayout: MotionScene OnClick overrides setOnClickListenerMotionLayout:MotionScene OnClick 覆盖 setOnClickListener
【发布时间】:2019-04-10 02:19:09
【问题描述】:

我刚开始玩MotionLayout。我使用MotionLayout 定义了一个活动布局,它使用MotionScene 来隐藏和显示视图。

MotionScene 转换如下所示:

<Transition
    app:constraintSetStart="@id/collapsed"
    app:constraintSetEnd="@id/expanded">

    <OnClick app:target="@id/nextButton"  />

</Transition>

麻烦的是,当我以编程方式向按钮添加 ClickListener 时没有任何反应:

nextButton.setOnClickListener {
        //do some stuff
    }

这个监听器被完全忽略,但每次点击都会触发转换(视图展开/折叠)。我已经看到有人在哪里 extends MotionLayout 处理点击事件,但似乎有一种更简单的方法可以为按钮添加另一个点击监听器。

问题 1:有没有办法在 MotionLayout 过渡中将 ClickListener 添加到 OnClick 的目标?

问题 2:有没有办法让过渡成为一次性事件? 期望的结果是,如果在单击按钮时视图是折叠的,则视图会展开,但如果它已经展开,则保持展开状态。

最后,我使用命名空间"http://schemas.android.com/apk/res-auto" 和文档clearly 声明targetmode 是OnClick 的属性。但是当我使用mode 时,该项目将无法编译,因为在该命名空间中找不到它。

问题 3:我是否使用了正确的命名空间?

【问题讨论】:

  • 你找到解决办法了吗?我们应该把它作为一个错误发布吗?
  • 我认为这也应该被报告为错误......我对此谷歌感到非常失望。他们制作了这个很酷的新玩具,但它基本上只是演示而已,并不是为实际使用而构建的

标签: android android-motionlayout


【解决方案1】:
  1. 我找不到。
  2. 我发现使用带有“transitionToEnd”值的 clickAction 属性是成功的。这将使运动布局无法返回到 startConstraintSet。
<OnClick
      motion:targetId="@+id/rateUsButton"
      motion:clickAction="transitionToEnd"/>
  1. 这就是我正在使用的命名空间,以及我见过的示例中使用的命名空间。

今天刚遇到同样的问题。我能够通过在我的代码中使用setOnTouchListener 而不是setOnClickListener 来拦截点击。

rateUsButton.setOnTouchListener { _, event ->
    if (event.action == MotionEvent.ACTION_UP) {
        // handle the click
    }
    false
}

我知道这个解决方案不是最好的,但我没有找到其他选择。返回 false 表示此处未处理触摸,因此将由运动布局处理。

【讨论】:

    【解决方案2】:

    您也可以通过删除从一开始以编程方式处理点击

     <OnClick app:target="@id/nextButton"  />
    

    总之。此外,通过检查转换的进度,很容易看到您的视图是否被扩展。因此,您可以使用

    在您的 java/kotlin 文件中以编程方式处理它
    yourButton.setOnClickListener {
        if (yourMotionLayoutId.progress == 0.0)
            yourMotionLayoutId.transitionToEnd
    }
    

    这样,它会检查transition是否处于未发生的状态(进度为0.0)和transition,否则什么都不做。

    【讨论】:

      【解决方案3】:

      我找到了一种更清洁、更正确的方法,你可以这样做.. OnClick 直接从视图中..

      注意:它不适用于:&lt;OnSwipe/&gt; only &lt;OnClick/&gt;

      PD。对不起,我来自墨西哥,正在使用翻译器

      <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
      }
      

      【讨论】:

        【解决方案4】:

        我刚刚使用了这个 hack:点击以编程方式处理,但它触发了隐藏视图,&lt;OnClick&gt;MotionScene 中注册:

        actualVisibleView.setOnClickListener {
                    doSomeLogic()
                    hiddenView.performClick()
                }
        

        MotionScene:

        <Transition
                android:id="@+id/hackedTransitionThanksToGoogle"
                motion:constraintSetEnd="@layout/expanded"
                motion:constraintSetStart="@layout/closed"
                motion:duration="300"
                motion:motionInterpolator="linear">
        
                <OnClick
                    motion:clickAction="transitionToEnd"
                    motion:targetId="@+id/hiddenView" />
        </Transition>
        

        【讨论】:

          【解决方案5】:

          一般来说,如果您需要回调,您可能希望自己控制动画。因此,如果您要添加 onClick 您应该自己调用转换。

          public void onClick(View v) {
             ((MotionLayout)v.getParent()).transitionToEnd());
             // you can decide all the actions and conditionals.
           }
          

          意图是有用的,开发人员并不关心。隐藏/显示 ui 元素等,或在您连接回调之前进行测试。

          【讨论】:

            【解决方案6】:

            你可以实现 MotionLayout.TransitionListener 来处理过渡时的事件。

            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) {
            
            }
            

            }

            【讨论】:

              【解决方案7】:

              这是一个简单的解决方案:

              只需添加这个乐趣:

               @SuppressLint("ClickableViewAccessibility")
              fun View.setOnClick(clickEvent: () -> Unit) {
                  this.setOnTouchListener { _, event ->
                      if (event.action == MotionEvent.ACTION_UP) {
                          clickEvent.invoke()
                      }
                      false
                  }
              }
              

              这就是你如何使用它:

              nextButton.setOnClick {
                      //Do something 
              }
              

              【讨论】:

                【解决方案8】:

                我昨天遇到了这个问题,现在它解决了,我所做的只是在 MotionLayout 标签内添加一个 View 标签。然后给它一个onClick 属性。

                这是预览

                <androidx.constraintlayout.motion.widget.MotionLayout
                    android:id="@+id/buttonMotion"
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:layout_gravity="center"
                    app:layoutDescription="@xml/circle_to_square">
                    <androidx.constraintlayout.utils.widget.ImageFilterView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:id="@+id/puthPic"
                        android:scaleType="centerCrop"
                        android:src="@drawable/red"
                        android:onClick="toggleBroadcasting"
                        />
                    <View
                        android:id="@+id/puthPicView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:onClick="toggleBroadcasting"/>
                </androidx.constraintlayout.motion.widget.MotionLayout>
                

                请访问我的要点here 了解更多信息。 然后,给它一颗星;)

                【讨论】:

                  猜你喜欢
                  • 2011-09-04
                  • 2011-08-26
                  • 1970-01-01
                  • 2013-07-07
                  • 2013-07-16
                  • 2013-09-29
                  • 1970-01-01
                  • 2021-11-12
                  • 2011-09-06
                  相关资源
                  最近更新 更多