I have an xml file called activity_collage.xml with 2 street view fragments - I would like both fragments to display the street view of a given location - So a total of 2 street views on the screen
Here's my code:
public class Collage extends ActionBarActivity implements OnStreetViewPanoramaReadyCallback {
StreetViewPanoramaFragment topLeft, topRight;
static final LatLng posOne = new LatLng(43.771925, -79.512460);
static final LatLng posTwo = new LatLng(43.790634, -79.193632);
Here's where I initialize my 2 StreetViewFragment objects in my onCreate() method
topLeft =
(StreetViewPanoramaFragment) getFragmentManager()
.findFragmentById(R.id.picTL);
topLeft.getStreetViewPanoramaAsync(this);
topRight =
(StreetViewPanoramaFragment) getFragmentManager()
.findFragmentById(R.id.picTR);
Here's the overridden method from the OnStreetViewPanoramaReadyCallback interface ...
#Override
public void onStreetViewPanoramaReady(StreetViewPanorama panorama) {
panorama.setPosition(posOne);
}
But how do I set the street view for topRight?
According to http://code.google.com/p/gmaps-api-issues/issues/detail?id=6953, multiple StreetViewPanorama Objects are not supported. In my experience, no explicit errors occur, but the second StreetViewPanorama will remain blank.
Frustratingly, I don't think the documentation was updated like the above thread indicates it should have been.
I have the same problem with my fragment XML containing 2 fragments : map fragment, and StreetViewPanoramaFragment. StreetView stays blank. I managed to use it with a simple view instead of fragment.
With Kotlin it gives :
val myFrag=getView()!!.findViewById<StreetViewPanoramaView>(R.id.street_view_panorama)
myFrag.onCreate(savedInstanceState);
myFrag.getStreetViewPanoramaAsync(OnStreetViewPanoramaReadyCallback { panorama ->
panorama.setPosition(LatLng(55.758818, 37.620587))
svPano = panorama
})
Related
I'm using a Recycler View to show all the images from the galley or the external storage of a device in a Grid Layout Manager. And I'm using a Radio Button to show if the image is selected or not.
PROBLEM
Whenever I select or deselect a Radio Button from the visible Views in the Recycler View some other Views which are outside the Visible Screen got selected or deselected.
It is like I'm pressing on the same View of the Recycler View, but the images are different.
PROBLEM
well that's because of the recycler view concept of reusing the views instead of creating new views every time you scroll.
you see if you have 100 items you want to show in a recycler view and only 20 of them could appear to the user, recycler view creates only 20 view holder to represent the 20 items, whenever the user scroll recycler view will still have 20 view holder only but will just switch the data stored in this view holders rather than create new view holders.
now to handle selection of your items there's two ways to do this.
the naive way
hold selection in a boolean array inside the recycle view adapter.
whenever the user scrolls, the adapter calls onBindViewHolder to update the visible viewholder with the proper data.
so when onBindViewHolder gets called just set the radio button selection according the boolean array using the position sent in the method call
at the end of your usage to the recycler view you can create a getter method in the adapter to get the selection array list of boolean and pass the data based on it
public class PhotosGalleryAdapter extends RecyclerView.Adapter<PhotosGalleryViewHolder> {
ArrayList<Your_Data_ClassType> data;
ArrayList<Boolean> dataSelected ;
public PhotosGalleryAdapter(ArrayList<Your_Data_ClassType> data) {
this.data = data;
dataSelected = new ArrayList<>(data.size()) ;
}
...
#Override
public void onBindViewHolder(#NonNull PhotosGalleryViewHolder holder, int position) {
...
RadioButton radioButton = holder.getRadioButton()
radioButton.setChecked(dataSelected.get(position));
radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
dataSelected.set(holder.getAbsoluteAdapterPosition() , isChecked) ;
}
});
...
}
}
the other way is to use a selection tracker and it should be the correct way to handle selections in a recycler view.
the problem with this way is it needs a lot of editing to the code and creating new classes to include as parameters in the selection tracker, but in the end you'll find it worth the time you spent on it.
in order to start with this way you need to do the following :
firstly, decide what should be a key (String-Long-Parcelable) so the tracker should use to differentiate between your data , the safest way is either String or Parcelable as I once tried Long and ended up with lots and lots of problems (in your case I will assume it's the photo's uri which will be of type string)
secondly, you need to create two new classes, one that extends ItemDetailsLookup, and the other extends ItemKeyProvider, and should use the key as their generic type (the type that is put between <> )
your two classes should look like this (that you might copy them straight forward)
the one that extends ItemKeyProvider :
public class GalleryItemKeyProvider extends ItemKeyProvider<String>{
PhotosGalleryAdapter adapter ;
/**
* Creates a new provider with the given scope.
*
* #param scope Scope can't be changed at runtime.
*/
public GalleryItemKeyProvider(int scope,PhotosGalleryAdapter m_adapter) {
super(scope);
this.adapter = m_adapter;
}
#Nullable
#Override
public String getKey(int position) {
return adapter.getKey(position);
}
#Override
public int getPosition(#NonNull String key) {
return adapter.getPosition(key);
}
}
the one that extends ItemDetailsLookup :
public class GalleryDetailsLookup extends ItemDetailsLookup<String> {
private final RecyclerView recView ;
public GalleryDetailsLookup(RecyclerView m_recView){
this.recView = m_recView;
}
#Nullable
#Override
public ItemDetails<String> getItemDetails(#NonNull MotionEvent e) {
View view = recView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder holder = recView.getChildViewHolder(view);
if (holder instanceof PhotosGalleryViewHolder) {
return ((PhotosGalleryViewHolder) holder).getItemDetails();
}
}
return null;
}
}
thirdly, you should include this new two methods in your adapter to be used by the above classes
public class PhotosGalleryAdapter extends RecyclerView.Adapter<PhotosGalleryViewHolder> {
...
public String getKey(int position) {
return data.get(position).getUri();
}
public int getPosition(String key) {
for (int i = 0; i < data.size(); i++) {
if (data.get(i).getUri() == key) return i;
}
return 0;
}
...
}
forthly (if there's an english word called forthly), you should initialize the tracker with all the above classes that were created before and he will handle the rest, the tracker takes as parameters
a unique selection tracker id (if that will be the only selection tracker you will use then name it anything)
the ItemKeyProvider that we created
the DetailsLookup that we created
a String-Long-Parcelable Storage to store the keys that were selected in (in our case it will be a String Storage)
a Selection predicate, it's responsible to handle the way of selection you want to do, you want it to be able to (select only one item-multiple selection with no limits- based on a weird algorithm like even only or odd only), in my case I will use a default multiple selection one but if you want to alter it with another selection algorithm you should create a new class that extends SelectionPredicates and implement your way of selection, you could also just check the other default ones might be what you're looking for.
anyway, that's how the initialization should look (you should put this code wherever you initialize your recycler view at whether it's in fragment or activity method):
private void initRecycleView() {
...
SelectionTracker<String> tracker = new SelectionTracker.Builder<>("PhotosGallerySelection",
Your_Recycler_View,
new GalleryItemKeyProvider(ItemKeyProvider.SCOPE_MAPPED, photosAdapter),
new GalleryDetailsLookup(Your_Recycler_View),
StorageStrategy.createStringStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build();
...
}
I didn't find a way to let me initialize the adapter with data and then create the tracker inorder to make the viewholders know about their selection or not, so in this case I firstly created the tracker and then made the adapter know about it's data using a setter and notifyDataSetChanged
what I mean by that is after creating the tracker instantly set the tracker and data to the adapter, so the initRecycleView should look like this
private void initRecycleView() {
...
SelectionTracker<String> tracker = new SelectionTracker.Builder<>("PhotosGallerySelection",
Your_Recycler_View,
new GalleryItemKeyProvider(ItemKeyProvider.SCOPE_MAPPED, photosAdapter),
new GalleryDetailsLookup(Your_Recycler_View),
StorageStrategy.createStringStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build();
photosAdapter.setTracker(tracker);
photosAdapter.setData(data);
photosAdapter.notifyDataSetChanged();
...
}
Last but no least, you should handle how the view holders should know if they were selected or not, so you should let the adapter know about the tracker and its data by creating a setter method in it, that's how the adapter should look like in the end :
public class PhotosGalleryAdapter extends RecyclerView.Adapter<PhotosGalleryViewHolder> {
ArrayList<Your_Data_Class> data;
private SelectionTracker<String> tracker;
public PhotosGalleryAdapter() {
data = new ArrayList<>();
}
public ArrayList<Your_Data_Class> getData() {
return data;
}
public void setData(ArrayList<Your_Data_Class> m_data) {
this.data = m_data;
}
#Override
public ScheduleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
...
}
#Override
public void onBindViewHolder(#NonNull PhotosGalleryViewHolder holder, int position) {
...
boolean isSelected = tracker.isSelected(data.get(i).getUri());
RadioButton radioButton = holder.getRadioButton;
radioButton.setChecked(isSelected);
}
#Override
public int getItemCount() {
return data.size();
}
public String getKey(int position) {
return data.get(position).getUri();
}
public int getPosition(String key) {
for (int i = 0; i < data.size(); i++) {
if (data.get(i).getUri() == key) return i;
}
return 0;
}
public void setTracker(SelectionTracker<String> m_tracker) {
this.tracker = m_tracker;
}
}
(as you may notice if you initialized the adapter with its data through the constructor, when he asks the tracker if there were an item selected or not, it will result in a NullPointerException as at the moment of initializing the adapter you still didn't initialize the tracker)
that way you could keep track of your selection the way google suggests in their documentation (which I honestly don't know why the made it very complicate like that).
if you want to know all the selected item in the end of your application/fragment use, you should call tracker.getSelection() which will return a Selection List for you to iterate on
There's a tiny problem/feature with the tracker that it won't start selecting the first item until you use a long press on it, that happens only in the first item you select, if you do want this feature (start selecting mode by long press) then leave it as it is
incase you don't want it you can make the tracker select a ghost key (any unique string key that means nothing to your data) at the beginning which should later enable the selection mode with a simple click on any photo
tracker.select("");
this also the way to make a default/old selection at the beginning, you could make a for loop and call tracker.select(Key) if you do want the tracker to start with few items being selected
N.B : incase you use the Ghost Key method you should watchout that the selection array that will get returned when you call tracker.getSelection() will also contain this Ghost Key.
at the end if you do have the curiosity of reading about selection tracker in the documentation follow this link
or maybe if you know how to read kotlin follow this two links
implementing-selection-in-recyclerview
a guide to recyclerview selection
I was stuck in the selection problem for days before I figure how to do all that so I hope you find your way through it.
Omar Shawky has covered the solutions.
With my answer I will stress on the reason why someone may face this sort of an issues with recycler views and how to avoid this common issue in the future (avoiding pitfalls).
Reason:
This issue happens because RecyclerView recycles views. So a RecyclerView item's view once inflated can get reused to show another off screen (to be scrolled to) item. This helps reduces re-inflation of views which otherwise can be taxing.
So if the radio button of an item's view is selected, and the same view gets reused to show some other item, then that new item can also have a selected radio button.
Solution:
The simplest solution for such issues is to have an if else logic in your ViewHolder to provide logic for both selected and de-selected cases. We also do not rely on information from radio button itself for initial setup (we do not use radioButton.isSelected() at the time of setup)
e.g code to write inside your ViewHolder class:
private boolean isRadioButtonChecked = false; // ViewHolder class level variable. Default value is unchecked
// Now while binding in your ViewHolder class:
// Setup Radio button (assuming there is just one radio button for a recyclerView item).
// Handle both selected and de-selected cases like below (code can be simplified but elaborating for understanding):
if (isRadioButtonChecked) {
radioButton.setChecked(true);
} else {
radioButton.setChecked(false);
}
radioButton.setOnCheckedChangeListener(
(radioButton, isChecked) -> isRadioButtonChecked = isChecked);
Do not do any of the following while setting up:
private boolean isRadioButtonChecked = false; // class variable
//while binding do not only handle select case. We should handle both cases.
if (isRadioButtonChecked) { // --> Pitfall
radioButton.setChecked(true);
}
radioButton.setOnCheckedChangeListener((radioButton, isChecked) -> isRadioButtonChecked = isChecked);
OR
// During initial setup do not use radio button itself to get information.
if (radioButton.isChecked()) { // --> Pitfall
radioButton.setChecked();
}
I have a Google Maps activity that includes a search box and a List View of Deliveries (which hold LatLng). When I click on an address inside of the List view, I want the camera to move to that address/marker (all Addresses inside the ListView are marked).
I have this method inside of my custom ListAdapter where I tried to get Update the Camera, but it doesn't work. Here's part of my code.
public class DeliveriesListAdapter extends ArrayAdapter<Delivery> implements View.OnClickListener {
private ArrayList<Delivery> deliveries;
public DeliveriesListAdapter(ArrayList<Delivery> data, Context context) {
super(context, R.layout.activity_row_item, data);
this.deliveries = data;
this.mContext = context;
}
#Override
public void onClick(View v) {
// TODO: on click, move camera to selected address on map.
int position = (Integer) v.getTag();
Delivery delivery = (Delivery) deliveries.get(position);
CameraUpdateFactory.newLatLng(delivery.getLatLng());
}
}
The List at the bottom of the screen is supposed to move the camera to that marker on the map.
Here's My Data class that holds the LatLng of the specified list item.
public class Delivery {
private String address;
private double distance, time;
private LatLng latLng;
private Place place;
public Delivery(Place place) {
address = place.getAddress().toString();
distance = -1;
time = -1;
latLng = place.getLatLng();
this.place = place;
}
Inside of my Google Maps activity:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback{
private ArrayList<Delivery> deliveryList = new ArrayList<>();
// called inside onCreate(...)
private void setUpListView() {
deliveriesListView = findViewById(R.id.deliveries_listview);
adapter = new DeliveriesListAdapter(addressList, this);
deliveriesListView.setAdapter(adapter);
}
/**
* Used to add Delivery to a list view.
* #param delivery The Delivery containing the address to be added to the view to add the marker.
*/
private void addDeliveryToList(Delivery delivery) {
deliveryList.add(delivery);
adapter.notifyDataSetChanged();
}
edit: I know that CameraUpdateFactory.newLatLng(delivery.getLatLng()); was wrong, I was messing with it earlier and deleted what I had originally.
SOLUTION: I managed to solve it by rewriting my custom list adapter to be a RecyclerViewAdapter, and managed to move the Overriden onClick method to my Maps Activity, which was what I was having trouble doing.
You also need to tell map to move camera. Like this mMap.moveCamera(CameraUpdateFactory.newLatLng(delivery.getLatLng())). And one more thing, you should neveer handle clicks in the adapter. They ought to be contained in the host UI part. You can use interface to have callback in the host UI part. You can get hint on how to do this here.
In your onCLick function, replace CameraUpdateFactory.newLatLng(delivery.getLatLng()); by animateCamera(CameraUpdateFactory.newLatLng(delivery.getLatLng())
I have tried to draw a shape for a long time without an answer. I used setContentView(view) for that view. However, I have another setContentView:
setContentView(R.layout.activity_main);//Buttons, Views from XML...
setContentView( mCustomDrawableView);//Custom Shape
Problem is, it only shows ONE view, the shape. The Buttons defined on XML are gone. Basically whichever setContentView(view) is called last is the only View who is drawed. I've also tried many other ways to draw the shape without an answer that works.
I have seen that multiple setContentView do overlap each other, but this is the only way that partially works. Any way to display both Views or another way to draw a shape and my XML Views?
EDIT:
My shape is made in java, not XML
EDIT 2:
Here is my customShape Class:
public class CustomDrawableView extends View {
private ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
int x = 10;
int y = 10;
mDrawable = new ShapeDrawable(new RectShape());
// If the color isn't set, the shape uses black as the default.
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x,y,x+10,y+10);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}
OK ... setContentView is something which takes a layout/view/whatever and the input defines the contents of the activity/fragment your are setting it to.
So of course your first call does the whole layout, and the second wipes that and sets the content to what you're sending to it.
The thing is to have your view and then add it to a layout. If you have any view in your initial setContentView xml named (+id) and defined (Layout x = getfromid(r.id.x) you can then add your view to that layout with x.addview(myview) or x.addview(CustomDrawableView(context)).
-edit for clarification-
An Activity/Fragment has a Layout which you set using setContentView. You do this once, normally. A Layout defines where Views are placed.
So, you setContentView(your.xml) in onCreate. If you have a programmatic View class, you can then do two things: add new YourView to a named and specified layout in your act/frag (using any layout defined in xml, passed to your frag/act in the xml passed in setContentView and then defined by Layout x = findViewById(R.id.idoflayoutinxmlinfield 'android:id="#+id/x") in code.
Then you can x.addview(new MyView(context)) or Myview z = new MyView(context) and x.addView(z);
-edit2-
When you get better, you can also just add your custom View to the original layout by adding it to the original xml definition.
-edit3-
Sigh. OK. Here it is:
In your activities java file:
RelativeLayout myRellayout;
#Override
public void onCreate(){
setContenView(your.xml)
myRelLayout = findViewByID(R.id.myrellayout);
myRelLayout.addView(new MyCustomView(this));
//or define your custom view and the add it by:
//MyCustomView mcv = new MyCustomView(this);
//myRelLayout.addView(mcv);
}
And your xml should contain any layout like this:
<RelativeLayout
android:id="#+id/myrellayout"
android:width="whatever"
android:height="whatever"
/>
I've been a lurker on your forums for some time now, and they have helped me enormously on a variety of issues I've had, so thanks! :D
I'm trying to create my first app for android and it's a card game named shithead that my friends and I used to play often.
I've decided to use a RecyclerView to display your hand. I need to be able to dynamically add buttons (with card images) to the display at runtime. From what I can tell, I need an adapter for this.
Using the handy guide at "https:// guides.codepath.com/android/using-the-recyclerview" and attempting to modify it for my own purposes I've come very close to a test run of making this work. When I run the program on an emulator in Android Studio, it gives me the following display:
images displayed as grey rectangles
I feel like I've got to be really close, and I'm simply missing some crucial syntax for working with android or android studio.
In my Card object I build a String idText that correlates to the card images I have saved in my project's mipmap-hdpi, mipmap-xhdpi etc. folders (mipmap-hdpi shown below)
mipmap-hdpi folder in Android Studio
public Card(int suitInput, int rankInput)
{
suit = suitInput;
rank = rankInput;
faceUp = false;
text = RankString[rank] + " of " + SuitString[suit];
idText = "i_" + RankString[rank] + "_of_" + SuitString[suit];
}
I also have a function getImageId in my Card Class:
public static int getImageId(Context context) {
return context.getResources().getIdentifier("drawable/#+id/" + idText, null, context.getPackageName());
}
my onBindViewHolder method in my CardAdapter is below:
#Override
public void onBindViewHolder(CardAdapter.ViewHolder viewHolder, int position)
{
//get the data model based on position
Card card = mCards.get(position);
//Set item views baased on views and data model
ImageButton imageButton = viewHolder.cardButton;
imageButton.setImageResource(card.getImageId(mContext));
TextView textView = viewHolder.cardText;
textView.setText(card.text);
}
my MainActivity class does very little as of yet. It initializes one player, me, and a Deck of Card objects. It then gives me 6 cards off the top of the unshuffled deck, AKA 2 - 7 of diamonds. It then initializes the CardAdapter.
public class MainActivity extends AppCompatActivity {
Player me;
Deck deck;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testdisplay);
//lookup the recyclerview in activity layout
RecyclerView rvCards = (RecyclerView) findViewById(R.id.rvCards);
me = new Player("Dr_StrangeKill");
deck = new Deck();
me.hand.add(deck.getCards().remove(0));
me.hand.add(deck.getCards().remove(0));
me.hand.add(deck.getCards().remove(0));
me.hand.add(deck.getCards().remove(0));
me.hand.add(deck.getCards().remove(0));
me.hand.add(deck.getCards().remove(0));
//create adapter passing in sample user data
CardAdapter adapter = new CardAdapter(this, me.hand);
//Attach the adapter to the recyclerView to populate items
rvCards.setAdapter(adapter);
//Set layout manager to position the items
rvCards.setLayoutManager(new LinearLayoutManager(this));
}
The Deck, Player, and Card objects all appear to be working as intended insofar as the values of the code are concerned, as the resultant display correctly shows that 'Dr_StrangeKill' has received 2 - 7 of Diamonds, but doesn't show the card images themselves! This is my first time working with images in any IDE (eclipse, visual studio, android studio), and I feel like i'm very close but off in some small but crucial way. Any help would be greatly appreciated!
I suggest you put your images in a drawable folder because mipmap folder is for launcher icons, if you want to support different screen densities, here's a link that shows you how to create a drawable folder for each screen density, after that you should get your image identifier like this:
context.getResources().getIdentifier(idText, "drawable", context.getPackageName());
It looks like you're trying to get the identifier of the image resource by name. In that case you could try:
return context.getResources().getIdentifier(idText, "mipmap", context.getPackageName());
But doing that is not recommended. See docs for getIdentifier()
https://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)
You could just add the id (an int) to your card object and instantiate like new Card(R.mipmap.hearts_2,2,"hearts") or however you're instantiating (seems like you're using public fields and no constructor) and grab that id in your adapter to set on the imageview.
I have read a few posts on this site about finding screen dimensions and most of them reference getWindowManager().getDefaultDisplay;
I tried to this is my code and got an error saying cannot resolve method getWindowManager(). My code is below so you can fully understand:
public class MainActivity {
public static void main(String args[])throws IOException{
Display display = getWindowManager().getDefaultDisplay;
//WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
//int displayid = Display.DEFAULT_DISPLAY;
//Display display = DisplayManager.getDisplay(displayid);
int tlx = (168/800)*display.getWidth();
int tly = (136/480)*display.getHeight();
int brx = (631/800)*display.getWidth();
int bry = (343/480)*display.getHeight();
int rotation = display.getRotation();
This is just an excerpt in case somethings don't make sense. Also, I'm using the display dimensions to find screen elements within an application. If I should be using the window dimensions for this please inform me upon how to go about doing that as well, but I still need to get the display right so I can use methods such as the .getRotation() one shown above. Thanks in advance for your help.
You need to reference getWindowManager from an Activity. Your MainActivity doesn't appear to be extending Activity so you either need to extend Actvity
public class MainActivity extends Activity {
Or pass an activity to that class then do something like this.
Display display = myActivity.getWindowManager().getDefaultDisplay();
Or through a Context
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();