Android - OnClick Listener in a separate class

2019-03-11 18:36发布

Is it possible to make a secondary class to hold the OnClick Listener? Meaning not being created in the Activity class?

I just find that putting OnClick listeners in the main activity class is just messy and I would rather have them in separate classes. Thanks

5条回答
三岁会撩人
2楼-- · 2019-03-11 19:10

Instead of putting the onCLicklistener in a separate class, why dont you try to define onClickListener outside onCreate()??

For e.g: like this

onCreate()

yourViewName.setOnClicklistener(listener):

Outside onCreate()

private OnClickListener listener    =   new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

        }
    };
查看更多
\"骚年 ilove
3楼-- · 2019-03-11 19:14

Let me share how I code it using MVP. It's the best way to make a clean code. Remember each classes must have an interface to control it. I will show you with the simplest one.

Suppose you want to Toast a text onClick and control it from another class. Here's how it works. Creating interfaces are for nothing but to connect each other and you can review the code easily.

  1. Create an interface for that MainActivity class.

    public interface MainActivityView {
        void showToast();
    }
    
  2. Create another interface for Presenter class.

    public interface IMainPresenter<V extends MainActivityView> {
        /*Generic Type is to make sure it comes from MainActivity class only and to avoid other class to access it.*/
        void onAttach(V mainView);
        void onButtonClick();
    }
    

Remember interfaces are nothing but to override method for each classes.

  1. Create Presenter class

    public class MainPresenter<V extends MainActivityView> implements IMainPresenter<V> {
    
        private V mainActivityView;
    
        @Override
        public void onAttach(V mainActivityView) {
            this.mainActivityView=mainActivityView;
        }
    
        public V getView() {
            return mainActivityView;
        }
    
        @Override
        public void onButtonClick() {
            getView().onToast(); //This is the method from MainActivity controlling with this class
        }
    }
    
  2. I'll skip, activity_main.xml layout because there's just a button with id="@+id/buttonId." In MainActivityClass,

    public class MainActivity extends AppCompactActivity implements MainActivityView {
    
    Button btn;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            MainPresenter mainPresenter = new MainPresenter();
            mainPresenter.onAttach(this);
    
            btn = findViewById(R.id.buttonId);
    
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mainPresenter.onButtonClick(); //Here, check No.3 again!
                }
             });
        }
    
        @Override
        public void showToast() {
             Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
        }
    }
    
  3. All I want to tell you is that. If you create objects in a class, it cannot make unit testing. That's why you're not seeing any new objects calling in android. So, you can use singleton pattern (Here is Lazy Type) in Presenter class. I'll remove its interface and Generic to see it clearly.

    public class MainPresenter {
    
           private static final MainPresenter mainPresenter = new MainPresenter();
    
           MainPresenter() {}
    
           public static MainPresenter getInstance() {
                   return mainPresenter;
           }
    
           //Some methods here can be get it once you create object with getInstance();
    }
    

And so you can get its methods from MainActivity like this. Instead of creating objects like this...

    MainPresenter mainPresenter = new MainPresenter();

You can get it like this...

    MainPresenter mainPresenter = mainPresenter.getInstance();

More example for singleton pattern can be found here, https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples

  1. Finally, using static is not a very good choice because it uses memory space whether you use it or not. And so, you can create objects within Application Layer get it with a Typecasting. I'm sure you don't need to unit test that Application layer.

    public class AppLayer extends Application {
    
        private MainPresenter mainPresenter;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            mainPresenter = new MainPresenter();
        }
    
        public MainPresenter getMainPresenter() {
            return mainPresenter;  
        }
    

And you need to give a class name within Application in manifest.xml

    <application
    android:name=".AppLayer"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    </application>

And you can get it with a Typecast in MainActivity like this!

    MainPresenter mainPresenter = ((AppLayer)getApplication()).getMainPresenter();
  1. For further studies, I suggest you to learn ButterKnife, Dagger 2 and SOLID Principles. It will help you to create clean coding. Have fun!
查看更多
姐就是有狂的资本
4楼-- · 2019-03-11 19:25

Yes you can. However, making the listener an inner class has one advantage - it can access the fields and variables of your activity class directly. If you make it a separate class, and your listener actually need to access 5 views, your listener constructor might look like this:

MyListener listener = new MyListener(context, button, textView1, textView2, ratingBar, imageView);

Which is kinda bulky too. If your listener is simple, go ahead and make it a separate class. Otherwise, its up to you for readability.

查看更多
Rolldiameter
5楼-- · 2019-03-11 19:27

Sure, that's possible. Just create a class that implements View.OnClickListener and set that as listener to the View. For example:

public class ExternalOnClickListener implements View.OnClickListener {

    public ExternalOnClickListener(...) {
        // keep references for your onClick logic 
    }

    @Override public void onClick(View v) {
        // TODO: add code here
    }

}

And then set an instance of above class as listener:

view.setOnClickListener(new ExternalOnClickListener(...));

The parameterized constructor is optional, but it's very likely you'll need to pass something through to actually make your onClick(...) logic work on.

Implementing a class anonymously is generally easier to work with though. Just a thought.

查看更多
我命由我不由天
6楼-- · 2019-03-11 19:27

You can do it. But just think that you will not have a reference to the activity, neither to it's attributes, including all the views. (unless you make them public or accessible with getters methods).

Also, be extra carefull with storing references to the activity or any members on the listener, since they might avoid the garbage collector from getting the listener memory back.

查看更多
登录 后发表回答