I have a List<Presenter> presenterList;
With
public class Presenter(){
String name;
String imageRef; // Filename to be downloaded
Bitmap image;
(etc...)
}
I'm working with AsyncTask & once the image has downloaded, I wish to go through the list and set Image value to the newly download image.
so far i have
Presenter pres = PresenterList.get(Position);
pres.Image = new (Bitmap) downloadedImageFromImageRef;
however i fear that this will not relate to the Image value of the presenter within the list.
How do i refer, or even assign to the specific Presenter attribute within the list?
From working with C (many years ago), i belive somthing like a pointer to the value in which to assign .Image would work
Thank you in advace
Well, if you have C experience, then the thing to know about Java is that it doesn't use pointers, but it does use references. So if I'm understanding your problem correctly, you are already using the Image attribute of a Presenter instance elsewhere and then you want to fill it in later. Assigning pres.Image = new (Bitmap) DownloadedImageFromImageRef; would not work in this case because other objects are looking at a different Bitmap object reference.
What you might need to do is use an observer pattern -- it depends on the details of your problem. Here's an example:
Somewhere in the code I have a class Foo that wants to use the Image property from a Presenter instance. But, since that property isn't set until later, this class wants to be notified when it is ready (it is an observer).
public class Presenter {
String Name;
String ImageRef; // Filename to be downloaded
private Bitmap Image;
private PresenterImageObserver observer;
public void setImageObeserver(PresenterImageObserver pio) {
this.observer = pio;
}
public void setImage(Bitmap b) {
this.Image = b;
this.observer.imageLoaded(b);
}
}
public interface PresenterImageObserver {
public void imageLoaded(Bitmap b);
}
public class Foo implements PresenterImageObserver {
//Foo's constructor. It wants the image from presenter p, when it is ready
public Foo(Presenter p) {
p.setImageObserver(this);
}
public void imageLoaded(Bitmap b) {
//b contains the loaded image and this Foo instance can use it now!
}
}
You'd need to set the image using pres.setImage(new (Bitmap) downloadedImageFromImageRef);.
So you have to find in your list the Presenter for which the correct imageref. You have basically two options.
First, you simply iterate through your list
for (Presenter presenter: presenterList) {
if (presenter.imageref.equals(imageName) {
Presenter.image = new Bitmap(downloadedImage);
break; // found : stop iterations
}
}
Secondly, you can create a HashMap for your presenters, with the imageref as the key :
HashMap<String, Presenter> map = new HashMap<>();
for (Presenter presenter: presenterList) {
map.put(presenter.imageref, presenter);
}
Then, you can directly find the right presenter through map.get(imageName)
Related
I have the following code for my Adapter:
#Override
public void onBindViewHolder(GameViewHolder holder, int position) {
final Games game = gameList.get(position);
holder.awayTeamImageView.setBackgroundResource(R.drawable.fortyers);
}
The above works perfectly but I am hard coding the image that will be displayed. What I really need is to get the background image from the game list and I am looking to do something like this:
holder.awayTeamImageView.setBackgroundResource(R.drawable.game.getaBackground());
But that causes an error
How can I dynamically set the imageView's background resource?
UPDATE:
I have attached a screenshot of the desired effect.
Each week the schedule will change so the list will always be different depending on the week selected.
Game constructor:
public Games(DataSnapshot game) {
this.AwayTeam = game.child("AwayTeam").getValue().toString();
this.AwayId = Integer.parseInt(game.child("AwayId").getValue().toString());
this.HomeTeam = game.child("HomeTeam").getValue().toString();
this.HomeId = Integer.parseInt(game.child("HomeId").getValue().toString());
this.aBackground = game.child("aBackground").getValue().toString();
this.hBackground = game.child("hBackground").getValue().toString();
}
Create a list of String that will contain the id of your drawables, keep them in the order synced with the position and then get the id as per the position from the list to pass while setting the drawable.
If it's something reused in multiple places and the order is consistent, you can also go for an Enum to get the drawable id in terms of a certain key of your choice. You might need to figure the key using a when condition as per position if it's really dependant on the position.
Hope that works!
Assuming Games class should be a model/data class; so you can have an int member field that can store drawable id for each instance of this class, and create getter & setter:
public class Games {
private int myDrawable;
public int getMyDrawable() {
return myDrawable;
}
public void setMyDrawable(int myDrawable) {
this.myDrawable = myDrawable;
}
}
Whenever you construct the Games objects, you can set the drawable image id using setMyDrawable(R.drawabe.foo).. Probably you can modify the Games constructor to accept an int parameter that you can set myDrawable to.
Then in RecyclerView:
#Override
public void onBindViewHolder(GameViewHolder holder, int position) {
final Games game = gameList.get(position);
holder.awayTeamImageView.setBackgroundResource(game.getMyDrawable());
}
UPDATE:
public class Games {
private int homeTeamImage, awayTeamImage;
public int getHomeTeamImage() {
return homeTeamImage;
}
public void setHomeTeamImage(int homeTeamImage) {
this.homeTeamImage = homeTeamImage;
}
public int getAwayTeamImage() {
return awayTeamImage;
}
public void setAwayTeamImage(int awayTeamImage) {
this.awayTeamImage = awayTeamImage;
}
public Games(DataSnapshot game, int homeTeamImage, int awayTeamImage) {
//... rest of code
this.homeTeamImage = homeTeamImage;
this.awayTeamImage = awayTeamImage;
}
}
But in terms of OOP, I'd suggest that you can have a Team class that has either team properties, like image, name, .... etc. and the Games class can be the controller of the game, not building the team instances.
Another approach that might help you is using getIdentifier from the string. For this what you have make a background string name same as the one in your resource. e.g if you want to show ic_menu_camera then your aBackground should be the same string.
Example game class:
public class Game {
String aBackground;
public Game(String aBackground) {
this.aBackground = aBackground;
}
public String getaBackground() {
return aBackground;
}
public void setaBackground(String aBackground) {
this.aBackground = aBackground;
}
}
Then use it like this. Read inline comments to understand in detail.
//set the background name that you want same as name in your drawable folder
// without any extension .png or .xml. Just name should be there
Game game = new Game("ic_menu_camera");
ImageView imageView = findViewById(R.id.imageView);
//get the id by name using this
/* #param name The name of the desired resource.
* #param defType Optional default resource type to find, if "type/" is
* not included in the name. Can be null to require an
* explicit type.
* #param defPackage Optional default package to find, if "package:" is
* not included in the name. Can be null to require an
* explicit package.
*
* #return int The associated resource identifier. Returns 0 if no such
* resource was found. (0 is not a valid resource ID.)
* */
int resId = getResources().getIdentifier(game.getaBackground(), "drawable",getPackageName());
// then set that id to your image
imageView.setBackgroundResource(resId);
Do not make things too complicated. Just use a simple approch. Using HashMap should do just fine.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Cincinnati", R.drawable.red_striped_helmet);
map.put("Cleveland", R.drawable.red_helmet);
map.put("New York", R.drawable.blue_helmet);
map.put("Chicago", R.drawable.dark_blue_helmet);
map.put(homeTeam, homeId);
...
holder.awayTeamImageView.setBackgroundResource(map.get("New York"));
// holder.awayTeamImageView.setBackgroundResource(map.get(homeTeam));
I'm currently using osmdroid to display current positioning.
Based on the following example i tried to optimize the system a little bit by not constructing the ItemizedOverlay<OverlayItem> and ArrayList<OverlayItem> each time my location is changed, but construct them only once in the constructor, and later on simply add points to my ArrayList variable.
Here's how it looks now:
private void InitializeMarkersOverlay() {
mOverlayItemArrayList = new ArrayList<OverlayItem>();
ItemizedOverlay<OverlayItem> locationOverlay =
new ItemizedIconOverlay<OverlayItem>(this, mOverlayItemArrayList, null);
mMapView.getOverlays().add(locationOverlay);
}
and when a new location arrives:
private void AddPointToOverlay(GeoPoint gPt, boolean bShouldClearList) {
OverlayItem overlayItem = new OverlayItem("", "", gPt);
Drawable markerDrawable = ContextCompat.getDrawable(this, R.drawable.pin);
overlayItem.setMarker(markerDrawable);
// first time initializer
if(bShouldClearList) {
mOverlayItemArrayList.clear();
}
mOverlayItemArrayList.add(overlayItem);
}
Since my mMapView already has a pointer to mOverlayItemArrayList i was hoping that my mapview's layer would be automatically notified regarding the change. but nothing actually happens. Only by recreating the objects, i get to see the pin.
Adding to the list does not work because ItemizedIconOverlay need to do some operations on addition. You can check source code for ItemizedIconOverlay.
You can see there is call to populate() in addItem method (and all other methods which are manipulating with items).
public boolean addItem(final Item item) {
final boolean result = mItemList.add(item);
populate();
return result;
}
But populate() is an implementation detail and is marked as protected so you cannot call it directly.
Correct solution would be:
Don't keep reference to the list but to ItemizedIconOverlay
instance.
Use mLocationOverlay.addItem(overlayItem)
You may need to call mapView.invalidate() after adding new point.
I got it working by accessing the overlay directly from the mapview object, not sure why exactly, as i was hoping mMapView.getOverlays() would hold a reference to the ItemizedIconOverlay and its itimized array
if(mMapView.getOverlays().size() > 0) {
((ItemizedIconOverlay<OverlayItem>)mMapView.getOverlays().get(0)).removeAllItems();
((ItemizedIconOverlay<OverlayItem>)mMapView.getOverlays().get(0)).addItem(overlayItem);
}
}
I think I kind of reinvent caching in Java but have a point I don't get further.
In case the answer is anywhere on Stackoverflow for this issue I might had not understood it when searching or didn't understand the required complexity and searched for a more easy way.
Short what I want to do: call a method on an Object. The object should load a picture and store it as Image. Then it should decorate itself with an Decorator so that called method will next time only return the image with no more IO operations.
My Interace Picture Interafce is simple like this:
import java.awt.*;
public interface PictureInterface {
public Image getImage();
}
My Decorator looks like this:
import java.awt.*;
public class PictureDecorator implements PictureInterface {
private final Picture p;
public PictureDecorator(Picture p){
this.p = p;
}
public Image getImage(){
return this.p.pipeImage();
}
}
It saves a Picture and on getImage() calls pictures pipeImage - the picture "real" getImage().
And last but not least the Picture Class:
import java.awt.Image;
public class Picture implements PictureInterface{
private final String path;
private final Image image;
public Picture(String path){
this.path = path;
}
private void loadImage(){
this.image = /*IO Magic Loading the Image from path*/
}
public Image getImage() {
loadImage();
/*Decorate Yourself with Picture Decorator*/
return /*Decorator.getImage*/;
}
Image pipeImage(){
return this.image;
}
}
If getImage is called I want Picture to Decorate itself and call the Decorators getImage and most importent overwrite its old refference (Java is call by value, this is where i'm stuck atm) so on further getImage Calls the Decorators getImage Method is called.
As a little extra-question I think my access to the mage from Decorator is not best practice, hints welcome ^^
EDIT:
To add a thing: I allready thought if this it not possible: what would be "smarter": go for if(image==NUll) or make a decorateYourself() function where image is loaded and decorator returned in Picture and in Decorator it only returns itself, apply this to the Image var and then call getImage, like:
ImageInterface x = new Image("path);
x = x.decorateYourself()
Image i = x.getImage()
this ways i would only do a method-call to return the decorator itself, but i have to call both methods ...
If getImage is called i want Picture to Decorate itself and call the
Decorators getImage and most importent overwrite its old refference
(Java is call by value, this is where i'm stuck atm) so on further
getImage Calls the Decorators getImage Method is called.
A decorator doesn't work in this way.
With decorator you want to augment or diminish a behavior of an existing class without being invasive for this class : no needed modification.
So the decorator instance decorates an object that has to share with the decorator class a common type and a common method.
Besides I don't think that you need to use a decorator.
Here you don't decorate a picture but you bypass its loading if it was already previously performed.
I think that it would be more suitable to use a proxy that decides whether it must load the resources of get it from the cache.
Don't worry, it doesn't change many things in the classes you have introduced: interface, common method and object wrapping are still required.
In your case PictureInterface is the common type between the proxy class and the proxy subjects classes that provides the common method : getImage().
import java.awt.*;
public interface PictureInterface {
public Image getImage();
}
PictureProxy, a proxy class could implement PictureInterface to act as any PictureInterface instances.
PictureProxy should be responsible to check if it has cached the result of a previous loading of the image. It is the case it returns it. Otherwise it calls getImage() on the Picture instance that holds and it caches the result.
import java.awt.*;
public class PictureProxy implements PictureInterface {
private final Picture p;
private final Image image;
public PictureProxy(Picture p){
this.p = p;
}
public Image getImage(){
if (image != null){
return image;
}
image = p.getImage();
return image;
}
}
And Picture class should not be aware of the proxy when it performs getImage().
It is the proxy class that handles the state of the cache.
import java.awt.Image;
public class Picture implements PictureInterface{
private final String path;
private final Image image;
public Picture(String path){
this.path = path;
}
private void loadImage(){
this.image = /*IO Magic Loading the Image from path*/
}
public Image getImage() {
loadImage();
return image;
}
}
From the client of the classes you could do something like that :
Picture picture = new PictureProxy(new Picture("picturePath"));
Image img = picture.getImage(); // load the image from Picture the first time and get it
Image img = picture.getImage(); // get it from the PictureProxy cache
The Picasso library allows one to load an image easily like:
Picasso.with(context).load(url).into(imageview);
The API also allows to specify an error image. But what can I do if I want the library to first try three or four different URLs before giving up and displaying the error image? Ideally these images would be tried sequentially, falling back to the next one if the previous wasn't loaded.
Natively there's no API for such functionality. But with some clever coded Picasso.Target you can easily achieve such functionality.
I'll add here a quick hack-untested code that should give you a rought idea on what to look for. You'll have to test and maybe fine tune, but that should be pretty OK.
private static final List<MultiFallBackTarget> TARGETS = new ArrayList<MultiFallBackTarget>();
public static class MultiFallBackTarget implements Picasso.Target {
private WeakReference<ImageView> weakImage;
private List<String> fallbacks;
public MultiFallBackTarget(ImageView image){
weakImage = new WeakReference<>(image);
fallbacks = new ArrayList<String>();
TARGETS.add(this);
}
public void addFallback(String fallbackUrl){
fallbacks.add(fallbackUrl);
}
public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from){
removeSelf();
ImageView image = weakImage.get();
if(image == null) return;
image.setImageBitmap(bitmap);
}
public void onBitmapFailed(Drawable errorDrawable){
ImageView image = weakImage.get();
if(image == null) {
removeSelf();
return;
}
if(fallbacks.size() > 0){
String nextUrl = fallbacks.remove(0);
// here you call picasso again
Picasso.with(image.getContext()).load(nextUrl).into(this);
} else {
removeSelf();
}
}
public void onPrepareLoad(Drawable placeHolderDrawable){}
private void removeSelf(){
TARGETS.remove(this);
}
}
Remember that Picasso does not hold strong references to the Target you put inside into(object). That means, internally Picasso uses WeakReference to that.
So that means that you need that self reference in TARGETS to keep reference of all MultiFallBackTarget you create and allow them to self-remove when their job is done.
I found many simple solutions to this (such as Intent.putExtra(String, String) and Bundle.putString(String, String)), but this is not helpful for my situation.
I have a class called MyMP3 which contains non-primitive types. I need to pass the following for MyMP3...
private AudioFile audioFile;
private Tag tag;
private int index;
private boolean saved, startedWithLyrics;
private String id3lyrics;
AudioFile and Tag are both classes that I imported from a .jar file. How can I go about passing these to another Activity via Intents? I tried messing with implementing Parcelable for my "MyMP3" class, but I am not sure how to correctly use these methods when not passing primitive types.
Could you help me out and look at my code below and try to tell me how to correctly use Parcelable with a custom class like mine? How do I set the Parcel in the writeToParcel function and how do I correctly retrieve the class in another Activity?
Below is my code (the part that is important, at least). I've been trying different things for a couple of days now, but I cannot get it to work. Please help me out!
public class MyMP3 extends AudioFile implements Parcelable
{
private AudioFile audioFile;
private Tag tag;
private int index;
private boolean saved, startedWithLyrics;
private String id3lyrics;
public MyMP3(File f, int index)
{
this.audioFile = AudioFileIO.read(f);
this.tag = this.audioFile.getTag();
this.index = index;
this.saved = false;
this.id3lyrics = getLyrics();
}
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel out, int flats)
{
/* This method does not work, but I do not know how else to implement it */
Object objects[] = {this.audioFile, this.tag, this.index, this.saved, this.startedWithLyrics, this.id3lyrics};
out.writeArray(objects);
}
public static final Parcelable.Creator<MyMP3> CREATOR = new Parcelable.Creator<MyMP3>()
{
public MyMP3 createFromParcel(Parcel in)
{
/* Taken from the Android Developer website */
return new MyMP3(in);
}
public MyMP3[] newArray(int size)
{
/* Taken from the Android Developer website */
return new MyMP3[size];
}
};
private MyMP3(Parcel in)
{
/* This method probable needs changed as well */
Object objects[] = in.readArray(MyMP3.class.getClassLoader());
}
}
You can make your MyMP3 class Parcelable like that. Make sure you get the read/write order correct. The non-primitives must also be Parcelable, so you might not have control over that unfortunately. Alternatively, you could come up with your own serialization/deserialization. You could use a text format, like JSON or XML. Another alternative is to use subclass Application (make sure you declare it in your manifest) and use it is as a place to hang objects that span Activities. This keeps the object in memory for the lifecycle of your app, so be careful with doing this.