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):
Related
i used yandex mapkit try to get accurate latlng but it ploted accurately but not get listener failed i used the link https://github.com/yandexmobile/yandexmapkit-android/blob/master/yandexmapkit-sample/src/ru/mapkittest/geocode/OverlayGeoCode.java but listener not called how to get listener when each and every point move
Thanks,
Shabeer Mohamed
If you want to geocode a point on a map after you tap on it, you need to do the following.
Extend Overlay class and implement GeoCodeListener
public class GeoCodeOverlay extends Overlay implements GeoCodeListener {
public GeoCodeOverlay(MapController mapController) {
super(mapController);
}
#Override
public boolean onFinishGeoCode(final GeoCode geoCode) {
if (geoCode != null) {
getMapController().getMapView().post(new Runnable() {
#Override
public void run() {
// show display name of the point
Toast.makeText(getMapController().getContext(),
geoCode.getDisplayName(), Toast.LENGTH_LONG).show();
}
});
}
return true;
}
#Override
public boolean onSingleTapUp(float x, float y) {
getMapController().getDownloader()
.getGeoCode(this, getMapController().getGeoPoint(new ScreenPoint(x, y)));
return true;
}
}
Example of usage
public class GeoCoderActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_geo_coder);
final MapView mapView = (MapView) findViewById(R.id.map);
mapView.getMapController().getOverlayManager()
.getMyLocation().setEnabled(true);
mapView.getMapController().getOverlayManager()
.addOverlay(new GeoCodeOverlay(mapView.getMapController()));
}
}
activity_geo_coder.xml. Don't forget to add your API key
<?xml version="1.0" encoding="utf-8"?>
<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: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="com.mapkittest.GeoCoderActivity">
<ru.yandex.yandexmapkit.MapView
android:id="#+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="PLACE_YOUR_API_HERE"
/>
</RelativeLayout>
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.
I have a Google Maps mapView in a ScrollView. it works fine, except when Scrolling I get some ugly black backgrounds along the bottom and top.
Here's a video of the issue:
https://www.youtube.com/watch?v=wLla7nS6Ehc&feature=youtu.be
To fix this, I wrapped the Map in a FrameLayout and made it bigger than the actual container, this fixes the issue, except now I can't see the Google watermark anymore. I tried using a bottom padding, but with the padding the black pattern shows up again, but this time only at the bottom.
Is it okay to put a TextView in the FrameLayout with the font and style of the Google Maps watermark as a work around? Am I violating the TOS if I do this?
This is the xml for my fix:
<FrameLayout
android:id="#+id/map_view_container"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_below="#+id/location"
android:layout_centerHorizontal="true"
android:layout_marginTop="#dimen/full_margin"
android:background="#color/transparent">
<com.google.android.gms.maps.MapView
android:id="#+id/map_view"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center"
android:background="#color/off_white"
map:mapType="normal" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparent" />
</FrameLayout>
EDIT: Fragment code:
public class MapFragment extends Fragment
implements View.OnClickListener,
com.google.android.gms.maps.OnMapReadyCallback {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_business_home, container, false);
final Bundle mapViewSavedInstanceState = savedInstanceState != null ? savedInstanceState.getBundle("mapViewSaveState") : null;
initializeViewComponents(rootView, mapViewSavedInstanceState);
return rootView;
}
private void initializeViewComponents(View rootView, Bundle savedInstanceState) {
scrollView = (ScrollView) rootView.findViewById(R.id.scroll_view);
mapView = (MapView) rootView.findViewById(R.id.map_view);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
MapsInitializer.initialize(this.getActivity());
this.map = googleMap;
// Gets to GoogleMap from the MapView and does initialization stuff
map.getUiSettings().setMyLocationButtonEnabled(true);
map.getUiSettings().setAllGesturesEnabled(false);
map.setMyLocationEnabled(true);
onAsyncLoad();
}
#Override
public void onResume() {
if (mapView != null) {
mapView.onResume();
}
super.onResume();
}
#Override
public void onLowMemory() {
if (mapView != null) {
mapView.onLowMemory();
}
super.onLowMemory();
}
public void onSaveInstanceState(Bundle outState) {
Log.i(TAG, "SAVING TO INSTANCE STATE");
//This MUST be done before saving any base class variables
final Bundle mapViewSaveState = new Bundle(outState);
if (mapView != null) { // May be null after onDestroView
mapView.onSaveInstanceState(mapViewSaveState);
outState.putBundle("mapViewSaveState", mapViewSaveState);
}
}
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>
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.