简体   繁体   English

Android onClickListener实现最佳实践

[英]Android onClickListener implementation best practices

There are four ways to add an onClickListener to a clickable View (button, for example): 有四种方法可以将onClickListener添加到可单击的视图(例如按钮):

  1. set the onClick attribute in the layout file which points to a method in the activity, 在布局文件中设置onClick属性,该属性指向活动中的方法,
  2. create an anonymous inner class, 创建一个匿名内部类,
  3. assign the onClickListener to a private member variable. 将onClickListener分配给私有成员变量。
  4. have the Activity context implement the onClickListener interface. 让Activity上下文实现onClickListener接口。

So my question is, how do you choose one of these implementation techniques over another? 所以我的问题是,你如何选择其中一种实现技术呢? Is there a best practices according to certain conditions, or is it just a matter of programmer preference? 根据某些条件是否有最佳实践,还是仅仅是程序员偏好的问题?

Here we use so called callback pattern. 这里我们使用所谓的回调模式。

public class Button {
    private Callback callback;

    public Button(Callback callback) {
        this.callback = callback;
    }

    public void update() {
        // Check if clicked..
        callback.onClick(this);
    }

    public interface Callback {
        public void onClick(Button Button);
    }
}


Button b = new Button(new Callback() {
    @Override
    public void onClick(Button b) {
        System.out.println("Clicked");
    }
});

In our case onClick handler implements the interface View.OnClickListener. 在我们的例子中,onClick处理程序实现了View.OnClickListener接口。

Key points: 关键点:

  • consistency with activity/fragment; 与活动/片段的一致性;
  • access to the members of activity/fragment; 访问活动/片段的成员;
  • readability; 可读性;
  • @Michael Krause showed one more good point about memory leaks ; @Michael Krause展示了关于内存泄漏的另一个好处;

1) Attribute in the XML file can be used only for activity, as @Karakuri mentioned it uses reflection which is slow. 1)XML文件中的属性只能用于活动,因为@Karakuri提到它使用慢的反射。

2) Anonymous inner class has special rules for access to the members of enclosing class (check [1] , [2] ). 2)匿名内部类具有访问封闭类成员的特殊规则(check [1][2] )。 There are some situations when memory leaks can happen (ex. threading with AsyncTask, Handlers). 在某些情况下可能发生内存泄漏(例如使用AsyncTask,Handlers进行线程处理)。

3) Here you have a full access to the members of enclosing class. 3)在这里,您可以完全访问封闭类的成员。

4) Is a variation of 3d. 4)是3d的变化。

Readability depends on your handler size, small logic can be ok to inline, but for larger blocks of code consider 3d and 4th. 可读性取决于您的处理程序大小,小逻辑可以内联,但对于较大的代码块,请考虑3d和4th。

I never use the onClick attribute as it ties the layout to a specific Activity (it must find the method via reflection). 我从不使用onClick属性,因为它将布局绑定到特定的Activity(它必须通过反射找到方法)。 It doesn't work on Fragments. 它不适用于碎片。

Options 2 and 3 are virtually identical. 选项2和3几乎相同。 Option 3 might be more advantageous if you want to use the private member as the OnClickListener of multiple views. 如果要将私有成员用作多个视图的OnClickListener ,则选项3可能更有利。

Option 4 is close to option 3. One key difference is that it changes the class declaration, so if it's important to you to keep the class declaration free of interface implementations (or perhaps you need to maintain binary compatibility of some kind), you may not want to use this option. 选项4接近选项3.一个关键的区别是它改变了类声明,所以如果保持类声明不受接口实现(或者你需要保持某种二进制兼容性)的重要性,你可以不想使用这个选项。

My advice is to avoid option 1 and choose whichever fits your code style best. 我的建议是避免选项1,并选择最适合您的代码风格。 You also aren't required to use the same approach in every single place in your code. 您也不需要在代码中的每个位置使用相同的方法。

There is a four ways to use OnClickListener . 有四种方法可以使用OnClickListener

First way 第一种方式

To define OnClickListener within the method call site. 在方法调用站点中定义OnClickListener

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = findViewById(R.id.myButton);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // do something
            }
        });
    }
}  

First reasons why to avoid this is because it clutters up onCreate method. 避免这种情况的第一个原因是因为它使onCreate方法变得混乱。 This becomes even more apparent when you want to observe click events from multiple views. 当您想要观察来自多个视图的点击事件时,这一点变得更加明显。
The next reason to avoid this is because it doesn't promote code reuse if several buttons should do the same. 避免这种情况的下一个原因是,如果几个按钮应该相同,它不会促进代码重用。

Second way 第二种方式

The second way is almost the same as first except implementation to field is assigned in the class. 第二种方式与第一种方法几乎相同,只是在类中分配了实现到字段。

public class MainActivity extends AppCompatActivity {

    private View.OnClickListener clickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // do something
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = findViewById(R.id.myButton);
        button.setOnClickListener(clickListener);
    }
}  

This way is pretty the same as first one, only advantage is that the method could be reused for several buttons. 这种方式与第一种方式非常相似,唯一的好处是该方法可以重复用于多个按钮。

Third way 第三种方式

This way is to declare an inner class to implement OnClickListener . 这种方式是声明一个内部类来实现OnClickListener If it will be used multiple times is better to define the instance as a field. 如果将多次使用,最好将实例定义为字段。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = findViewById(R.id.myButton);
        button.setOnClickListener(new ButtonClick());
    }

    class ButtonClick implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // do something
        }
    }
}  

The advantage of this way is that it helps to organize the code. 这种方式的优点是它有助于组织代码。 You can easily collapse this internal class and forget about it until you need to look at it. 你可以轻松地折叠这个内部类,忘记它,直到你需要查看它。
The other good reason is that it could be turned in public class and reused in other app areas. 另一个很好的理由是它可以在公共课程中转向并在其他应用领域中重复使用。

Fourth way 第四种方式

The fourth way involves Activity to implement OnClickListener . 第四种方法涉及Activity来实现OnClickListener

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = findViewById(R.id.myButton);
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // do something
    }
}  

The first disadvantage that this way creates a public method in the Activity and there you should pass this activity when setOnClickListener is called. 这种方式的第一个缺点是在Activity中创建一个公共方法,并且在调用setOnClickListener时应该传递this活动。
Second reason to avoid this way is that if another button is added you should determine which button was clicked. 避免这种方式的第二个原因是,如果添加了另一个按钮,您应该确定单击了哪个按钮。 Then you should use switch() or if() statements. 然后你应该使用switch()if()语句。 It isn't performed because it wastes a cycle or several for each button click. 它不会执行,因为它会为每个按钮点击浪费一个或几个周期。
Last disadvantage for this way is that difficult to organize a class. 这种方式的最后一个缺点是难以组织一个班级。 In example you have an Activity which implements multiple interfaces. 在示例中,您有一个实现多个接口的Activity。 Suddenly all of the methods from these interfaces are intertwined together, this becomes more evident after you've added methods to some of those interfaces. 突然,这些接口中的所有方法都交织在一起,在您为某些接口添加方法后,这一点变得更加明显。 Also now you can't add an interface with method named onClick . 此外,您现在无法使用名为onClick方法添加接口。

There is some differences between these ways, but you should choose your way according your code and needs 这些方式之间存在一些差异,但您应根据自己的代码和需求选择自己的方式

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM