在android中实现View.OnClickListener的最佳方法

时间:2015-05-06 16:50:41

标签: java android

假设我们有一个活动,其中有很多关于OnClickListener要注册的观点。

实现这一点的最常用方法是让Activity-Subclass实现OnClickListener,如下所示:

public class ActivityMain extends Activity implements View.OnClickListener
{   
    @Override
    public void onClick(View view)
    {
        switch (view.getId())
        {
            //handle multiple view click events
        }
    }
}

我喜欢实现它的方法是在Activity-Subclass中创建一个私有类,然后让它成为内部类 实现OnClickListener:

public class ActivityMain extends Activity implements View.OnClickListener
{
    private class ClickListener implements View.OnClickListener
    {   
        @Override
        public void onClick(View view)
        {
            switch (view.getId())
            {
                //handle multiple view click events
            }
        }
    }
}

这样代码看起来更有条理,易于维护。

此外,谈论" Is-a"," Has-a"关系,后者似乎是一个很好的实践,因为现在 Activity-Subclass会有一个" Has-a"与ClickListener的关系。 在前一种方法中,我们会说我们的活动 - 子类" Is-a" ClickListener,它不完全正确。

请注意,我并不关心后者会导致的内存开销。

另外,在xml中添加onClick标记是完全不可能的。

那么,实现ClickListener的最佳方法是什么?

请不要建议像RoboGuice或ButterKnife等图书馆。

更新

我想分享一下我最终采用的方法。

我在Activity / Fragment中直接实现了监听器。

就OOP设计而言。 " HAS-A" 方法不会带来任何实际好处,甚至会占用更多内存。考虑到我们将为我们实现的每个类似侦听器创建的嵌套类的数量(以及内存开销),应该明确避免这种方法。

14 个答案:

答案 0 :(得分:44)

首先,Android没有定义关于注册点击侦听器的最佳实践。这完全取决于您的使用案例。

实现Activity的View.OnClickListener接口是可行的方法。因为Android强烈推荐一遍又一遍地实现接口实现,无论它是Activity还是Fragment。

现在如你所述:

public class ActivityMain extends Activity implements View.OnClickListener
{
    private class ClickListener implements View.OnClickListener
    {   
        @Override
        public void onClick(View view)
        {
            switch (view.getId())
            {
                //handle multiple view click events
            }
        }
    }
}

这是你的方法。现在这是你的实现方式,如果你不关心内存开销,这没有什么不妥。但是如果您可以在主类中实现它,那么创建内部类和实现View.OnClickListener的好处是什么,这也可以导致代码清晰度和简单性。

所以这只是一个讨论,而不是获得实现 View.OnClickListener 的最佳解决方案,因为如果你按照每个人的实际观点,你将寻求一个简单且内存有效的解决方案

所以我更喜欢传统方式。它使事情变得简单而有效。检查以下代码:

@Override
public void onClick(View view)
{
    switch (view.getId())
    {
        //handle multiple view click events
    }
}

P.S:你的方法肯定会增加代码行数:P;)

答案 1 :(得分:8)

首先让我们清楚地了解基础知识..

通过实现一个接口,你的课程不会变成那样......就像你说的那样:

“我们的活动 - 子类”是一个“ClickListener,这不完全正确。”

如果您的课程延伸,则该课程只能具有“Is-a”关系,在本例中为Func fn=new Func(Fn); // hidden by syntactic sugar 。实现接口意味着它可以像接口设置合同一样。

一个例子:

班彼得延伸人类..意味着彼得是人类......

彼得班也可以实现程序员,音乐家,丈夫等 意味着彼得可以表现得如上所述。

至于最佳实践,你可以创建一个完全独立的类来实现Activity,如下所示:

OnClickListener

在您的主class MyListener implements View.OnClickListener{ @Override public void onClick(View view) { // do whatever you want here based on the view being passed } } 中,您可以实例化Activity并致电MyListener并传递您的观点:

onClick()

答案 2 :(得分:6)

我在我的<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:background="@color/background_material_light" android:layout_width="match_parent" android:layout_height="match_parent"> <include android:id="@+id/toolbar_actionbar" layout="@layout/toolbar_default" android:layout_width="match_parent" android:layout_height="wrap_content"/> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/toolbar_actionbar"> <view android:layout_width="match_parent" android:layout_height="match_parent" class="it.gmariotti.cardslib.library.recyclerview.view.CardRecyclerView" android:id="@+id/recycler" /> </android.support.v4.widget.SwipeRefreshLayout> </RelativeLayout> button.setOnClickListener(this);中使用Activity,然后在单独的方法中获取implements View.OnClickListener的ID。请参阅下面的示例:

Button

答案 3 :(得分:5)

在这里你可以创建一个btnClickListner对象,然后在你想要为按钮执行onCLieck操作时调用那个btnCLickLisner对象。

让我们假设,在我的活动中,我有一个5到10个按钮,每个按钮单独写一个onclick listner是个坏主意。所以要过来这个,我们可以像下面这样使用..

注册您的按钮

Button button1 = (Button)findViewById(R.id.button1);
Button button2 = (Button)findViewById(R.id.button2);
Button button3 = (Button)findViewById(R.id.button3);
Button button4 = (Button)findViewById(R.id.button4);
Button button5 = (Button)findViewById(R.id.button5);

这里我点击了

后将onclick listner设置为我的按钮
button1.setOnClickListener(btnClickListner);
button2.setOnClickListener(btnClickListner);
button3.setOnClickListener(btnClickListner);
button4.setOnClickListener(btnClickListner);
button5.setOnClickListener(btnClickListner);

这是btnClick Listner实现

View.OnClickListener btnClickListner = new OnClickListener()
    {
  @Override
        public void onClick( View v )
        {
            // TODO Auto-generated method stub
            if( button1.getId() == v.getId() )
            {
                //Do Button1 click operations here

            }
            else if( button2.getId() == v.getId() )
            {

               // Do Button2 click operations here

            }
            else if( button3.getId() == v.getId() )
            {
                 // Do Button3 click operations here

            }
            else if( button4.getId() == v.getId() )
            {
                // Do Button4 click operations here

            }
            else if( button5.getId() == v.getId() )
            {
                // Do Button5 click operations here
            }

        }

     }

答案 4 :(得分:4)

我发现使用Butterknife制作干净的代码。而且因为它使用代码生成(而不是反射),所以它几乎没有性能开销。

public class ActivityMain extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      ButterKnife.inject(this);
  }

  @OnClick(R.id.button_foo)
  void onFoodClicked() {
    // Do some foo
  }

  @OnClick(R.id.button_bar)
  void onBarClicked() {
    // do some bar
  }
}

答案 5 :(得分:2)

您的$('.button').click(function (event) { $(window).bind("scroll", someFunction); } someFunction = function() { //do stuff $(window).unbind("scroll"); // remove scroll listener } 是一个内部非静态类,此'has-a'的耦合与您的类ClickListener实现Activity的情况没有什么不同。这是因为您的内部View.OnClickListener需要ClickListener的实例,并且实际上无法重复使用。我认为你过度工程,实际上并没有获得任何东西。

编辑:要回答您的问题,我希望每个小部件都有匿名ActivityMain。我认为这创造了最好的逻辑分离。我也有类似View.OnClickListener的方法,我把所有逻辑都放在那个小部件上。

答案 6 :(得分:2)

第一种方法比另一种更好,因为View.OnClickListenerInterface而不是abstract class。除了后者可能在各种情况下泄漏,因为你使用的是非静态内部类。

答案 7 :(得分:2)

对此的一点点评论,也许是一些话题。

什么,如果我们不只是实现OnClickListener,我们有一堆其他的Listeners / Callback来实现。在我的意见中,在类中实现所有这些而不是使用匿名类/ lambda将变得混乱。很难记住哪个方法属于哪个接口。

因此,如果我们必须多次实现一个接口(在本例中为OnClickListener),那么在类基础上实现并使用switch / case是一个很好的解决方案。

但是如果我们必须实现多个接口,那么使用匿名类/ lambda

可能是一个很好的解决方案

答案 8 :(得分:1)

对于这种特殊情况,我会说维护OnClickListener的单个实例是最适合您的方法。您将拥有“Has-a”关系,因为您使用onClick(View view)回调中的视图ID处理行为,所以不需要创建多个实例。

public class ActivityMain extends Activity implements View.OnClickListener {

    private View.OnClickListener mClickListener = new View.OnClickListener() {   
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                //handle multiple view click events
            }
        }
    };

}

答案 9 :(得分:1)

只是你使用like not implements子类或不处理click事件就像这样做。

android.view.View.OnClickListener method_name = new android.view.View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            // put your code .
        }
    };

并将点击事件处理为按钮ya任何类型的点击事件,如

button_name.setOnClickListener(method_name);

它的工作非常简单 感谢

答案 10 :(得分:0)

It really depends on what you want to achieve. If you have e.g. a complex functionality with threading, dependencies, etc., I personally like to decouple it completely from the Activity into a separate class themeProvider.reload($injector);, that does the heavy stuff, knows about certain XyzActions and returns them results, if needed. My Invokers are basically objects, that implement Invoker/OnClick/etc.OnTouchs and bind themselves to needed actions. E.g. there could be a Listener implementing LoginInvoker for a OnClickListener and an Button and also a generic ImageView that gets invoked when a ActionListener is clicked. The MenuItem has update methods for showing progress to the user and the result of the bound action. The action posts updates to its Invokers and can be garbage collected, if all of them die, because it has no connection to the UI.

For less complex actions, I couple them directly to the Android component (i.e. Invoker/Activity/Feagment) and also call them View, with the big difference of them implementing the UI callbacks directly.

In both cases I declare the actions as members, so I can see on a quick glance what specific actions the Android component supports.

If there's something trivial like "show a Toast if button is pressed", I use anonymous inner classes for the UI callbacks, because you normally don't care that much about them with regards to maintainability.

答案 11 :(得分:0)

 public class ProfileDetail extends AppCompatActivity implements View.OnClickListener {

          TextView tv_address, tv_plan;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_profile_detail);



            tv_address = findViewById(R.id.tv_address);
            tv_plan = findViewById(R.id.tv_plan);

            tv_address.setOnClickListener(this);
            tv_plan.setOnClickListener(this);
    }
 @Override
    public void onClick(View view) {
            switch (view.getId()) {
                case R.id.tv_plan:
                    startActivity(new Intent(getApplicationContext(),PlanActivity.class));
                    break;
                case R.id.tv_address:
                     startActivity(new Intent(getApplicationContext(),AddressActivity.class));
                    break;
            }
        }
}

答案 12 :(得分:0)

swap(1, 2)

答案 13 :(得分:0)

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Chronometer chronometer;
    private Button startButton;
    private Button stopButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        chronometer = findViewById(R.id.chronometer);
        startButton =findViewById(R.id.startBtn);
        stopButton = findViewById(R.id.stopBtn);

        startButton.setOnClickListener(this);
        stopButton.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.startBtn:
                chronometer.start();
                break;
            case R.id.stopBtn:`
                chronometer.stop();
                break;

        }
    }

}