it's my first question, so sorry for bad formatting or anything like this.
I'm trying to add info windows to markers, which are clustered by ClusterManager from google utils, but my item does not get rendered by my custom InfoWindowAdapter and that's what i'm trying to do.
After onMapReady callback, i initialize cluster manager like this:
mClusterManager = new ClusterManager<>(this, mMap);
mClusterManager.setRenderer(new ClusterMarkerRenderer(this, mMap, mClusterManager));
mClusterManager.setOnClusterClickListener(cluster -> {
LatLngBounds.Builder builder = LatLngBounds.builder();
for (CarMapItem item : cluster.getItems()) {
builder.include(item.getPosition());
}
final LatLngBounds bounds = builder.build();
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
return true;
});
mMap.setInfoWindowAdapter(new InfoWindowCarAdapter(this));
ClusterMarkerRenderer Class (only important parts):
public class ClusterMarkerRenderer extends DefaultClusterRenderer<CarMapItem> {
public ClusterMarkerRenderer(Context context, GoogleMap map, ClusterManager<CarMapItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(CarMapItem item, #NotNull MarkerOptions markerOptions) {
if (item.getIcon() != null) {
markerOptions.icon(item.getIcon());
}
if (!item.hasCustomBitmap()) {
markerOptions.rotation(item.getRotation());
}
markerOptions.title(item.getName());
markerOptions.anchor(0.5f, 0.5f);
markerOptions.flat(true);
markerOptions.infoWindowAnchor(0.5f, 0.5f);
markerOptions.snippet(item.getImei() + "#" + item.getPosition().latitude
+ "#" + item.getPosition().longitude + "#" + item.getRotation() + "#" + item.getIconName() + "#" + item.getName());
super.onBeforeClusterItemRendered(item, markerOptions);
}
#Override
protected void onClusterItemRendered(#NotNull CarMapItem item, #NotNull Marker marker) {
super.onClusterItemRendered(item, marker);
marker.setTag(item);
}
}
This is where i set marker options, before cluster item gets rendered, my icon is set and everything is fine, but after rendering, i just set my item as tag to retrieve information for drawing and it won't render anything after this. And there is my last class, InfoWindowCarAdapter:
public class InfoWindowCarAdapter implements GoogleMap.InfoWindowAdapter {
private View myContentsView;
private Activity context;
#SuppressLint("InflateParams")
public InfoWindowCarAdapter(Activity context) {
this.context = context;
myContentsView = context.getLayoutInflater().inflate(R.layout.item_car_info, null);
}
#Override
public View getInfoContents(Marker marker) {
Log.e(TAG, "GET INFO CONTENTS FROM MARKER");
CarMapItem object = (CarMapItem) marker.getTag();
if (object != null) {
TextView tvTitle = myContentsView.findViewById(R.id.txt_car_name);
if (object.getItemType() == 1) {
((TextView) myContentsView.findViewById(R.id.txt_adit_info)).setText(String.format(Locale.getDefault(), "%s", Helper.getFormatedAlarmWithVars(context, object.getSpeed(),
object.getIconName(), object.getVar2())));
(myContentsView.findViewById(R.id.txt_status)).setVisibility(View.GONE);
(myContentsView.findViewById(R.id.ic_status_icon)).setVisibility(View.GONE);
} else {
TextView title = myContentsView.findViewById(R.id.txt_status);
ImageView icon = myContentsView.findViewById(R.id.ic_status_icon);
((TextView) myContentsView.findViewById(R.id.txt_adit_info)).setText(String.format(Locale.getDefault(), "%s", "Click for more info"));
if (object.getCarEngineStatus() == 1) {
title.setText(String.format(Locale.getDefault(), "%d KM/h", object.getSpeed()));
} else {
title.setText(Helper.formatSecondPrecise(context, object.getLastActivityTime()));
}
icon.setImageResource(Helper.getIconResIdByCarStatus(object.getVehicleStatus()));
(myContentsView.findViewById(R.id.txt_status)).setVisibility(View.VISIBLE);
(myContentsView.findViewById(R.id.ic_status_icon)).setVisibility(View.VISIBLE);
}
tvTitle.setText(object.getName());
return myContentsView;
}
return myContentsView;
}
#Override
public View getInfoWindow(Marker marker) {
return null;
}
}
When this class gets called, get info contents never does neither get info window, even tho i tried to log messages, still no output from this class, only from constructor.
Can u help me?
If anyone needs, i fixed it myself, and here is the mistake: In my activity, where i set InfoWindowAdapter to google maps object, i had to set ClusterManager's MarkerManager to Google Maps object and then set my custom info window adapter to cluster manager's marker collection just like this:
mMap.setInfoWindowAdapter(mClusterManager.getMarkerManager());
mClusterManager.getMarkerCollection().setInfoWindowAdapter(new InfoWindowCarAdapter(this));
if anybody needs this, it's here :)
Related
I'm desperately trying to simulate a click on a spinner item with Espresso.
The spinner is populated with objects from the class Project. This class has a toString() method, which allows the spinner to display String.
private void populateDialogSpinner() {
final ArrayAdapter<Project> adapter2 = new ArrayAdapter<Project>(this, android.R.layout.simple_spinner_item, allProjects);
adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
vm.getAllProjects().observe(this, projects -> {
allProjects.clear();
allProjects.addAll(projects);
adapter2.notifyDataSetChanged();
});
I have found several solutions in order to use this spinner with Espresso but none of them has worked. This is my Test class.
#Test
public void addActivity () throws InterruptedException {
onView(withId(R.id.fab_add_task)).perform(click());
onView(withId(R.id.txt_task_name)).perform(replaceText(textTaskText), closeSoftKeyboard());
onView(withId(R.id.project_spinner)).perform(click());
// A few solutions I have tried :
// 1 onView(withText("Projet Lucidia")).perform(click());
// 2 onData(anything())
// .inAdapterView(withId(R.id.project_spinner))
// .onChildView(withMyValue("Projet L"))
// .perform(click());
// 3 onData(anything()).atPosition(1).perform(click());
// 4 onView(allOf(withId(R.id.project_spinner), withSpinnerText("Project Lucidia"))).perform(click());
withMyValue refers to this class
public static Matcher<View> childAtPosition(final Matcher<View> parentMatcher, final int position) {
return new TypeSafeMatcher<View>() {
#Override
public void describeTo(Description description) {
description.appendText("Child at position " + position + " in parent ");
parentMatcher.describeTo(description);
}
#Override
public boolean matchesSafely(View view) {
ViewParent parent = view.getParent();
return parent instanceof ViewGroup && parentMatcher.matches(parent) && view.equals(((ViewGroup) parent).getChildAt(position));
}
};
}
Each time, the emulator stays with the spinner open like this :
Any idea of how I can handle this?
I'm not sure if it's the best answer but I have found this code thats works perfectly well.
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
UiObject spinnerItem = uiDevice.findObject(new UiSelector().text("Projet Lucidia"));
spinnerItem.click();
please I need your help after searching a lot without issues.
I have an demostration app to use an second screen attached to my device.
I have the source code of the app, they use the Mediarouter class and an class named LauncherSecondScreen extended from the Presentation class
I have tried to make the app as an service to keep runnig the app in background, but the mediarouter callback seems running only on the princpal thread ( I'm not sure I am just a beginner in android dev).
I have the full code of the app : there is two layout activity one showed on the princpal screen and the other on the second screen:
public class MainActivity extends Activity {
private final String TAG = "PresentationWithMediaRouterActivity";
private MediaRouter mMediaRouter;
private LauncherSecondScreen mPresentation;
private boolean mPaused;
/**
* Initialization of the Activity after it is first created. Must at least
* call {#link android.app.Activity#setContentView setContentView()} to
* describe what is to be displayed in the screen.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
// Be sure to call the super class.
super.onCreate(savedInstanceState);
// Get the media router service.
mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE);
// See assets/res/any/layout/presentation_with_media_router_activity.xml for this
// view layout definition, which is being set here as
// the content of our screen.
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
// Be sure to call the super class.
super.onResume();
// Listen for changes to media routes.
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);
// Update the presentation based on the currently selected route.
mPaused = false;
updatePresentation();
}
private void updatePresentation() {
// Get the current route and its presentation display.
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
Display presentationDisplay = route != null ? route.getPresentationDisplay() : null;
// Dismiss the current presentation if the display has changed.
if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
Log.i(TAG, "Dismissing presentation because the current route no longer "
+ "has a presentation display.");
mPresentation.dismiss();
mPresentation = null;
}
// Show a new presentation if needed.
if (mPresentation == null && presentationDisplay != null) {
Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
mPresentation = new LauncherSecondScreen(this, presentationDisplay);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Log.w(TAG, "Couldn't show presentation! Display was removed in "
+ "the meantime.", ex);
mPresentation = null;
}
}
// Update the contents playing in this activity.
updateContents();
}
private void updateContents() {
// Show either the content in the main activity or the content in the presentation
// along with some descriptive text about what is happening.
if (mPresentation != null) {
if (mPaused) {
mPresentation.dismiss();//getSurfaceView().onPause();
} else {
mPresentation.show();//getSurfaceView().onResume();
}
} else {
/* mInfoTextView.setText("presentation_with_media_router_now_playing_locally");
mSurfaceView.setVisibility(View.VISIBLE);
if (mPaused) {
mSurfaceView.onPause();
} else {
mSurfaceView.onResume();
}*/
}
}
private final MediaRouter.SimpleCallback mMediaRouterCallback =
new MediaRouter.SimpleCallback() {
#Override
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
updatePresentation();
}
};
/**
* Listens for when presentations are dismissed.
*/
private final DialogInterface.OnDismissListener mOnDismissListener =
new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
if (dialog == mPresentation) {
Log.i(TAG, "Presentation was dismissed.");
mPresentation = null;
updateContents();
}
}
};
#SuppressLint({"NewApi"})
public class LauncherSecondScreen extends Presentation
{
public LauncherSecondScreen(Context paramContext, Display paramDisplay)
{
super(paramContext, paramDisplay/*,android.R.style.Theme_Holo_Light_Dialog_NoActionBar*/);
}
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(R.layout.dialog_second_screen_content);
//// this.iv_secondScreen_banner = ((ImageView)findViewById(R.id.titleImage));
}
}
}
the app is well, it make one view in the princpale screen and a second view in the second screen , but when i resume the app to background the second screen take the same view of the first screen.
I want to keep the second view showing in the second screen even i resume the app to use another app
I have fragment from which I'm launching activity with shared element transition that has viewpager in it, the enter transition works fine but when i scroll in view pager and finish transition the shared image comes from left side which is not desired it should reposition itself to where it was launched, here is my code:
Intent myIntent = new Intent(getActivity(), EnlargeActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(getActivity(),
imageView,
ViewCompat.getTransitionName(imageView));
startActivity(myIntent, options.toBundle());
I'm updating view and its name in activity that contains viewpager when finishing activity, but its going with blink:
public void finishAfterTransition() {
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// Clear all current shared views and names
names.clear();
sharedElements.clear();
ViewGroup viewGroup = (ViewGroup) viewPagerDetail.getAdapter()
.instantiateItem(viewPagerDetail, viewPagerDetail.getCurrentItem());
if (viewGroup == null) {
return;
}
// Map the first shared element name to the child ImageView.
sharedElements.put(viewGroup.findViewById(R.id.img).getTransitionName(), viewGroup.findViewById(R.id.img));
// setExitSharedElementCallback((SharedElementCallback) this);
}
});
super.finishAfterTransition();
Basically, Android start the transition with your pre-defined View and transitionName and automatically use the same properties for the return transition. When you change your focused View in ViewPager, Android doesn't know about that and keep the transition on the previous one on its way back. So you need to inform Android about the changes:
Remap the transition properties: Use setEnterSharedElementCallback to change the transitionName and View to the new one before returning from Activity2.
Wait for the Activity1 to finish rendering addOnPreDrawListener.
It's a bit complex in the final implementation. But you can look at my sample code https://github.com/tamhuynhit/PhotoGallery. I try to implement the shared-element-transition from many simple to complex sections.
Your problem appeared from Level 3 and solved in Level 4.
I am writing a tutorial about this but it's not in English so hope the code can help
UPDATE 1: Work flow
Here is how I implement it in my code:
Override finishAfterTransition in Activity2 and call setEnterSharedElementCallback method to re-map the current selected item in ViewPager. Also, call setResult to pass the new selected index back to previous activity here.
#Override
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void finishAfterTransition() {
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
View selectedView = getSelectedView();
if (selectedView == null)
return;
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(selectedView);
names.add(transitionName);
sharedElements.put(transitionName, selectedView);
setExitSharedElementCallback((SharedElementCallback) null);
}
});
Intent intent = new Intent();
intent.putExtra(PHOTO_FOCUSED_INDEX, mCurrentIndex);
setResult(RESULT_PHOTO_CLOSED, intent);
super.finishAfterTransition();
}
Write a custom ShareElementCallback so I can set the callback before knowing which View is going to be used.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class CustomSharedElementCallback extends SharedElementCallback {
private View mView;
/**
* Set the transtion View to the callback, this should be called before starting the transition so the View is not null
*/
public void setView(View view) {
mView = view;
}
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(mView);
names.add(transitionName);
sharedElements.put(transitionName, mView);
}
}
Override onActivityReenter in Activity1, get the selected index from the result Intent. Set setExitSharedElementCallback to re-map new selected View when the transition begins.Call supportPostponeEnterTransition to delay a bit because your new View may not be rendered at this point. Use getViewTreeObserver().addOnPreDrawListener to listen for the layout changes, find the right View by the selected index and continue the transition supportStartPostponedEnterTransition.
#Override
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityReenter(int resultCode, Intent data) {
if (resultCode != LevelFourFullPhotoActivity.RESULT_PHOTO_CLOSED || data == null)
return;
final int selectedIndex = data.getIntExtra(LevelFourFullPhotoActivity.PHOTO_FOCUSED_INDEX, -1);
if (selectedIndex == -1)
return;
// Scroll to the new selected view in case it's not currently visible on the screen
mPhotoList.scrollToPosition(selectedIndex);
final CustomSharedElementCallback callback = new CustomSharedElementCallback();
getActivity().setExitSharedElementCallback(callback);
// Listen for the transition end and clear all registered callback
getActivity().getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {}
#Override
public void onTransitionPause(Transition transition) {}
#Override
public void onTransitionResume(Transition transition) {}
#Override
public void onTransitionEnd(Transition transition) {
removeCallback();
}
#Override
public void onTransitionCancel(Transition transition) {
removeCallback();
}
private void removeCallback() {
if (getActivity() != null) {
getActivity().getWindow().getSharedElementExitTransition().removeListener(this);
getActivity().setExitSharedElementCallback((SharedElementCallback) null);
}
}
});
// Pause transition until the selected view is fully drawn
getActivity().supportPostponeEnterTransition();
// Listen for the RecyclerView pre draw to make sure the selected view is visible,
// and findViewHolderForAdapterPosition will return a non null ViewHolder
mPhotoList.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mPhotoList.getViewTreeObserver().removeOnPreDrawListener(this);
RecyclerView.ViewHolder holder = mPhotoList.findViewHolderForAdapterPosition(selectedIndex);
if (holder instanceof ViewHolder) {
callback.setView(((ViewHolder) holder).mPhotoImg);
}
// Continue the transition
getActivity().supportStartPostponedEnterTransition();
return true;
}
});
}
UPDATE 2: getSelectedItem
To get selected View from the ViewPager, don't use getChildAt or you get the wrong View, use findViewWithTag instead
In the PagerAdapter.instantiateItem, use position as tag for each View:
#Override
public View instantiateItem(ViewGroup container, int position) {
// Create the View
view.setTag(position)
// ...
}
Listen to onPageSelected event to get the selected index:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mSelectedIndex = position;
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Call getSelectedView to get the current view by the selected index
private View getSelectedView() {
try {
return mPhotoViewPager.findViewWithTag(mSelectedIndex);
} catch (IndexOutOfBoundsException | NullPointerException ex) {
return null;
}
}
This is actually a default behavior, I was struggling SharedElementTransitions a lot, but I have nested fragments. I got my solution from an article (very recent article), it shows an implementation with a RecyclerView, which I assume you have. In short, the solution is to override onLayoutChange :
recyclerView.addOnLayoutChangeListener(
new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view,
int left,
int top,
int right,
int bottom,
int oldLeft,
int oldTop,
int oldRight,
int oldBottom) {
recyclerView.removeOnLayoutChangeListener(this);
final RecyclerView.LayoutManager layoutManager =
recyclerView.getLayoutManager();
View viewAtPosition =
layoutManager.findViewByPosition(MainActivity.currentPosition);
// Scroll to position if the view for the current position is null (not
// currently part of layout manager children), or it's not completely
// visible.
if (viewAtPosition == null
|| layoutManager.isViewPartiallyVisible(viewAtPosition, false, true)){
recyclerView.post(()
-> layoutManager.scrollToPosition(MainActivity.currentPosition));
}
}
});
Here is the article, and you will also find the project on GitHub.
Apparently, I am developing an Indoor Positioning System and I want to resize an image which is being fetched from the server using picasso library, however I am getting a null pointer exception error which says: Attempt to read from field 'double com.indooratlas.android.sdk.resources.IALatLng.latitude' on a null object reference.
I know why I am getting this null pointer exception, however I do not see any other approach to resize the image.
Here is my ImageViewActivity named Extra_Activity which does not contain full code and only contains related code snippet:
public class Extra_activity extends FragmentActivity
{
private static final float dotRadius = 1.0f;
private static final int MAX_DIMENSION = 2048;
private IALocationManager mIALocationManager;
private IAResourceManager mResourceManager;
private IATask<IAFloorPlan> mPendingAsyncResult;
//private IAFloorPlan mFloorPlan;
private BlueDotView mImageView;
private Target mLoadTarget;
private static final String TAG ="FloorPlanLoader";
private IALatLng latLng;
private IALocationListener mIALocationListener = new IALocationListener()
{
#Override
public void onLocationChanged(IALocation location) {
Log.d(TAG, "location is: " + location.getLatitude() + "," + location.getLongitude());
if (mImageView != null && mImageView.isReady()) {
latLng = new IALatLng(location.getLatitude(), location.getLongitude());
//PointF point = mFloorPlan.coordinateToPoint(latLng);
//mImageView.setDotCenter(point);
//mImageView.postInvalidate();
}
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
};
private IARegion.Listener mRegionListener = new IARegion.Listener()
{
#Override
public void onEnterRegion(IARegion region) {
if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
String id = region.getId();
Log.d(TAG, "floorPlan changed to " + id);
fetchFloorPlan(id);
}
}
#Override
public void onExitRegion(IARegion region) {
// leaving a previously entered region
}
};
private void showFloorPlanImage(final IAFloorPlan floorPlan) {
final String filePath = floorPlan.getUrl();
if (mLoadTarget == null)
{
mLoadTarget = new Target()
{
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
{
Log.d(TAG, "onBitmap loaded with dimensions: " + bitmap.getWidth() + "x"
+ bitmap.getHeight());
mImageView.setImage(ImageSource.bitmap(bitmap.copy(bitmap.getConfig(), true)));
mImageView.setRadius(floorPlan.getMetersToPixels() * dotRadius);
PointF point = floorPlan.coordinateToPoint(latLng);
mImageView.setDotCenter(point);
mImageView.postInvalidate();
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
#Override
public void onBitmapFailed(Drawable placeHolderDrawable) {
Toast.makeText(Extra_activity.this, "Failed to load bitmap",
Toast.LENGTH_SHORT).show();
}
};
}
RequestCreator request = Picasso.with(this).load(filePath).rotate(90); //.resize(0,0)
final int bitmapWidth = floorPlan.getBitmapWidth();
final int bitmapHeight = floorPlan.getBitmapHeight();
if (bitmapHeight > MAX_DIMENSION) {
request.resize(0, MAX_DIMENSION);
} else if (bitmapWidth > MAX_DIMENSION) {
request.resize(MAX_DIMENSION, 0);
}
request.into(mLoadTarget);
Log.w(TAG, "showFloorPlanImage: " + filePath);
progressDialog.dismiss();
}
private void fetchFloorPlan(String id) {
cancelPendingNetworkCalls();
final IATask<IAFloorPlan> asyncResult = mResourceManager.fetchFloorPlanWithId(id);
mPendingAsyncResult = asyncResult;
if (mPendingAsyncResult != null) {
mPendingAsyncResult.setCallback(new IAResultCallback<IAFloorPlan>() {
#Override
public void onResult(IAResult<IAFloorPlan> result) {
Log.d(TAG, "fetch floor plan result:" + result);
if (result.isSuccess() && result.getResult() != null)
{
//mFloorPlan = result.getResult();
showFloorPlanImage(result.getResult());
} else {
if (!asyncResult.isCancelled()) {
Toast.makeText(Extra_activity.this,
(result.getError() != null
? "error loading floor plan: " + result.getError()
: "access to floor plan denied"), Toast.LENGTH_LONG)
.show();
}
}
}
}, Looper.getMainLooper());
}
}
private void cancelPendingNetworkCalls() {
if (mPendingAsyncResult != null && !mPendingAsyncResult.isCancelled()) {
mPendingAsyncResult.cancel();
}
}
}
The error message from logcat:
The (Extra_activity.java:154) refers to PointF point = floorPlan.coordinateToPoint(latLng); in the showFloorPlanImage method. The PointF point = floorPlan.coordinateToPoint(latLng); should be placed in the onLocationChanged method however, in this code snippet I can't use PointF point = floorPlan.coordinateToPoint(latLng); inside that method as variable floorplan can only be accessed inside showFloorPlanImage method. And this whole leads to null pointer exception.
I have asked a similar question to this one where I used a different approach (different code), however, I was able to resize the image but the blue dot was getting displayed out of sight: "IndoorAtlas SDK 2.0: Using Picasso with custom ImageView"
What is the correct solution as none of my approaches seems to work? Many thanks in advance if you can help me solve this problem.
I don't really understand the relation between your Nullpointer and the image resizing. However what you want to achieve here is only resizing an image if I understand it right. Instead of trying to resize the image itself, why don't you resize its container instead? (here the ImageView). You can achieve that with getLayoutParams().height and getLayoutParams().width or in xml like in this example:
<ImageView
android:id="#+id/image"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="#drawable/image" />
It's hard to tell from incomplete code snippets, but to me it looks like your error is unrelated to either Picasso or IndoorAtlas. You seem to have used our example code as a template, but made latLng an instance variable.
As a consequence, your onBitmapLoaded() where you get the NPE can be called (and apparently is called) before latLng is initialized. The latter happens asynchronously at the onLocationChanged() callback (and in this case, after you needed it). You can't depend on a particular calling order when working with asynchronous callback constructs.
Best of luck in your development!
This question has been answered before, but the solutions doesn't seem to work for me. I would like to know what the best way is to save an ArrayList.
I generate an ArrayList with all the installed applications on the phone. This list is shown in a ListView where the user can (de)select apps. This is all working fine. What I would like is that the Arraylist gets saved when the user presses a save button or when the activity calls onPause().
When the user returns to the list the user will see the list the way he saved/left it.
Here is my code:
onCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);
loadApps();
loadListView();
addClickListener();
}
loadApps
private void loadApps(){
manager = getPackageManager();
apps = new ArrayList<AppDetail>();
if(apps.size()==0) {
Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> availableActivities = manager.queryIntentActivities(i, 0);
for (ResolveInfo ri : availableActivities) {
AppDetail app = new AppDetail();
app.label = ri.loadLabel(manager);
app.name = ri.activityInfo.packageName;
app.icon = ri.activityInfo.loadIcon(manager);
app.allowed = false;
apps.add(app);
}
Log.i("applist", apps.toString());
}
}
AppDetail.class
public class AppDetail {
CharSequence label;
CharSequence name;
Drawable icon;
Boolean allowed;
loadListView
private void loadListView(){
list = (ListView)findViewById(R.id.apps_list);
adapter = new ArrayAdapter<AppDetail>(this, R.layout.list_item, apps) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = getLayoutInflater().inflate(R.layout.list_item, null);
}
ImageView appIcon = (ImageView)convertView.findViewById(R.id.item_app_icon);
appIcon.setImageDrawable(apps.get(position).icon);
TextView appLabel = (TextView)convertView.findViewById(R.id.item_app_label);
appLabel.setText(apps.get(position).label);
TextView appName = (TextView)convertView.findViewById(R.id.item_app_name);
appName.setText(apps.get(position).name);
if(list.isItemChecked(position)){convertView.setBackgroundColor(getResources().getColor(R.color.green));}
if(!list.isItemChecked(position)){convertView.setBackgroundColor(getResources().getColor(R.color.white));}
return convertView;
}
};
list.setAdapter(adapter);
list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
addClickListener
private void addClickListener() {
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int pos,
long id) {
checked = list.getCheckedItemPositions();
ArrayList<AppDetail> allowedApps = new ArrayList<>();
for (int i = 0; i < checked.size(); i++) {
// Item position in adapter
int position = checked.keyAt(i);
// Add sport if it is checked i.e.) == TRUE!
if (checked.valueAt(i)) {
allowedApps.add(adapter.getItem(position));
}
}
adapter.notifyDataSetChanged();
Log.i("", allowedApps.toString());
}
});
}
At this moment I'm creating two lists:
List: list of all apps
AllowedApps: list of checked (allowed) apps, to use in an other activity
If you need saving your list when activity is paused, you have several ways to do it. First you need define the private list field in your activity.
private ArrayList<AppDetail> allowedApps;
1) Make AppDetail serializable and use onSaveInstanceState
public class AppDetail implements Serializable {
CharSequence label;
CharSequence name;
Drawable icon;
Boolean allowed;
}
---------------- EDIT -----------------
I would change Drawable icon field for int icon.
In your loadApps() method change the setence app.icon = ri.activityInfo.getIconResource();
In yout loadListView method change the setence appIcon.setImageResource(apps.get(position).icon);
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("allowedApps", allowedApps);
}
Retrieve the list in onCreate method
if (savedInstanceState != null) {
allowedApps = (List<AppDetail>)savedInstanceState.getSerializable("allowedApps");
}else{
allowedApps = new ArrayList<AppDetail>();
}
2) Use onRetainCustomNonConfigurationInstance
Return the list in onRetainCustomNonConfigurationInstance
#Override
public Object onRetainCustomNonConfigurationInstance() {
return allowedApps;
}
Retrieve the list in onCreate method
Object allowedApps= getLastCustomNonConfigurationInstance();
if (allowedApps != null) {
this.allowedApps = (List<AppDetail>) allowedApps;
}else{
this.allowedApps = new ArrayList<AppDetail>();
}
I think you are looking for something like "Parcelable". It can save any ArrayList and retrieve back when you need it just like the Shared Preferences.
Please have a look here,
How to save custom ArrayList on Android screen rotate?
ArrayList is serializable. Save it as a serializable object in file on storage