I'm doing lazy loading on a RecyclerView adapter. First I fetch the image metadata (if it has any) and then let Picasso download the actual image.
public class PostHolder extends RecyclerView.ViewHolder implements Callback<Photo>{
private PostImageView cover;
private TextView content;
private long id;
public PostHolder(View itemView) {
super(itemView);
this.itemView.setOnClickListener(onClickListener);
content = (TextView) itemView.findViewById(R.id.post_content);
cover = (PostImageView) itemView.findViewById(R.id.post_image);
}
public void setPost(final Post post, int i){
cover.unsetImage();
this.id = post.getPhotoId();
itemView.setTag(i);
content.setText(post.getMessage());
if("photo".equals(post.getType()) || "video".equals(post.getType()) && id != 0L){
cache.get(id, this);
}else{
cover.setUrl(post.getImageUrl());
}
}
#Override
public void result(Photo result) {
if(result != null && result.getId() == PostHolder.this.id){
cover.setPhoto(result);
}
}
}
cache.get() loads my metadata from the cache, the result is returned with result().setPost() gets called in onBindViewHolder(). Now I'm getting everyone's favorite viewholder issue - my ImageView displays a different image before switching to the correct one. I know that Picasso correctly handles this and I have a check for my own loading where i compare the holder's id to the metadata id. I've spent a few hours on this already. Internet, you are my only hope.
It's fixed now, though I'm not exactly sure why. I believe it's
Picasso
.with(getContext())
.cancelRequest(this);
inside unsetImage() because of the double request needed to load it.
What I think happens:
Picasso is still loading the previous image.
A call for the current image metadata is dispatched.
Picasso loads the preVious image. Since Picasso doesn't know about the metadata, it's not automatically canceled.
The metadata get's loaded, and Picasso starts loading the current image.
Related
Guys Im using the following custom code to load 20 images from resources and present in a viewpager
public class CustomPagerAdapter extends PagerAdapter {
int[] mResources = {
R.drawable.slide1,
R.drawable.slide2,
R.drawable.slide3,
R.drawable.slide4,
R.drawable.slide5,
R.drawable.slide6,
R.drawable.slide7,
R.drawable.slide8,
R.drawable.slide9,
R.drawable.slide10,
R.drawable.slide11,
R.drawable.slide12,
R.drawable.slide13,
R.drawable.slide14,
R.drawable.slide15,
R.drawable.slide16,
R.drawable.slide17,
R.drawable.slide18,
R.drawable.slide19,
R.drawable.slide20,
};
Context mContext;
LayoutInflater mLayoutInflater;
public CustomPagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mResources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageResource(mResources[position]);
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
This works fine but I want to put the jpgs in a directory on the device so that they can be changed without recompiling the app
I think I need to get the images into the mResource array. I can get the path but not sure what format the code should be instead of using the draw-able lines
i have read articles on here but none make sense to me I am really new to this and the code looks nothing like the code I am using
can anyone point me in the right direction?
Any help is greatly appreciated
Mark
Yes, you can certainly do so. I will try to explain you the process step-by-step,
Step 1
Have a File object pointing to the path, like,
File directory = new File("path-to-directory");
Ensure that the path is to the directory with the images,
Step 2
List all the files inside the directory using listFiles() method, like
File[] allImages = directory.listFiles();
Now you have an array of all the files just like int[] mResources. The only difference being, now you have actual file references, while previously you had resource ids.
Step 3
You can just display the images in the ViewPager just like you did previously. But this is a bit tricky and can take you a considerable amount of time and code to get an image properly displayed from File.
You also need to take care of caching, so that when you load a previously loaded image again, it gets it from the cache.
To do all this, I recommend you to use this library (recommended by Google), Glide.
Setting an image is one line of code,
Glide.with(context).from(file).into(imageView);
That's it. Now you have your images displayed in a ViewPager from a directory in the device.
I stumbled upon this question while searching for an answer. But it doesn't seem to be a solution for my case.
In my viewcontroller I've the following:
public void setModel(CarcassonneModel model) {
this.model = model;
ivHoveringTile.imageProperty().bind(getImage(model.board.getActiveTile().getFilename()));
}
private ObjectProperty<Image> getImage(String filename) {
File file = new File("src/carcassonneapplicatie/resources/tiles/" + filename + ".png");
Image image = new Image(file.toURI().toString());
ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>(image);
return imageProperty;
}
However, the displayed image doesn't change when I change the filename in my model using an action event. I've got other bindings for my labels and they seem to work perfectly, except for this one.
If you do
someProperty.bind(someOtherProperty);
then someProperty is updated automatically whenever someOtherProperty.set(...) is invoked.
In your code someOtherProperty is the ObjectProperty<Image> you create in your getImage() method. Since you don't even retain a reference to this property, there's no possible way you can ever call set(...) on it. So the image in ivHoveringTile never updates.
You need to bind to an observable object in the model, representing the actual value that may change.
I've got a problem with creating new Tabs in Wicket on Runtime. When I add a new tab to the list of Tabs of my AjaxTabbedBar, I don't see any changes on the screen, maybe you can help me?
ExamplePage extends Webpage:
private AjaxTabbedPanel<AbstractTab> myTabBar;
tabs = new ArrayList<AbstractTab>();
tabs.add(new AbstractTab(new Model<String>("Übersicht")) {
private static final long serialVersionUID = 1L;
#Override
public Panel getPanel(String panelId) {
if (myOverviewTab == null) myOverviewTab = new OverviewTab(panelId, getInstance());
return myOverviewTab;
}
});
tabs.add(new AbstractTab(new Model<String>("Details")) {
private static final long serialVersionUID = 1L;
#Override
public Panel getPanel(String panelId) {
if (myDetailTab == null) myDetailTab = new DetailTab(panelId);
return myDetailTab;
}
});
myTabBar = new AjaxTabbedPanel<AbstractTab>("tabs", tabs);
add(myTabBar);
This is where I create the tabs on start and here comes my Runtimeaddition
public void newDetailTab(AjaxRequestTarget target){
System.out.println("newDetailtab");
tabs.add(new AbstractTab(new Model<String>("Details")) {
private static final long serialVersionUID = 1L;
#Override
public Panel getPanel(String panelId) {
return new DetailTab(panelId);
}
});
myTabBar.setSelectedTab(myTabBar.getSelectedTab()+1);
target.add(myTabBar);
}
So in the last line I want to change my actual tab, but actually it doesn't works. What makes me wonder is that the first tab has the number -1 (myTabBar.getSelectedtTab()), is this an error?
i also tried to update my tabbar with an AjaxRequestTarget, but there come different errors: cant update a page or Component with id [[tabs28]] was not found while trying to perform...
Hope you can help me.
edit: I found some people on google with an similiar problem, they tried to use LoadabledetachableModel... i just dont really know how to include this because its abstract, and i dont really know how to fill the methods these model wants to use...
So, i got it... Finally i had to use the AjaxRequestTarget from my function from the other class to refresh it directly... When i give that as a parameter there seems to be a fault. I think that this is the point but i'm not totally sure because i changed a lot :)
TabbedPanel starts out with -1 for its selected tab. On rending it will automatically select the first visible tab.
It seems to me you're working on a new TabbedPanel instance in #newDetailTab().
Generally you should add the new tab and then update the entire AjaxTabbedPanel with the AjaxRequestTarget.
"Cannot update component with id=XY" most of the time is indicating that you are missing setOutputMarkupPlaceholderTag(true) on the component you are trying to update via ajax.
I've built a log-in composite that I am displaying in my application entry-point to the user. Upon entry of the username and password, I am sending the username and password to the server via a RemoteService and will receive back an object containing the ClientSession. If the ClientSession is a valid object (recognised username and password), I wish to display the main application panel otherwise I want to display the login dialog again (with an error message).
My question is, that during the async call to the server, how to I mask the screen so that the user cannot click anything whilst the Session is obtained from the server?
I know that the login should be fast, but the Session object contains a lot of Client Side cached values for the current user that is used to generate the main panel. This may take a fraction of a second or up to 5 seconds (I can't control the speed of the underlying infrastructure unfortunately) so I want to mask the screen until a timeout is reached then allow the user to try again.
I have done this exact operation before using GWT Ext, but vanilla GWT seems to have a lot less samples unfortunately.
Thanks
Chris
The GWT class PopupPanel has an optional "glass panel" that blocks interaction with the page underneath.
final PopupPanel popup = new PopupPanel(false, true); // Create a modal dialog box that will not auto-hide
popup.add(new Label("Please wait"));
popup.setGlassEnabled(true); // Enable the glass panel
popup.center(); // Center the popup and make it visible
You might want to check out GlassPanel from the GWT Incubator project. AFAICT it's not perfect, but should be of some help nevertheless ;)
You can also use a dialog box for this purpose.
Here is the code how to use it.
public class NTMaskAlert extends DialogBox {
private String displayText;
private String message;
private static NTMaskAlert alert;
Label lable;
private NTMaskAlert(String text) {
setText(text);
setWidget(new Image(GWT.getModuleBaseURL()
+ "/images/ajax-loader_1.gif"));
setGlassEnabled(true);
setAnimationEnabled(true);
super.show();
super.center();
WorkFlowSessionFactory.putValue(WorkFlowSesisonKey.MASKING_PANEL, this);
}
public static void mask(String text) {
if (text != null)
new NTMaskAlert(text);
else
new NTMaskAlert("Processing");
}
public static void unMask() {
NTMaskAlert alert = (NTMaskAlert) WorkFlowSessionFactory
.getValue(WorkFlowSesisonKey.MASKING_PANEL);
if (alert != null) {
alert.hide();
alert = null;
}
}
public void setDisplayText(String displayText) {
this.displayText = displayText;
alert.setText(displayText);
}
public String getDisplayText() {
return displayText;
}
public void setMessage(String message) {
this.message = message;
lable.setText(message);
}
public String getMessage() {
return message;
}
}
Use static mask and unmask method for operations.
This is my solution:
public class CustomPopupPanel extends PopupPanel {
private Label label = new Label();
public CustomPopupPanel() {
super(false, true); // Create a modal dialog box that will not auto-hide
super.setGlassEnabled(true); // Enable the glass panel
super.add(label); // Add the widget label into the panel
}
public CustomPopupPanel(String text) {
this();
this.mask(text);
}
public final void mask(String text) {
label.setText(text);
super.center(); // Center the popup and make it visible
}
public void unmask() {
super.hide(); // Hide the popup
}
}
I'm making a Java ME application for Symbian S60 5th edition and I have problem with the memory. After some time of running the app I recieve the out of memory exception.
I'm getting images from Google Maps (by the integrated GPS in Nokia 5800) and showing them.
I have this implemented like this:
class MIDlet with method setForm()
class Data which has a thread that collects info about the coordinates, gets image from Google maps, creates new form, appends the image, and calls the method setForm(f) from the Midlet.
Probable the Display.setCurrent(Form f) keeps references on the forms and like this the memory gets fast full.
I tried with Canvas but it has some stupid UI (some circle and some 4 buttons) that I don't like.
How can I solve this problem?
PS: the code...
In class MIDlet
public void setInfo(Form f)
{
getDisplay().setCurrent(f);
}
in class TouristData which collects information about location and gets map image
private attributes:
private Form f=null;
private ImageItem imageItem=null;
private Image img = null;
method locationUpdated which is called when recieve new location:
public void locationUpdated(LocationProvider provider,final Location location)
{
if (!firstLocationUpdate)
{
firstLocationUpdate = true;
statusListener.firstLocationUpdateEvent();
}
if(touristUI != null)
{
new Thread()
{
public void run()
{
if(location != null && location.isValid())
{
//lokacija je, prikaži!
try
{
QualifiedCoordinates coord =location.getQualifiedCoordinates();
if(imageItem == null)
{
imageItem = new ImageItem(null,null,0,null);
imageItem.setAltText("ni povezave");
f.append(imageItem);
}
else
{
img = googleConnector.retrieveStaticImage2(360,470, coord.getLatitude(), coord.getLongitude(), 16, "png32"); //z markerje
imageItem.setImage(img);
}
}catch(Exception e)
{}
}
else
{
}
}
}.start();
}
}
Are you keeping references to the forms or the images? These will keep them from being garbage collected and will cause out of memory errors.
It is hard to tell without some source code. Anyway, it will be better to re-architect your Midlet not to create new forms, but to reuse the same one.