So I am trying to inflate a layout from a class but so far no luck at all ... The layout is a loading spinner I want the spinner to be inflated when the class is called because there is an Asynch task withing the class. I want the inflated layout to be removed when the Asynch task finishes. But so far I have nothing.
Here is my XML for the spinner
?xml version="1.0" encoding="utf-8"?>
<ProgressBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LoadingSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="?android:attr/progressBarStyleLarge"
android:layout_centerHorizontal="true">
</ProgressBar>
And my class
public class ClusterManager extends ClusterManager {
public ClusterManager(LayoutInflator inflator) {
super();
this.inflater = inflater;
}
private void reload() {
new AsyncTask<Void, Void, Void>()
{
#Override
protected Void doInBackground(Void... params)
{
//Start spinner here
}
#Override
protected void onPostExecute(Void aVoid)
{
//Stop/Remove the spinner
}
}.execute();
}
I have tried to inflate it like this.
View loadingView = inflator.inflate(R.layout.loading_spinner,null);
But no result the layout is not inflated nothing is shown on the screen. What am I doing wrong? Is this the right way to inflate it or there is something that I am missing?
Related
I'm trying to use a SwipeToRefreshLayout to update some information inside a marker. Although I use android:layout_width="wrap_content" to keep the content size, for some reason it always fills the parent, leaving a huge space below the information content. What am I doing wrong?
By the way, if I don't have the SwipeRefreshLayout , and use only the ScrollView, that empty space doesn't appear.
Here is my XML code:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/svMarker"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/svMarkerInfo">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/llMarkerInfo">
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
Thank you for your answers!
I leave also the image of how is being showed using the SwipeRefreshLayout. Behind the marker information there is a map:
If I dont use SwipeRefreshLayout, the marker information is shown the next way, which visually is what I am looking for. but I'm missing the SwipeToRefresh update:
The SwipeRefreshLayout will disabled while dragging a map by using a listener and the refresh will only take place if you swipe down on the second half ( i.e. the layout below map). To get map drag events, we need to do some workaround because there is no direct support from Google and this library MapStateListener has done that. Just paste these three classes MapStateListener.java, TouchableMapFragment.java, TouchableWrapper.java into your project.
Here is a working example.
MainActivity.java
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback
{
private SwipeRefreshLayout swipeRefreshLayout;
private View secondHalf;
private TouchableMapFragment mMap;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.svMarker);
secondHalf = findViewById(R.id.secondHalf);
setUpMap();
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
#Override
public void onRefresh()
{
// A thread to keep RefreshLayout busy for 5 seconds
if(!swipeRefreshLayout.isRefreshing())
new LoadImagesTask().execute();
}
});
}
public void setUpMap()
{
try
{
if (mMap == null)
{
mMap = ((TouchableMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.firstHalf));
mMap.getMapAsync(this);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
#Override
public void onMapReady(GoogleMap googleMap)
{
new MapStateListener(googleMap, mMap, this)
{
#Override
public void onMapTouched()
{
// Map touched
swipeRefreshLayout.setEnabled(false);
}
#Override
public void onMapReleased() {
// Map released
swipeRefreshLayout.setEnabled(true);
}
#Override
public void onMapUnsettled() {
// Map unsettled
}
#Override
public void onMapSettled() {
// Map settled
}
};
}
private class LoadImagesTask extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... voids)
{
try
{
for (int i = 1; i <= 5; i++)
{
Thread.sleep(1000);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid)
{
super.onPostExecute(aVoid);
swipeRefreshLayout.setRefreshing(false);
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout android:id="#+id/svMarker"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="2">
<fragment
android:id="#+id/firstHalf"
android:name="com.esealed.watermeterclient.TouchableMapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<ScrollView
android:id="#+id/secondHalf"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#color/colorAccent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Other views comes here -->
</LinearLayout>
</ScrollView>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
The only solution I could figure out was defining the SwipeRefreshLayout size dinamically. No need to change my XML file.
On my MapActivity.class, where I manage the marker's info, every time I show a new marker information, I call the next method:
LinearLayout llMarkerInfo = (LinearLayout) findViewById(R.id.llMarkerInfo);
SwipeRefreshLayout svMarker = (SwipeRefreshLayout) findViewById(R.id.svMarker);
//First i register to the listener so I can get the measured size of the marker information's layout
ViewTreeObserver vto = llMarkerInfo.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//I get the measured width and height of the marker information's layout
int width = llMarkerInfo.getMeasuredWidth();
int height = llMarkerInfo.getMeasuredHeight();
//Set that size to the SwipeToRefresh layout
LinearLayout.LayoutParams svParams = new LinearLayout.LayoutParams(width, height);
svMarker.setLayoutParams(svParams);
//Unregister the listener so is not called many times
if (Build.VERSION.SDK_INT < 16)
llMarkerInfo.getViewTreeObserver().removeGlobalOnLayoutListener(this);
else
llMarkerInfo.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
This way, I define exactly the size my marker information's layout has, to my SwipeRefreshLayout, dinamically.
Image with the final result (expected = reality):
I have trouble displaying the layout of my main activity :
I create an ActivityA with an ImageView.
In onCreate(), I launch an AsyncTask, which retrieves content from Internet, and opens an ActivityB.
When I launch my application, it displays ActivityB right away.
public class ActivityA extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyTask mytask = new MyTask();
mytask.execute();
}
}
My MainActivity xml file is the following :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/loadingPanel"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/home_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/ic_home_page" />
<ProgressBar
android:id="#+id/marker_progress"
style="?android:attr/progressBarStyle"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|center_horizontal"
android:indeterminate="true" />
</RelativeLayout>
The ProgressBar is used to show the loading process of the AsyncTask.
thanks for helping
public class ActivityA extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyTask mytask = new MyTask();
mytask.execute();
}
}
private class MyTask extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
// code for retrieve contents from Internet
}
#Override
protected void onPostExecute(String result) {
// cancel ProgressBar and start activity B here like
Intent i = new Intent(A.this, B.class);
startActivity(i);
}
#Override
protected void onPreExecute() {
// showing ProgressBar
}
}
don't forgot to mention A activity as a launcher.
I hope it will help you to solve this problem.
I've got a FragmentActivity in which I fire an AsyncTask that gathers a list of data. This activity has a ListFragment and I want to update the listview as the data keeps coming.
I have another fragment that should also get the data, so I implemented some quick version of observer pattern, so that they both get notified when an element has been added to the list:
private void addElement(MyListElement item) {
myList.add(item);
Collections.sort(myList, Collections.reverseOrder());
notifyObservers();
}
private void notifyObservers() {
for(IListObserver dObserver: observers){
dObserver.updateList(myList);
}
}
But my list is never updated (or the other fragment, for the matter).
Here is the ListFragment code:
public class MyListFragment extends SherlockListFragment implements IListObserver{
private TextView first;
private Context mContext;
private List<MyListElement> myList;
private MyListAdapter myListAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.mContext = getActivity().getApplicationContext();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_listview, container, false);
myList = new ArrayList<SizeDirectory>(0);
myListAdapter = new MyListAdapter(getActivity(), myList);
setListAdapter(myListAdapter);
myListAdapter.notifyDataSetChanged();
return view;
}
#Override
public void updateList(List<MyListElement> myList) {
this.myList = myList;
this.myListAdapter.notifyDataSetChanged();
getListView().requestLayout();
}
}
And this is the AsyncTask:
class ListLoader extends AsyncTask<String, MyListElement, Boolean> {
private String LOG_TAG = ListLoader .class.getSimpleName();
/**
* Maybe show loading indicator
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
}
protected Boolean doInBackground(String... args) {
MyListElement item = getListItem(...)
publishProgress(item );
return true;
}
/**
* After completing background task Dismiss the progress dialog
**/
protected void onPostExecute(Boolean result) {
if (result) {
//Everything was ok
}
}
#Override
protected void onProgressUpdate(MyListElement... values) {
super.onProgressUpdate(values);
addElement(values[0]);
}
}
The funny thing is, if I put this AsyncTask in my ListFragment, and call updateList from there, then I do get the list updated as the asynctask progresses.
I though maybe this was due to the activity Lifecycle calling the onCreateView of my Fragment AFTER creating activity (and thus resetting the list) but I checked with debugger and it's not the case. I am also assuming this is not related to not being able to update UI from another thread since I do it from onProgressUpdate plus I would've gotten an Exception (or wouldn't I?).
EDIT: layout_listview
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/firstTextView"
android:layout_width="fill_parent"
android:layout_height="129dp"
android:scrollbars="vertical"
android:text="#string/hello_world" />
<Button
android:id="#+itd/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Button" />
</RelativeLayout>
<ListView
android:id="#id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
After the updateList() call, your list adapter is still holding a reference to the original value of myList . Simply setting MyListFragment.myList won't update the adapter. You need a way of setting MyListAdapter's copy of the list. If this is one of the stock Android adapters you may need to create a new adapter for the new list and set it on the list fragment.
For a few days a have some problem.
I need to show simple ProgressBar (not dialog) while doing some stuff in main thread...
I thought its a very simple question, but i cant do this, help me please.
First i tried simple setVisibility(View.VISIBLE) before and setVisibility(View.GONE) after.
But this is doing in the same thread, and ProgressBar freezed while my function working.
Now i have this code, but i have some error, and i dont know whats wrong..
my simple layout :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="#+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
i have a base activity :
public class BaseActivity extends Activity {
public ProgressBar loading;
public class ProgressBarShow extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... unused) {
return(null);
}
protected void onProgressUpdate() {
}
protected void onPreExecute() {
loading.setVisibility(View.VISIBLE);
}
protected void onPostExecute() {
}
}
}
and finally my working activity , which extends BaseActivity
public class SearchActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loading = (ProgressBar) findViewById(R.id.loading);
new ProgressBarShow().execute();
//doing long stuff
//new ProgressBarHide().execute(); there isnt, but sense the same
}
}
I have many activities, which need progress bar, thats why i have created BaseActivity,
to not to dublicate code.
I need to do long work (stuff function) in main thread, because i want to freeze main window and not to allow user do anything (click button etc..), just show working ProgressBar.
Whats wrong in my example? Or give me some advice how can i do this better
class ProgressTask extends AsyncTask<Integer, Integer, Void>{
ProgressBar progress;
Context context;
public ProgressTask(ProgressBar progress, Context context) {
this.progress = progress;
this.context = context;
}
#Override
protected void onPreExecute() {
// initialize the progress bar
// set maximum progress to 100.
progress.setMax(100);
}
#Override
protected Void doInBackground(Integer... params) {
// get the initial starting value
int start=params[0];
// increment the progress
for(int i=start;i<=100;i+=5){
try {
boolean cancelled=isCancelled();
//if async task is not cancelled, update the progress
if(!cancelled){
publishProgress(i);
SystemClock.sleep(1000);
}
} catch (Exception e) {
Log.e("Error", e.toString());
}
}
return null;
}
//Has direct connection to UI Main thread
//Called everytime publishProgress(int) is called in doInBackground
#Override
protected void onProgressUpdate(Integer... values) {
progress.setProgress(values[0]);
Toast.makeText(context, "test"+values[0], Toast.LENGTH_SHORT).show();
}
#Override
protected void onPostExecute(Void result) {
// async task finished
Log.v("Progress", "Finished");
}
#Override
protected void onCancelled() {
progress.setMax(0);
}
}
Use AsyncTask http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html Put your ProgressBar in it, while all work is doing in main thread
In one of my app, I have a scenario where I need to do some background task. For doing that I am using Async Task. Also I am using custom progress dialog. Below is the layout of the custom progress dialog
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
android:orientation="vertical" >
<ProgressBar
android:layout_width="60dp"
android:layout_height="60dp"
android:indeterminateDrawable="#drawable/progressloader"
android:layout_gravity="center"/>
<TextView
android:id="#+id/progressMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:textSize="18sp"
android:text="Please wait...." />
</LinearLayout>
Everything works fine but when I try to set text to TextView then I am getting java NullPointerException.
AsyncTask code
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
#Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
}
#Override
protected Long doInBackground(String... urls) {
// txtView.setText("Testing"); here I am getting the error
fetchDetails();
return 0;
}
#Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
MainActivity
public class SummaryActivity extends Activity {
final TextView txtView = (TextView)findbyid(R.id.progressMessage);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
new InitialSetup().execute("");
}
}
If I understand correctly, your TextView of which you want to set the text can be found in the xml file progressbar.xml (i.e. R.layout.progressbar). This TextView can be obtained once the content view has been set (using setContentView()). In your code you set it before this call is been and the code of mussharapp, he is calling it to early. Namely, he calls it after the setContentView(R.layout.accountsummary) call which does not contain the TextView. Consequently, the variable txtView will be NULL and you will get a NullPointerException.
What you should do is the following:
Set the variable txtView in onPreExecute, after setContentView is called.
Based on Paresh Mayani's explanation: Use the runOnUiThread method.
For the code look down below:
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
#Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
#Override
protected Long doInBackground(String... urls) {
// Already suggested by Paresh Mayani:
// Use the runOnUiThread method.
// See his explanation.
runOnUiThread(new Runnable() {
#Override
public void run() {
txtView.setText("Testing");
}
});
fetchDetails();
return 0;
}
#Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
Yes, because you are trying to set the TextView inside the doInBackground() method, and this is not allowed,
Why not allowed? Because There is a only one Thread running which is UI Main Thread, and it doesn't allowed to update UI from thread process. read more info here: Painless Threading
So there is a solution if you want to set the TextView inside the doInBackground() method, do the UI updating operations inside the runOnUiThread method.
Otherwise, suggestion is to do all the UI display/update related operations inside the onPostExecute() method instead of doInBackground() method of your AsyncTask class.
(TextView)findViewByid(R.id.progressMessage);
should only be executed after the command setContentView().
TextView txtView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
**txtView = (TextView)findbyid(R.id.progressMessage);**
new InitialSetup().execute("");
}
Also you can only change UI elements in the main UI thread. doInBackground() is not in the main UI thread. Make UI changes in onPostExecute
public class InitialSetup extends AsyncTask<String, Integer, Long> {
private Activity activity;
ProgressDialog progressDialog;
public InitialSetup(Activity activity) {
this.activity = activity;
}
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Starting task....");
progressDialog.show();
}
#Override
protected Long doInBackground(String... urls) {
// do something
//
return 0;
}
#Override
protected void onPostExecute(Long result) {
progressDialog.dismiss();
//Perform all UI changes here
**textView.setText("Text#2");**
}
}
The explanations are correct: You are not to make UI changes in any thread except the thread which create the UI. But AsyncTask has a method called
onProgressUpdate()
which always will run in the UI Thread. So based on the modifications by dennisg your code should look like this:
private class InitialSetup extends AsyncTask<String, String, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
#Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
#Override
protected Long doInBackground(String... urls) {
publishProgress("Testing");
fetchDetails();
return 0;
}
#Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
#Override
protected void onProgressUpdate(String... update) {
if (update.length > 0)
txtView.setText(update[0]);
}
}
Note that the type of the parameter of onProgressUpdate is the second type given in AsyncTask!
Extra: To make your code more robust you should check if the progress dialog still exists before setting the text.