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.
CodePudding user response:
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
});
}
}
};
}
}
CodePudding user response:
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?