Call Activity's methods from fragment

2019-07-30 03:17发布

INTRODUCTION

I have a bluetooth app that consists on an Activity which has 3 buttons that do:

  • Set visibility on and create a server connection
  • Find devices and create a client connection
  • Send something

I have the following classes:

  • Main activity: where I have all the methods, and the button listeners
  • 3 different classes with the Threads: ClientConnection, ServerConnection and ConnectedThread

Now I have to change the desing a little bit and instead of having one activity with all this, using Tabs on the ActionBar I need to create 2 fragments: Server and client.

So, basically, I have to define the Server and send buttons in the Server fragment, and the client button in the Client fragment, and set their listeners.

QUESTION

From each fragment, when using one button, I should call its corresponding method ubicated in the main Activity. I would like to do it this way, instead of copying all the methods and interfaces in each fragment, just to have it simplier.

I tried setting the methods as static, but there are some definitions inside the methods that can't be called with static references. Example:

public class ServerFragment extends Fragment { 
    //...  
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.server_layout, container, false);

        btnServer = (Button) view.findViewById(R.id.buttonServer);   
        btnServer.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                onClickServer();
            }
        });
        //...

_

public class BluetoothActivity extends Activity {
    //...
    public void onClickServer() {
        Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
        startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE);
    }

If I set onClickServer() as static, I have to set REQUEST_DISCOVERABLE too as static, and the startActivityForResult() doesn't let me use a static variable there.

How could I do this?

UPDATE -- Throwing NullpointerException

I have created new class called ClickInterface as suggestions, and there i have defined the 2 interfaces, one for the server and the other for the client.

No, from the fragment, I'm trying to call the method in the onClick method, but is throwing an NPE just in the line where i do the call to the callback's method:

public class ServerFragment extends Fragment implements OnClickListener {

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.server_layout, container, false);

        btnServer = (Button) view.findViewById(R.id.buttonServer);
        btnServer.setOnClickListener(this);

        return view;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.buttonServer:
                serverFragCallback.onServer(); //HERE I RECEIVE THE NPE
                break;
        }
    }

This is the logcat's output:

03-20 16:33:57.581: E/AndroidRuntime(24884): FATAL EXCEPTION: main
03-20 16:33:57.581: E/AndroidRuntime(24884): Process: com.uax.bluetoothconnection, PID: 24884
03-20 16:33:57.581: E/AndroidRuntime(24884): java.lang.NullPointerException
03-20 16:33:57.581: E/AndroidRuntime(24884):    at com.uax.bluetoothconnection.ServerFragment.onClick(ServerFragment.java:43)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.view.View.performClick(View.java:4633)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.view.View$PerformClick.run(View.java:19330)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.os.Handler.handleCallback(Handler.java:733)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.os.Handler.dispatchMessage(Handler.java:95)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.os.Looper.loop(Looper.java:157)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at android.app.ActivityThread.main(ActivityThread.java:5356)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at java.lang.reflect.Method.invokeNative(Native Method)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at java.lang.reflect.Method.invoke(Method.java:515)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
03-20 16:33:57.581: E/AndroidRuntime(24884):    at dalvik.system.NativeStart.main(Native Method

4条回答
趁早两清
2楼-- · 2019-07-30 03:49

Use interface to call method from Activity from any fragment page, for example:

public interface OnServerClickListener {
    public void onUserClickServer();
}

And in Activity:

public class BluetoothActivity extends Activity implements OnServerClickListener {
//...
public void onUserClickServer() {
  onClickServer();
}
}

And now in fragment you can use this:

public class ServerFragment extends Fragment { 
//...  
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.server_layout, container, false);

    btnServer = (Button) view.findViewById(R.id.buttonServer);   
    btnServer.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
             try{
            ((OnServerClickListener)getActivity()).onUserClickServer();
             }
             catch(Exception e){Log.e("ServerFragment","Main Activity doesnt implement OnServerClickListener");
        }
    });

ENJOY!

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-07-30 04:07

as Chris said, you have tu let your activity implement an interface, let's say ClickInterface:

public class BluetoothActivity extends Activity implements ClickInterface{
//...
@override
public void onClickServer() {
    Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
    startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE);
}

the interface would be like this:

public interface ClickInterface {
    public void onClickServer();
}

and this is your fragment:

public class ServerFragment extends Fragment { 
    private ClickInterface mCallback;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mCallback = (ClickInterface) activity;
    }  
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.server_layout, container, false);

    btnServer = (Button) view.findViewById(R.id.buttonServer);   
    btnServer.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            mCallback.onClickServer();
        }
    });
}

So, you're calling "onClickServer()" method on mCallback object, and this method would be called in the activity because it implements the interface

查看更多
姐就是有狂的资本
4楼-- · 2019-07-30 04:08

Making everything static is the wrong way to go on this. Do this:

  1. Define a new interface with the methods you need to call from your fragments
  2. Make your activity implement the interface
  3. In your fragments, cast getActivity() to your interface and call the methods. (Remember to check beforehand, i.e. if (getActivity() instanceof MyInterface) ...
查看更多
Rolldiameter
5楼-- · 2019-07-30 04:12

What you want is a callback interface for the communication from a Fragment to an Activity. The docs already provide a good example for this.

Define an Interface which your Activity implements:

public interface Callback {
    void doSomething();
}
public class YourActivity implements Callback {
    ...
    @Override
    public void doSomething() {
         // your implementation here
    }
}

In your Fragment use this interface if one of the Buttons are clicked.

public class ServerFragment extends Fragment { 

    Callback iCallback;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            iCallback = (Callback) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.server_layout, container, false);

        btnServer = (Button) view.findViewById(R.id.buttonServer);   
        btnServer.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                iCallback.doSomething();
            }
        });
    }
}

With this approach you can leave your logic in the Activity and handle the UI events in the Fragment. Through the events in the Fragment you can invoke the methods in your Activity.

查看更多
登录 后发表回答