简体   繁体   中英

How to send message from "Activity" class to a customize layout

I am trying to develop a bluetooth app. I have a class called "ConnectionManagement" which implement the "connect/accept" and "read/write". The information i get from this class i send it to another activity. To do this, i used a Handler, this "handler" must be declared on the activity which i modify.

The problem is i have a class called "VistaJuego" which extends from View, and then i can not declare the handler on this class. I use "VistaJuego" from another class called "Juego" which extends from AppCompatActivity. In this last class i can declare a handler, but i do not know how can i send the information of this handler to my custom layout( "VistaJuego" class).

I really would appreciate any help, or any comment about how to face this inconvenient.

I show you the headers of the classes, maybe you can understand it better.

    public class ConnectionManagement{
       private Context context;
       private Handler handler;
       ...

       public ConnectionManagement(Context context, Handler handler){
          this.context = context;
          this.handler = handler;
          ...
       }
       ...
      
       public class AcceptThread extends Thread{
          ...
      
       }

       public class ConnectThread extends Thread{
          ...
       }
       ...
       
    }

In this class, i have another class which read and write from the socket, and methods to execute the "ConnectionManagement" class.

    public class Juego extends AppCompatActivity {

       protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          requestWindowFeature(Window.FEATURE_NO_TITLE);
          getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.
          LayoutParams.FLAG_FULLSCREEN);
          setContentView(R.layout.juego);
       }

       ...
       ...

    }

In this class, i can declare a "handler", and connect the devices and start the exchange of the messanges between the devices, but i need modify a layout, which is called "juego".

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      <com.game.VistaJuego
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/VistaJuego"
        android:focusable="true"
        android:background="@drawable/mapa_batalla2">

       </com.game.VistaJuego>

    </LinearLayout>

In this xml file, i used a Linear Layout which contains the class "VistaJuego" which implement all my graphics.

    public class VistaJuego extends View {
       ...
       ...
       public VistaJuego(Context context, AttributeSet attrs){
         ...
      
       }
    }

In this class i create my graphics and using a Thread i update the position of these.

In conclusion i need how to send the messages from "Juego" class to "Vistajuego" class (Thread) to update the possition of the drawables.

Thanks in advance to everyone who suggests something to me.

You need to send messages from the class Juego to the class VistaJuego . Since I didn't see enough information about the purpose of the class ConnectionManagement , I will give an example of how to communicate directly between Juego and VistaJuego . I will show you how to use the handlers either using main thread's looper or your own handler thread's looper. But the important point here is to tie the handlers to the correct and same looper that you want to communicate.

1st Option

First option is communicating in main thread's looper. So this is a simplier way than the next option but if you do intensive graphic calculation or any other heavy process, you better stick with the next option.

In the Juego class:

public class Juego extends AppCompatActivity {

    // Let's declare some message constants, you change them for your convenience
    public static final int MSG_UPDATE_GRAPHIC_0 = 0;
    public static final int MSG_UPDATE_GRAPHIC_1 = 1;
    public static final int MSG_UPDATE_GRAPHIC_2 = 2;
    public static final int MSG_UPDATE_GRAPHIC_3 = 3;

    // Do note how we tie the handler to the main looper object
    private final Handler handler = new Handler(Looper.getMainLooper());

    // For creating events I use buttons as example
    Button btnGraphic0Event, btnGraphic1Event, btnGraphic2Event, btnGraphic3Event;

    private VistaJuego vistaJuego;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.
        LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.juego);

        AttributeSet mAttrs = ...;
        vistaJuego = new VistaJuego(getApplicationContext(), mAttrs);
        // May be you want to set VistaJuego to initial or reset state initially
        handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_0); // An empty message to init or reset view

        btnGraphic0Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_0);
            /* Alternatively you can send arguments and/or one object if you
            want to send values. I'll give an example only here for each
            alternative method. You can use them for other for your convenience
            */
            // 1. A message with 2 int arguments
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, arg1, arg2).sendToTarget();
            // 2. Or if you want to send only 1 int argument, just put 0 for 2nd
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, arg1, 0).sendToTarget();
            // 3. A message with an object value
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, obj).sendToTarget();
            // 4. You can even send a bundle object along with the messages
            Bundle bundle = new Bundle();
            bundle.putInt("KEY_FOR_INT_VALUE", 100);
            bundle.putBoolean("KEY_FOR_BOOLEAN_VALUE", true);
            Message message = Message.obtain(handler, MSG_UPDATE_GRAPHIC_0);
            message.setData(bundle);
            message.sendToTarget();
        });

        btnGraphic1Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_1);
        });

        btnGraphic2Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_2);
        });

        btnGraphic3Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_3);
        });

    }

    ...
    ...

}

Let's implement the VistaJuego class:

public class VistaJuego extends View {
    ...
    ...

    public VistaJuego(Context context, AttributeSet attrs){
        ...
        // Attention to the looper!
        new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                // Here you will process the messages that sent from Juego
                switch(msg.what) {
                    case Juego.MSG_UPDATE_GRAPHIC_0:
                    { // The braces are needed for definitons, in case of there is no first place statement
                        // TODO process the message for graphic 0 event
                        // Examples for getting arguments from the message
                        // 1. Get 1 int argument from the message
                        int arg1 = msg.arg1;
                        // 2. Get 2 int arguments from the message
                        int arg1 = msg.arg1;
                        int arg2 = msg.arg2;
                        // 3. Get an object; you can cast if needed
                        MyCustomObject myCustomObject
                        try {
                            myCustomObject = (MyCustomObject) msg.obj;
                        } catch(ClassCastException exception) {
                            // TODO Oops! wrong class
                        }
                        // 4. Get the bundle you sent along with this message
                        Bundle bundle = msg.getData();
                        break;
                    }

                    case Juego.MSG_UPDATE_GRAPHIC_1:
                        // TODO process the message for graphic 1 event
                        break;
                    case Juego.MSG_UPDATE_GRAPHIC_2:
                        // TODO process the message for graphic 2 event
                        break;
                    case Juego.MSG_UPDATE_GRAPHIC_3:
                        // TODO process the message for graphic 2 event
                }
            }
        };
    }
}

This far the examples show how to implement a communication using the main thread's looper which was the 1st option.

2nd Option

Now let's see the second option. In his option we take the work off of the main thread and post the result like updating the ui to the main thread.

public class Juego extends AppCompatActivity {

    // Let's declare some message constants, you change them for your convenience
    public static final int MSG_UPDATE_GRAPHIC_0 = 0;
    public static final int MSG_UPDATE_GRAPHIC_1 = 1;
    public static final int MSG_UPDATE_GRAPHIC_2 = 2;
    public static final int MSG_UPDATE_GRAPHIC_3 = 3;

    // Here we will use a background Handler thread which has a looper object
    private HandlerThread handlerThread;

    private Handler handler;

    // For creating events I use buttons as example
    Button btnGraphic0Event, btnGraphic1Event, btnGraphic2Event, btnGraphic3Event;

    private VistaJuego vistaJuego;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.
        LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.juego);

        handlerThread = new HandlerThread("aNameForHandlerThread");
        handlerThread.setDaemon(true); // Handler thread will be stopped when its creator has been stopped.
        handlerThread.start();
        // Do note how we tie the handler to the custom handler thread's looper object
        handler = new Handler(handlerThread.getLooper());

        AttributeSet mAttrs = ...;
        // Attention here we need to pass the same looper object for the handler of VistaJuego
        vistaJuego = new VistaJuego(getApplicationContext(), mAttrs, handlerThread.getLooper());
        // May be you want to set VistaJuego to initial or reset state initially
        handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_0); // An empty message to init or reset view

        btnGraphic0Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_0);
            /* Alternatively you can send arguments and/or one object if you
            want to send values. I'll give an example only here for each
            alternative method. You can use them for other for your convenience
            */
            // 1. A message with 2 int arguments
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, arg1, arg2).sendToTarget();
            // 2. Or if you want to send only 1 int argument, just put 0 for 2nd
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, arg1, 0).sendToTarget();
            // 3. A message with an object value
            Message.obtain(handler, MSG_UPDATE_GRAPHIC_0, obj).sendToTarget();
            // 4. You can even send a bundle object along with the messages
            Bundle bundle = new Bundle();
            bundle.putInt("KEY_FOR_INT_VALUE", 100);
            bundle.putBoolean("KEY_FOR_BOOLEAN_VALUE", true);
            Message message = Message.obtain(handler, MSG_UPDATE_GRAPHIC_0);
            message.setData(bundle);
            message.sendToTarget();
        });

        btnGraphic1Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_1);
        });

        btnGraphic2Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_2);
        });

        btnGraphic3Event.setOnClickListener(view -> {
            handler.sendEmptyMessage(MSG_UPDATE_GRAPHIC_3);
        });

    }

    ...
    ...

}

Since we use a custom HandlerThread in Juego, we need to pass its looper to the constructor of VistaJuego in order to communicate within the same looper. But in this option we will use an extra handler which will be tied to the main thread's looper in order to be able to update UI elements after background processing:

public class VistaJuego extends View {
    ...
    ...
    // This handler will be used to post updates to th UI thread
    private Handler mainHandler = new Handler(Looper.getMainLooper());

    public VistaJuego(Context context, AttributeSet attrs, @NonNull Looper looper){
        ...
        // Attention to the looper! Must be the same looper as of Juego
        new Handler(looper) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                // Here you will process the messages that sent from Juego
                switch(msg.what) {
                    case Juego.MSG_UPDATE_GRAPHIC_0:
                    { // The braces are needed for definitons, in case of there is no first place statement
                        // TODO process the message for graphic 0 event
                        // Examples for getting arguments from the message
                        // 1. Get 1 int argument from the message
                        int arg1 = msg.arg1;
                        // 2. Get 2 int arguments from the message
                        int arg1 = msg.arg1;
                        int arg2 = msg.arg2;
                        // 3. Get an object; you can cast if needed
                        MyCustomObject myCustomObject
                        try {
                            myCustomObject = (MyCustomObject) msg.obj;
                        } catch(ClassCastException exception) {
                            // TODO Oops! wrong class
                        }
                        // 4. Get the bundle you sent along with this message
                        Bundle bundle = msg.getData();
                        ...
                        mainHandler.post(()-> {
                            // TODO Update UI after background processing
                        });
                        break;
                    }

                    case Juego.MSG_UPDATE_GRAPHIC_1:
                        // TODO process the message for graphic 1 event
                        ...
                        mainHandler.post(()-> {
                            // TODO Update UI after background processing
                        });
                        break;
                    case Juego.MSG_UPDATE_GRAPHIC_2:
                        // TODO process the message for graphic 2 event
                        ...
                        mainHandler.post(()-> {
                            // TODO Update UI after background processing
                        });
                        break;
                    case Juego.MSG_UPDATE_GRAPHIC_3:
                        // TODO process the message for graphic 2 event
                        ...
                        mainHandler.post(()-> {
                            // TODO Update UI after background processing
                        });
                }
            }
        };
    }
}

one last question about the definition of the Handler object:

private Handler handler = new Handler(new Handler.Callback() {
...
...
});

In this definition, the Handler.Callback , it would be like you comment it before?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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