I try to get back a view I initialized before in my activity in onCreate(), but I don't know how to do this.
It's look like something like this :
public class activityA extends AppCompatActivity {
public ImageView ivImg1;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.layoutA);
ivImg1 = (ImageView)findViewById(R.id.img1);
}
The image view is empty of any ressource. I create a Handler in an other class :
public class firstClass{
final private imgHandler mHandler = new imgHandler();
final int KEY = 1;
Message msg = mHandler.obtainMessage(KEY,R.drawable.face1,0,ivImg1);
mHandler.sendMessage(msg);
}
And now I would like to set the first argument R.drawable.face1 to the ImageView in my Handler :
public class imgHandler extends Handler {
#Override
public void handleMessage(Message msg){
super.handleMessage(msg);
ImageView viewToSet = (ImageView)msg.obj;
viewToSet.setImageResource(msg.arg1); //Here is the logcat error
}
}
I result on a java.lang.NullPointerException. I think it's because the object I send in the message is empty. In onCreate the view is inside, but when I call it in the message, the findViewByIdisn't in ivImg1.
Class FirstClass is in a new Thread, and class imgHandler is in the UI Thread.
I hope someone can help me.
Sorry for my english.
Related
I have done I game in which I want to call on a method I have in a view from another view. I figured I would somehow have to send the "first view" into the "second view" through the my MainActivity in order for the second view to be able to call on the first view methods. However, I couldn't come up with any way of sending in the first view to the second view through my MainAcitivity, so I decided to change tactics. I now tried to have a function in my MainActivity to handle the interection between the views, but once again I was not able to call on the method from the second View.
Therefore my question is how do you send a view into another view through an Activity, or If that's not possible how do you call on an activity method through a view?
Here is the code (I added some comments to better show the problem I'm having):
public class MainActivity extends AppCompatActivity {
private FishView gameView;
private SmallBall smallBall ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
smallBall = new SmallBall(this);
screen.addView(gameView); // first view
screen.addView(smallBall); //second view
}
//this is the method I want to reach through the View
public void handleAvoidedBall(){
gameView.avoidedBall();
}
}
public class SmallBall extends View {
private final Bitmap sodaCan;
private final static long smallBallPeriod = 60;
private final Handler handler = new Handler();
public SmallBall(Context context) {
super(context);
Paint smallBall = new Paint();
smallBall.setColor(Color.GRAY);
smallBall.setAntiAlias(false);
resetBall();
sodaCan = BitmapFactory.decodeResource(getResources(),R.drawable.sodacan);
Timer movementTimer = new Timer();
movementTimer.scheduleAtFixedRate(smallBallTask, 0, smallBallPeriod);
}
private final TimerTask smallBallTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
invalidate();
if (isBallLanded()){
//Here I want to call on a handleAvoidedBall() in MainActivity
//OR simply have gameView here if possible
// gameView.avoidedBall();
//OR
//SomeMainAcitvityObject.handleAvoidedBall();
}
}
});
}
};
#Override
protected void onDraw(Canvas canvas) {
..... //Do stuff}
}
So as I hopefully have explained somewhat decent now, I'm wondering how to either send gameView into the SmallBall view OR how to call on handleAvoidedBall() in MainActivity from the SmallBall view?
Thank you for your time and hope you have a wonderful day!
Your best option would be to define a listener that you would set on the SmallBallView.
Define the listener:
public interface BallListener {
void onAvoided(SmallBall ball);
}
And then inside your SmallBall class, you would have this method:
public void setListener(BallListener listener){
this.listener = listener;
}
And then call this in your activity, after you've instantiated the SmallBall class:
smallBall.setListener(new SmallBallListener(){
#Override
public void onAvoided(SmallBall ball){
// Do stuff here
}
})
As #LukeWaggoner mentioned, you should consider using listeners instead of making view static in your activity.
You told us, that you'd like to add more than one SmallBall views, so I figure that you don't want to write a listener's code for each of them.
It is easily doable with MainActivity implementing SmallBallListener.
Listener:
public interface SmallBallListener {
void onAvoidedBall();
}
SmallBall class:
public void setListener(SmallBallListener listener){
this.listener = listener;
}
MainActivity:
public class MainActivity extends AppCompatActivity implements SmallBallListener {
private FishView gameView;
private SmallBall smallBall ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
screen.addView(gameView); // first view
// Add 10 small ball views
for(int i = 0; i < 10; i++) {
SmallBall ball = new SmallBall(this);
ball.setListener(this); // MainActivity is a listener here, so each ball has the same listener code
screen.addView(ball);
}
}
//this is the method I want to reach through the View
public void handleAvoidedBall() {
gameView.avoidedBall();
}
#Override
public void onAvoidedBall() { // this is the SmallBallListener method
this.handleAvoidedBall();
}
}
So whichever SmallBall view call listener.onAvoidedBall(), it will fire onAvoidedBall() method in MainActivity class.
Turns out all I had to do was to set:
private FishView gameView;
to:
public static FishView gameView;
And then simply use "MainActivity.gameView" in the SmallBall view. This gave me no additional warings either, so that was good also.
I am making an app that is using Fragments and wish to change the text in a TextView based on a message received in a client thread communicating with a laptop. The client server communication is no issue as the client thread is receiving the strings just fine.
I can't seem to properly figure out how to access the fragments TextView and alter its text.
Here is how I am currently trying to do so:
class ClientThread implements Runnable {
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
LivingRoomFragment frag = (LivingRoomFragment)getSupportFragmentManager().findFragmentById(R.id.LivingRoomFragment);
frag.setText("Inside ClientThread right now");
}
});
}
}
public static class LivingRoomFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
TextView temp;
public LivingRoomFragment(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_room_control_fragment1, container, false);
temp = (TextView) rootView.findViewById(R.id.textView5);
MainActivity main = new MainActivity();
new Thread(main.new ClientThread(requests)).start();
return rootView;
}
public void setText(String s){
temp.setText(s);
}
}
In this case, MainActivity is the activity extending FragmentActivity.
I'm using an emulator and the app always crashing saying there was a null pointer exception at the line using frag.setText("Inside ClientThread right now"), which I believe means that the instance of LivingRoomFragment is null. It is to my understanding so far that this is supposed to be executed using a Handler because you cannot access the UI from a thread without using a method like this.
What am I doing wrong?
I am not sure, try
MainActivity main = (MainActivity)getActivity;
instead of
MainActivity main = new MainActivity();
okay Toast.
now here is your Solution.
create Broadcast Receiver in your Fragments.
create your action,action is an key which diffrenciat your broadcast.
use below sample code.(you do not have post more code,so it may not ecxatly what you want okay?)
class ClientThread implements Runnable {
private Handler mHandler;
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent("my_action");
intent.putExtra("message", "TEXT_YOU_WANT_TO_SET");
sendBroadcast(intent);
// LocalBroadcastManager manager = LocalBroadcastManager
// .getInstance(context);
// LivingRoomFragment frag = (LivingRoomFragment)
// getSupportFragmentManager()
// .findFragmentById(R.id.LivingRoomFragment);
// frag.setText("Inside ClientThread right now");
}
});
}
}
public static class LivingRoomFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
TextView temp;
private MyBroadCastReceiver broadCastReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
broadCastReceiver = new MyBroadCastReceiver();
getActivity().registerReceiver(broadCastReceiver,
new IntentFilter("my_action"));
}
public LivingRoomFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(
R.layout.activity_room_control_fragment1, container, false);
temp = (TextView) rootView.findViewById(R.id.textView5);
MainActivity main = new MainActivity();
new Thread(main.new ClientThread(requests)).start();
return rootView;
}
public void setText(String s) {
temp.setText(s);
}
private class MyBroadCastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent intent) {
// chaneg the TextView text here
if (intent.getAction() != null
&& intent.getAction().equalsIgnoreCase("my_action")) {
temp.setText(intent.getStringExtra("message"));
}
}
}
}
good luck.
My static handler has a WeakReference to my Activity (this is to prevent the well documented memory leak issue).
I post a long delayed message and I want this message delivered to my activity (which should be in the foreground).
My concern is that on orientation change, my activity is destroyed and the handler has a reference to the old activity which should have been destroyed.
In order to get around this in my onCreate for the activity I do this.
if(mHandler == null)
mHandler = new LoginHandler(this);
else {
mHandler.setTarget(this);
}
And my handler is declared as a static global variable:
private static LoginHandler mHandler = null;
and the implementing class is also static as below:
private static class LoginHandler extends Handler {
private WeakReference<LoginActivity> mTarget;
LoginHandler(LoginActivity target) {
mTarget = new WeakReference<LoginActivity>(target);
}
public void setTarget(LoginActivity target) {
mTarget = new WeakReference<LoginActivity>(target);
}
#Override
public void handleMessage(Message msg) {
// process incoming messages here
LoginActivity activity = mTarget.get();
switch (msg.what) {
case Constants.SUCCESS:
activity.doSomething();
break;
default:
activity.setStatusMessage("failed " + msg.obj, STATUS_TYPE_DONE);
}
}
}
What I want to know is if there is something wrong with changing the WeakReference on onCreate or is there anything else wrong with this approach?
Thanks,
So I wrote the following test to figure out whether I had the right idea or not and it seems that m approach is correct. In onCreate we change the WeakReference and the posted message will always get delivered to the activity that is in the foreground. If you change this code to always create a new Handler in onCreate you'll notice the update messages do not get delivered.
public class MainActivity extends Activity {
private static int COUNT = 0;
static LoginHandler mHandler;
private static class LoginHandler extends Handler {
private WeakReference<MainActivity> mTarget;
LoginHandler(MainActivity target) {
mTarget = new WeakReference<MainActivity>(target);
}
public void setTarget(MainActivity target) {
mTarget.clear();
mTarget = new WeakReference<MainActivity>(target);
}
#Override
public void handleMessage(Message msg) {
// int duration = Toast.LENGTH_LONG;
// process incoming messages here
MainActivity activity = mTarget.get();
activity.update(msg.arg1);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mHandler == null)
mHandler = new LoginHandler(this);
else
mHandler.setTarget(this);
((Button)findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Message msg = new Message();
msg.arg1 = COUNT++;
mHandler.sendMessageDelayed(msg, 3000);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void update(int count) {
((TextView) findViewById(R.id.hello_world)).setText("Hello World # "+ count);
}
}
A solution in getting away with activity's destroy-and-create life cycle, if you want to retain the active objects is to make use of the "Retent Fragments".
The idea is simple, you are telling the Android system to " retain" your fragment, when it's associated activity is being destroyed and re created. And make sure you grab the current activity's context in the fragment's onAttach() callable, so you are always updating the correct activity.
Below link has more details:
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
TestaActivity.java
public class TestaActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvText=(TextView)findViewById(R.id.textView1);
tvText.setText("Sample");
}
}
Print.java
public class Print {
public Print(Context tempContext) {
//I want to assign the value to the tvText from here
}
}
In the above example, as you can see I have set the text in tvText to "Sample". In the same way, I need to assign the textView1 ID with some value inside Print class, once it is created.
Please help me to figure out the way to do it.
If your class Print is instantiated when TestaActivity is on the screen, then you can get tvText reference, passing to Print in some way a TestaActivity reference.
Maybe you could pass it via constructor:
From TestaActivity you do:
Print print = new Print(this);
where this represents the instance of TestaActivity.
And then in your Print code you can do:
TextView tvText = (TextView)((TestaActivity)context.findViewById(R.id.textView1));
tvText.setText("Sample");
Another solution is provide an interface from TestaActivity, transparent for the outside, which manage your changes on the textview (or whatever).
Something like that:
private TextView tvText;
public void setTvText(String str){
tvText.setText( str );
}
And then in your Print class:
((TestaActivity)context).setTvText( "Sample" );
try as:
public class Print {
protected TestaActivity context;
public Print(Context tempContext) {
context = tempContext;
}
public void changetextViewtext(final String msg){
context.runOnUiThread(new Runnable() {
#Override
public void run() {
//assign the value to the tvText from here
context.tvText.setText("Hello Test");
}
});
}
}
and call changetextViewtext from Activity for Changeing TextView Text from Print Class
public class TestaActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvText=(TextView)findViewById(R.id.textView1);
tvText.setText("Sample");
Print myPrint = new Print(this);
myPrint.changetextViewtext("Hello World !!!");
}
}
as your need!!!!:)
#imran - the solution is correct except that you would want to pass the TextView as an argument in the constructor or the method.
Harcoding TextView in a method is bad because you cannot be reuse it.
I've got an activity with a handler defined:
final Handler updateHandler = new Handler() {
public void handleMessage(Message msg) {
// Do stuff.
}
};
In my onCreate method, if I assign this handler to a static list outside of my Activity, will it leak (or rather, will that outside list keep a reference to my activity forever)? I.e,:
#Override
public void onCreate(Bundle savedInstanceState) {
SomeStaticClass.addHandler(updateHandler);
}
...
public class SomeStaticClass {
static List<Handler> handlers = new ArrayList<Handler>();
public static void addHandler(Handler handler) {
handlers.add(handler);
}
}
In my onCreate method, if I assign this handler to a static list outside of my Activity, will it leak (or rather, will that outside list keep a reference to my activity forever)?
Yes. Do not put Handlers in static data members.