Fragment list not getting update - android

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.

Related

SwipeRefreshLayout is filling parent in Android

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):

Zbar add floating View

Ok, so, i want to make something like this:
http://postimg.org/image/qs3okxitf/
Now, im using zbarscannerview like this:
public class BarKodScreen extends AppCompatActivity implements ZBarScannerView.ResultHandler {
private ZBarScannerView mView;
private BarcodeFormat barcodeFormatEAN13, barcodeFormatEAN8;
private List<BarcodeFormat> listaZaFormat = new ArrayList<BarcodeFormat>();
private ImageView img;
private LinearLayout lejout;
private View kamera;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barkod);
mView = new ZBarScannerView(this);
lejout = (LinearLayout) findViewById(R.id.cameraPreview);
img = (ImageView) findViewById(R.id.cameraImageView);
kamera = (View) findViewById(R.id.zaKameru);
kamera = mView;
lejout.addView(kamera);
lejout.addView(kamera);
lejout.removeView(img);
lejout.addView(img);
barcodeFormatEAN13 = BarcodeFormat.EAN13;
barcodeFormatEAN8 = BarcodeFormat.EAN8;
listaZaFormat.add(barcodeFormatEAN13);
listaZaFormat.add(barcodeFormatEAN8);
mView.setFormats(listaZaFormat);
}
#Override
public void onResume() {
super.onResume();
mView.setResultHandler(this); // Register ourselves as a handler for scan results.
mView.startCamera(); // Start camera on resume
}
#Override
public void onPause() {
super.onPause();
mView.stopCamera(); // Stop camera on pause
}
#Override
public void handleResult(Result rawResult) {
// Do something with the result here
Log.v("GetCOntent", rawResult.getContents()); // Prints scan results
barKodZahtev(rawResult.getContents());
}
}
and my xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="#+id/zaKameru"
android:layout_width="match_parent"
android:layout_height="150dp"/>
<ImageView
android:id="#+id/cameraImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.6"
android:src="#drawable/share" />
</LinearLayout>
What I dont understand is how to add the image(text from the screenshot) as a child view for my zbarscannerView.
Check out their rapository on github, they have something there.

How to inflate a layout from a class

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?

How do you keep track of dependant asynchronous tasks?

I'm making an Android Application at the moment using MVC. I'm using an Activity as a Controller and a different class as the View.
The View is waiting for two asynchronous tasks, a Google Map and a task sent to fetch data from a database. The View needs the data from the database to place a marker on the map. If the map loads first the we can't place the marker. If the database task finishes then we needs to wait for the map to load.
How do I check that the dependant tasks are finished?
Should I just have a flag to say if the db task is finished and then when the map loads check this to continue and vice versa for the map.
Or is there a better way to do all this.
This is a simplified version of the View:
public class SellerAddView
implements OnChangeListener<Model>, OnMapReadyCallback{
...
public SellerAddView(View view, Model model, Activity activity){
model.addListener(this);
mapFragment = ((MapFragment)activity.getFragmentManager()
.findFragmentById(R.id.add_map));
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
LatLng coords = model.getLatLng();
map.addMarker(new MarkerOptions().position(coords));
}
#Override
public void onChange(SellerAddModel model) {
updateView();
}
}
Here is the simplified Controller:
public class Controller extends Activity{
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View screenView = View.inflate(this, activity_seller_add, null);
model = new Model();
view = new view(screenView, model, this);
populateModel();
setContentView(screenView);
}
public void populateModel(){
handler.post(new Runnable() {
#Override
public void run() {
synchronized (model) {
Model newModel = new ModelDao().getId(id);
model.consume(model);
}
}
});
}
}
So you can use the Splash screen to wait for all data to be loaded, that will be better.
Sample code as following:
public class SplashScreen extends Activity {
private static int SPLASH_DELEY = 3000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(SplashScreen.this, MainActivity.class);
startActivity(intent);
finish();
}
}, SPLASH_DELEY);
}
}
And for activity_splash_screen.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="com.bjiang.map_ex.SplashScreen">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageLogo"
android:layout_centerInParent="true"
android:src="#drawable/splash_file"/>
</RelativeLayout>

ListView doesn't show progressbar when the data loaded

When I start activity with ListView it start to load some information from the Internet and after apply adapter, but during loading ListVIew doesn't show me a ProgressBar ring. When I trying to use SherlockListActivity instead ListView I have same problem.
<?xml version="1.0" encoding="utf-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/comments"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
public class CommentsActivity extends SherlockActivity{
#InjectView(R.id.comments) ListView mCommentsListView;
private String mPostId;
private CommentsAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.comment_activivty);
ButterKnife.inject(this);
Intent intent = getIntent();
mPostId = intent.getStringExtra("POST_ID");
LC lc = new LC();
lc.execute(mPostId);
}
public void setupAdapter(){
mAdapter = new CommentsAdapter(this);
mCommentsListView.setAdapter(mAdapter);
}
private class LC extends LoadComments{
#Override
protected void onPostExecute(Void result) {
//setup adapter after loading comments
setupAdapter();
}
}
}
Add ProgressBar to your layout file:
<ProgressBar
android:id="#+id/progress_bar"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Inject the ProgressBar in your activity:
#InjectView(R.id.progress_bar) ProgressBar mProgressBar;
Now you have to hide it when the adapter is set:
public void setupAdapter(){
mAdapter = new CommentsAdapter(this);
mCommentsListView.setAdapter(mAdapter);
mProgressBar.setVisibility(View.GONE);
}
Do it like this,
#Override
protected Void OnPreExecute(){
ProgressDialog prgDialog = new ProgressDialog(getContext());
prgDialog.setTitle("Loading");
prgDialog.setMessage("please wait")
prgDialog.setCancelable(false);
prgDialog.show();
}
on post Execute
private class LC extends LoadComments{
#Override
protected void onPostExecute(Void result) {
//setup adapter after loading comments
prgDialog.dismiss();
setupAdapter();
}
}

Categories

Resources