So I am converting my application, after loads of research I decided to convert my Activities into Fragments. That was a success, but now the slightly harder part comes in, to implement the code from my Activity to the fragments. So I used getView(). and getActivity(). to fix the issues, and all was fine. As you can see below...
public class HomeFragment extends Fragment {
public static HomeFragment newInstance(String title) {
HomeFragment homeFragment = new HomeFragment();
Bundle bundle = new Bundle();
bundle.putString("title", title);
homeFragment.setArguments(bundle);
return homeFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_activity_home, container, false);
// Time function - Displays timeview on Card
final boolean keepRunning = true;
Thread thread = new Thread(){
#Override
public void run(){
while(keepRunning){
// Make the thread wait half a second (if you're only showing time up to seconds, it doesn't need to be updating constantly)
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Toast.makeText(getActivity().getApplicationContext(), "Default Signature Fail", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
getActivity().runOnUiThread(new Runnable(){
#Override
public void run(){
TextView time = (TextView) getView().findViewById(R.id.time);
time.setText(DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR));
}
});
}
}
};
thread.start();
// Date function - Displays dateview on Card
final boolean keepRunning1 = true;
Thread thread_two = new Thread(){
#Override
public void run(){
while(keepRunning1){
// Make the thread wait half a second. If you want...
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Toast.makeText(getActivity().getApplicationContext(), "Default Signature Fail", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
getActivity().runOnUiThread(new Runnable(){
#Override
public void run(){
TextView date = (TextView) getView().findViewById(R.id.date);
date.setText(DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR));
}
});
}
}
};
thread_two.start();
return view;
}
}
When running the application, it displays the app for approx 2 secs and then force closes, so I checked the logs for any errors. And this came up --
05-20 16:54:17.213: E/AndroidRuntime(26473): java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
05-20 16:54:17.213: E/AndroidRuntime(26473): at com.activelauncher.fragments.HomeFragment$1$1.run(HomeFragment.java:54)
I'm fairly new to Fragments I have had more experience working with Activities so I don't know what the issue is here. So I check line 54 and it was this line --
time.setText(DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR));
However I don't see any issues or errors there? Is there something I am missing?
Thanks for reading this.
Change this
TextView time = (TextView) getView().findViewById(R.id.time);
to
TextView time = (TextView) view.findViewById(R.id.time);
And make this final
final View view;
Simailarly
TextView date = (TextView) view.findViewById(R.id.date);
Try this..
Change this..
TextView time = (TextView) getView().findViewById(R.id.time);
time.setText(DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR));
to
TextView time = (TextView) view.findViewById(R.id.time);
time.setText(""+DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR));
and also
TextView date = (TextView) view.findViewById(R.id.date);
date.setText(""+DateUtils.formatDateTime(getActivity().getBaseContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR));
Related
I'm trying to set the text of a TextView in my Android app using the following function:
#Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
if (!stateChanges.getFrom().getSubscribed() && stateChanges.getTo().getSubscribed()) {
new AlertDialog.Builder(this)
.setMessage("You have successfully subscribed to push notifications!")
.show();
// Get player ID and output to Main Activity
TextView playerIdView = findViewById(R.id.playerIdView);
playerIdView.setText(stateChanges.getTo().getUserId());
}
Log.i("Debug", "onOSPermissionsChanged: " + stateChanges);
}
This uses the OneSignal API to get the user's unique ID, which is returned as a string. After some debugging I realised the contents of a TextView can't be changed outside of the onCreate() method. However, the stateChanges parameter is required, which only exists within onOSSubscriptionChanged. Is there any way of getting around this?
EDIT: the error was elsewhere. stateChanges.getTo().getUserId() was returning null.
You need to set it on UI thread
playerIdView.post(new Runnable() {
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());
}
});
or
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());}
};
mainHandler.post(myRunnable);
you need to initialize you textview in your onCreateView() method and after that you can use that textView pretty much anywhere as long as you are in UI thread. So change your code to below:
Declare your textview globally so that you can use it anywhere in your activity instance.
TextView playerIdView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
playerIdView = findViewById(R.id.playerIdView);
}
and then in your onSSubscription method just do the following:
#Override
public void onOSSubscriptionChanged(OSSubscriptionStateChanges stateChanges) {
if (!stateChanges.getFrom().getSubscribed() && stateChanges.getTo().getSubscribed()) {
new AlertDialog.Builder(this)
.setMessage("You have successfully subscribed to push notifications!")
.show();
// Get player ID and output to Main Activity
playerIdView.setText(stateChanges.getTo().getUserId());
}
Log.i("Debug", "onOSPermissionsChanged: " + stateChanges);
}
Try This
runOnUiThread(new Runnable(){
#override
public void run() {
playerIdView.setText(stateChanges.getTo().getUserId());
}
})
I need may load at first bacground (setContentView(R.layout.activity_logo); ) after (6 second) Text in TextView4. but program make wrong. After start application, app wait 6 second and next build setContentView(R.layout.activity_logo); and write to TextView4
Why?
Thanks
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
try {
TimeUnit.SECONDS.sleep(6);
TextView textview4 = (TextView) findViewById(R.id.textView4);
textview4.setText("alalaalalalalalal");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
TextView textview4 = (TextView) findViewById(R.id.textView4);
textview4.setText("alalaalalalalalal");
}
}, 6000);
}
sleep() blocks the UI thread and your UI cannot draw itself.
Use e.g. a Handler and postDelayed() to post a Runnable to run at a later time without blocking the UI thread.
I have a tab fragment inside of a container activity.
I would like to download some data to display in the tab. In the tab, I have made an asynctask that I execute just after inflating the fragment layout in onCreateView. When I do it this way, the AsyncTask's doInBackground... work occurs on the main thread and the view does not load until it is done. All of my progress... logs show up at the same time as soon as the task is done.
However, if I put a button in the fragment layout and start the asynctask work as a response to the button click, it works as expected, I get my normally spaced progress updates.
So, why does AsyncTask run on the main thread if it is the first thing to happen when my fragment starts? And, how can I prevent this? My ultimate goal is to show a progress wheel while the data is downloading.
Edit. Here is where I call my AsyncTask.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.temp_my_moments, container, false);
getMyClips(getActivity(), rootView, p_session, p_person,progressBar);
//If I use this button, it works fine:
// test = (Button) rootView.findViewById(R.id.testbutton);
// test.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// progressBar =(ProgressBar) rootView.findViewById(R.id.myMomentsProgress);
// getMyClips(getActivity(), rootView, p_session, p_person, progressBar);
// }
// });
return rootView;
}
And here is getMyClips():
public void getMyClips(Context context,View view,String thisSession,String thisPerson,ProgressBar progressBar) {
Log.d("Currently running", "getMyClips");
JSONObject params = new JSONObject();
try {
params.put("Function", clipsMine_apiCall);
params.put("p_session", thisSession);
params.put("p_person", thisPerson);
} catch (JSONException e1) {
e1.printStackTrace();
}
ApiClipCaller webServiceTask = new ApiClipCaller(context,view,progressBar);
if (webServiceTask.hasConnection()) {
webServiceTask.execute(params);
} else {
//TODO no internet connection
}
}
Edit Here is my AsyncTask. This is inside the Fragment:
public class ApiClipCaller extends AsyncTask<JSONObject, Integer, ApiResponse> {
public Context context;
public String clipID;
public String sessionID;
public String personID;
public String functionName;
public View view;
public ProgressBar progressBar;
public final String apiURL = "...."; //need to keep private
LinearLayout loading;
public ApiClipCaller(Context c, View v,ProgressBar progressBar) {
this.context = c;
this.view = v;
this.progressBar = progressBar;
loading = (LinearLayout) view.findViewById(R.id.loadingMyMoments);
}
#Override
protected ApiResponse doInBackground(JSONObject... params) {
JSONObject realParams = params[0];
try {
functionName = realParams.getString("Function");
} catch (JSONException e) {
e.printStackTrace();
}
ApiResponse responseObject = masterFunction(realParams);
//making some work to see if it is running correctly
for (int i=10;i<=100;i += 10)
{
try {
Thread.sleep(1000);
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return responseObject;
}
#Override
protected void onProgressUpdate(Integer... progress) {
loading.setVisibility(View.VISIBLE);
progressBar.setProgress(progress[0]);
Log.d("progress",Integer.toString(progress[0]));
}
#Override
protected void onPostExecute(ApiResponse responseObject) {
loading.setVisibility(View.GONE);
if (functionName.equals(clipsMine_apiCall)) {
JSONObject clipObject = responseObject.getResponseJSONObject();
//just testing here to see if I can get data back in the fragment view when it is done.
///This works as expected.
String responseString = responseObject.getResponseString();
TextView showAsyncResults = (TextView) view.findViewById(R.id.testAsyncTask);
showAsyncResults.setText(responseString);
}
super.onPostExecute(responseObject);
}
}
So, it seems like the answer is that I was going about this all wrong. It it is not possible to launch asynctask when the first activity or fragment loads, because the onCreate etc. methods are not actually on the UI thread. So, AsyncTask cannot be executed directly from there, as Ordous pointed out.
Instead, it seems like the solution is to load information when the application first starts by creating a class which extends Application and doing the loading work from there, because this will necessarily be on the main thread. The methods in there can be called from the activity to access the layouts and make a progress bar or similar.
Reference: http://developer.android.com/reference/android/app/Application.html
A good example: http://www.intertech.com/Blog/androids-application-class/
So I have a music application. I am trying to update the UI with the progress of the media player (like current time, current song, album cover) everytime the song changes. I found that using interfaces was a awesome magical way of communication between activity and fragments so I implemented an interface in my MusicManger class. My code will show what and how did it.
Two problems
1) Commented look below, ExecutorService seems to stop after one loop. No Errors in catch block (this is why I tagged with java)
2) Commented please look, All the System.out methods print but the UI doesn't update. I do believe I called the method from mainThread so it should update.
I'll show code in logical order will add titles in bold before code segment to tell you basic idea of code.
Passing UI references from fragment to MusicManager class, code below in Fragment class
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_songlist, container, false);
// Set the adapter
TextView musicManagerSongName, musicManagerCurrent, musicManagerTotal;
ProgressBar musicManagerProgress;
ImageView musicManagerImageView;
mListView = (AbsListView) view.findViewById(R.id.slist);
musicManagerSongName = (TextView)view.findViewById(R.id.textView12);
musicManagerCurrent = (TextView)view.findViewById(R.id.textView10);
musicManagerTotal = (TextView)view.findViewById(R.id.textView11);
musicManagerProgress = (ProgressBar)view.findViewById(R.id.progressBar);
musicManagerImageView = (ImageView)view.findViewById(R.id.imageView2);
MainActivity.mediaPlayer.passUIReferences(musicManagerSongName, musicManagerCurrent, musicManagerTotal, musicManagerProgress, musicManagerImageView, view);
// line above is a method within MusicManager that takes the references will show code next!
ImageButton playbutton = (ImageButton)view.findViewById(R.id.playbuttonbar);
ImageButton nextButton = (ImageButton)view.findViewById(R.id.nextbuttonbar);
ImageButton backButton = (ImageButton)view.findViewById(R.id.backbuttonbar);
ImageButton toggleButton = (ImageButton)view.findViewById(R.id.shufflebuttonbar);
ImageButton pausebutton = (ImageButton)view.findViewById(R.id.pausebuttonbar);
playbutton.setBackgroundResource(R.drawable.playbuttonbar);
playbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(1);
}catch(Exception e) {
}
}
});
backButton.setBackgroundResource(R.drawable.backbutton1);
nextButton.setBackgroundResource(R.drawable.nextbutton1);
toggleButton.setBackgroundResource(R.drawable.shufflebuttonselected);
pausebutton.setBackgroundResource(R.drawable.pausebutton1);
pausebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(0);
} catch (Exception e){
}
}
});
mListView.setAdapter(mAdapter);
((MainActivity) mListener).restoreActionBar();
return view;
}
As Commended above the code that is located in MusicManager class that takes references and stores them. Also shows interface implementation with MusicManager class. And the Executor service
public void passUIReferences(View... views) {
this.uiElements = views;
}
private ExecutorService executorService = Executors.newSingleThreadExecutor();
private MediaplayerUpdateInterface uiUpdateInterface;
public MediaPlayerManager(MediaplayerUpdateInterface inter) {
this.player = new MediaPlayer();
this.uiUpdateInterface = inter;
// The below line starts the single thread while loop for excutorservice and only loops and prints "this" once after I start one song then it never loops again
executorService.submit(new Runnable() {
#Override
public void run() {
while(true) {
if (player.isPlaying() && uiElements != null) {
System.out.println("this");
uiUpdateInterface.updateUI(uiElements, 0);
}
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
public interface MediaplayerUpdateInterface {
public void updateUI(View[] views, int type);
}
Finally some code from MainActivity class that actually is suppose to update the UI note that both println's work as expected but only once as stated above because of the executorservice issue
public static MediaPlayerManager mediaPlayer = new MediaPlayerManager(new MediaPlayerManager.MediaplayerUpdateInterface() {
#Override
public void updateUI(View[] views, int type) {
System.out.println("check1 " + type);
updateMediaplayerViews(views, type);
}
});
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The view array is shown perviously! Also the last view in the array is shown as the main view for songlist fragment.
I am sorry for all the code I've tried to debug it as you can see from my println's there is just something I am unaware of going on here.
Ok so there was an error that I needed to catch to see within the following code:
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The issue is I was trying to change the view from a different thread then the one which created it. Solving it was pretty long and painful but basically I made it nonstactic used more interfaces then used the famous
Mainactivity.runOnUiThread(new Runnable(....));
I'm trying perform some steps inside a function on Android. I would like to tell to user what is happen in a specific moment without exit of my function. something like it:
public boolean updateServiceList() {
LinearLayout start = (LinearLayout) findViewById(R.id.start);
LinearLayout major = (LinearLayout) findViewById(R.id.major);
TextView messenger = (TextView) findViewById(R.id.messenger);
Integer i = 0;
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isOnline = cm.getActiveNetworkInfo().isConnectedOrConnecting();
if(isOnline==false) {
Button exit = (Button) findViewById(R.id.exit_btn);
ProgressBar pg = (ProgressBar) findViewById(R.id.progress);
exit.setText("OK");
exit.setVisibility(View.VISIBLE);
exit.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
gMain.this.finish();
}
});
pg.setVisibility(View.GONE);
messenger.setText(R.string.nointernet);
while(isOnline==false) {
i++;
messenger.setText(messenger.getText()+"Try: "+ i.toString);
boolean isOnline = cm.getActiveNetworkInfo().isConnectedOrConnecting();
}
return false;
} else {
isOnline = cm.getActiveNetworkInfo().isConnected();
while (isOnline==false || 1==1) {
isOnline = cm.getActiveNetworkInfo().isConnected();
}
}
return true;
}
My problem is in "while(isOnline)" where I can't see the update messages. I tried invalidate() and postInvalidate() there but no results. Any ideas?
EDIT:
I found the solution! There it is:
How to refresh a TextView while looping in Android?
The Rackers's answer:
public class myClass extends Activity{
private Handler mHandler;
private TextView text;
private int i;
#Override
public void onCreate(Bundle savedInstanceState) {
text = (TextView) findViewById(R.id.textView01);
i = 0;
mHandler = new Handler();
mHandler.post(mUpdate);
}
private Runnable mUpdate = new Runnable() {
public void run() {
text.setText("My number: " + i);
i++;
mHandler.postDelayed(this, 1000);
}
};}
So, at my case I just call the others steps if the current is solved. If not, I just post the step again...
Your approach will not work.
When you call messenger.setText(), you think the screen is supposed to update right then. You are mistaken.
What really happens when you call messenger.setText() is that a message goes on a message queue, to be processed by the main application thread. That's the same thread you're tying up with your while() loop. While you are in that while() loop, your entire UI is frozen: no clicks, no updates, nothing.
I suggest that you get rid of the while() loop and move along with the rest of your app.