I have a FragmentActivity in the first tab of a TabHost, and the FragmentActivity itself holds a ViewPager.
The ViewPager's setAdapter() method sets a FragmentPagerAdapter with a set of Fragments. The goal is to have swipeable images in the first pane of the TabHost.
To test it, I was loading a bunch of images that I had kept locally in the project's drawable directory. Everything worked beautifully.
After having tested this initial setup, I'm downloading a bunch of image URLs' off a REST web service. I want these images to load lazily in the ViewPager, and I tried calling Picasso's load() method in three places:
The onCreateView() method of the ViewPager's Fragments (the same place where I was earlier loading images from the local drawable directory).
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.layout_fragment, container, false);
ImageView imageView = (ImageView)getView().findViewById(R.id.fragment_image);
String url = getArguments().getString(IMAGE_RESOURCE_URL);
Context context = getActivity();
Picasso.with(context).load(url).fit().into(imageView);
return myFragmentView;
}
The onViewCreated() method of the ViewPager's Fragments.
#Override
public void onViewCreated(View view, Bundle savedInstanceState){
Context context = getActivity();
String url = getArguments().getString(IMAGE_RESOURCE_URL);
ImageView imageView = (ImageView)view.findViewById(R.id.fragment_image);
Picasso.with(context).load(url).fit().into(imageView);
}
The onInstantiateItem() method of the FragmentPagerAdapter.
#Override
public Object instantiateItem(ViewGroup container, int position) {
View v = inflater.inflate(R.layout.layout_fragment, container, false);
Context context = Tab1Activity.this;
String url = getItem(position).getArguments().getString("IMAGE_RESOURCE_URL");
ImageView imageView = (ImageView)v.findViewById(R.id.fragment_image);
Picasso.with(context).load(url).fit().into(imageView);
return v;
}
None of these methods worked. I know that its not a problem with Picasso because the other day I tried using Picasso in a ListView and it worked like a charm. What am I doing wrong ?
Your first approach should work fine... if you implement it correctly. I would expect your code to crash with a NullPointerException.
Replace:
ImageView imageView = (ImageView)getView().findViewById(R.id.fragment_image);
with:
ImageView imageView = (ImageView)myFragmentView.findViewById(R.id.fragment_image);
Your lazy loader should work in the context of the
Fragment.createView()
where the Fragment is one of a collection being paged by the ViewPager and the FragmentpagerAdapter
That is your option one.
I use another lazyloader that manages local memcache and local filesys cache for the bitmaps needed to load the images. at the time of the "OnCreateView()" it will go to the network to get the URL for the loader if the bitmap is not already cached.
Related
i have try a lot of example or tutorial on tablayout, all work fine
(https://www.simplifiedcoding.net/android-tablayout-example-using-viewpager-fragments/, http://www.androidbegin.com/tutorial/implementing-fragment-tabs-in-android/)
but now i encounter 1 problem, which is i dont know how to set/change the TextView/ImageView while onCreate/onLoad
so far, what i able to do is, write all my code under onTabSelected, but this is not my solution because i cant show empty or static values on the 1st tab, then have to wait until click or slide again then only load the real data.
i'm sure it have a way or solution for my problem, can anyone share it to me (code/website)
or in order to have this Tab feature with viewpager contain recyclerview, Tablayout is not the right way to code, perhaps it have another more correct way to code.
Do these changes inside of onCreateView of fragment that you want
Example:
//Our class extending fragment
public class Tab1 extends Fragment {
//Overriden method onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Change R.layout.tab1 in you classes
View view = inflater.inflate(R.layout.tab1, container, false);
TextView textView = (TextView) view.findViewById(R.id.you_textview_id);
textView .setText("Your text to show");
//Returning the layout file after inflating
return view;
}
}
The TextView need to be previous declared in tab.xml that you're inflating, in our example, tab1.
I have a ViewPager as the row of a RecyclerView. It is like a "featured products" row.
I set the adapter of the ViewPager in the onBindViewHolder of the RecyclerView. ViewPager contains a TextView and an ImageView. ImageView is loaded from an URL via Glide in instantiateItem. The list of items in the ViewPager is 4.
The problem is, the ImageViews of the first two items in the ViewPager are not loaded. If I swipe the ViewPager to the 3rd item and back, I see the first ImageView successfully.
TextViews work fine. The problem is only with the images.
If I debug the code, I observe that the code block that belongs to Glide is reached.
If I use the ViewPager as a standalone view in the fragment (not as a row of the RecyclerView) I observe no problems.
There is a similar question:
Image not loading in first page of ViewPager (PagerAdapter)
But that unfortunately does not apply to my case. I declare my variables locally and with the final modifier already.
PagerAdapter's instantiateItem is like follows:
#Override
public Object instantiateItem(ViewGroup container, int position) {
final LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View v = inflater.inflate(viewpager_layout, container, false);
final ImageView imgProduct = (ImageView) v.findViewById(R.id.imgProduct);
final TextView lblName = (TextView) v.findViewById(R.id.lblName);
final Product product = data.get(position);
lblName.setText(product.name);
Glide
.with(ctx)
.load(product.url)
.asBitmap()
.thumbnail((float) 0.4)
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.animate(R.animator.fade_in)
.into(imgProduct);
}
RecyclerView's onBindViewHolder looks like:
#Override
public void onBindViewHolder(final DataObjectHolder holder, int position) {
int listType = getItemViewType(position);
final ProductList item = data.get(position);
if (listType == 0) {
final List<Product> lstProducts = GetProducts(item.products);
final MyPagerAdapter myAdapter = new MyPagerAdapter(ctx, lstProducts);
holder.viewPager.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged(); // this changes nothing also..
}
else {
// removed..
}
}
I work with the AppCompat library by the way.
All suggestions are welcome. Thank you.
Glide sets images asynchronously and helps to avoid any lag in UI thread. If there are many images it does take some time for the image to set in, but it doesn't cause any lag.
It's a simple Asynchronous request issue, when you swipe to the third tab and come back, till the data would have come and come and then it's gets binded with the UI.
I had faced the similar issue in my project.
I am assuming that you are using a new fragment for each view pager
One thing you can do is...
1.While creating fragment in viewPager in **getItem()**, set a tag with fragment.
2. create a viewPager **addOnPageChangeListener** and on page selected, get that fragment by tag and check if image is loaded or not.
3. If not loaded, then show some loader, and wait for the response, after response hide the loader
Although there are many questions about nested fragments but still it got me stumped in this case.I am making an android app which has an Activity having a frame layout.I am loading a fragment(say Fragment B) into the frame layout.Fragment B has a ViewPager that contains two fragments.First fragment of viewpager(say Fragment VP1) has a gridview that loads images from network.Now on griditem image click I want to show the image in full size in a new fragment which has NetowrkImageView. How do i do this? I tried need to call getChildFragmentManager(),but didn't work. If you guys need code I'll show that. Thanks in advance.
onCreateView() of VP1
public View onCreateView(LayoutInflater inflater, #Nullable final ViewGroup container, #Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_images_and_videos,null);
gridItemList=new ArrayList<>();
gridview= (GridView) view.findViewById(R.id.gridview);
adapter=new ImagesVideosGridviewAdapter(getActivity(),gridItemList);
gridview.setAdapter(adapter);
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// GridItemModel gridItem= (GridItemModel) parent.getItemAtPosition(position);
Bundle args=new Bundle();
args.putString("url",gridItemList.get(position).getUrl());
FragmentTransaction transaction=getChildFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container_event_specific,new ShowFullImageFragment()).commit();//frame_container_event_specific is present in the main activity.
}
});
new LoadMedia().execute("");
return view;
}
Logcat error
java.lang.IllegalArgumentException: No view found for id 0x7f0d008a (com.test.rajat.a10times:id/frame_container_event_specific) for fragment ShowFullImageFragment{ccc6a0 #0 id=0x7f0d008a}
Because your VP1 layout doesn't contain R.id.frame_container_event_specific, the fragment manager is unable to find the view. It's in the activity layout, so you better let the fragment manager from the activity to replace the fragment.
But think again about using the fragment approach. I think it's much simpler and easier to make the full image fragment as a standalone Activity. It displays full image anyway. Otherwise, even though you are able to replace the fragment correctly, you have to do a lot to manage the fragment back stack, like replacing the previous fragment when user hits back button.
I have a fragment that is suppose to populate a background with an Imageview that is created with Picasso.
public class Fragment1 extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Context c = getActivity().getApplicationContext();
View myFragmentView = inflater.inflate(R.layout.home, container, false);
TextView text = (TextView) myFragmentView.findViewById(R.id.text);
ImageView imageView = (ImageView) myFragmentView.findViewById(R.id.image);
String url = ModalScreens.byId(1).getBackgroundImagePath();
Picasso.with(c).load(url).fit().into(imageView);
return myFragmentView;
}
}
I can't figure out what I am doing wrong but the image wont load.
From your code it seems possible that you're loading a local resource and not from the web. If the former is the case, then you could just load it like
StackOverflow showing image View from local disk
If not and you're loading a web image, then this could be a variety of problems, here's some hopefully helpful troubleshooting
Have you added?
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Is the link a valid (as adavis as suggested)? Free of spaces from the beginning and end what not. I would do a
Log.wtf("Testing valid URL", "|"+url+"|")
Is the image too large? In that case it might return a null
Image too large Stackoverflow answer
Is the Imageview properly set to display? I would confirm by initially setting it to some image like this
android:src="#mipmap/large in your layout xml for your ImageView
I have an Activity with an ImagePagerAdapter (extends of FragmentStatePagerAdapter) that has this getItem method:
#Override
public Fragment getItem(int position) {
Log.d(LOGTAG, "------------>mUserPicturesList.get("+position+").getFilename(): " + mUserPicturesList.get(position).getFilename());
return UserDetailFragment.newInstance(mUserPicturesList.get(position).getFilename());
}
The fragment that is instantiated has this onCreateView:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate and locate the main ImageView
final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
mImageView = (ImageView) v.findViewById(R.id.imageView);
mProgressPicturePager = (ProgressBar) v.findViewById(R.id.progress_picture_pager);
String imageUrl = WApp.PHOTO_URL + mImageUrl + "?type=user_gallery_big_img";
Picasso picasso = Picasso.with(getActivity());
picasso.setDebugging(true);
picasso.load(imageUrl)
.placeholder(R.drawable.no_picture_man_big)
.error(android.R.drawable.stat_notify_error)
.into(mImageView, new Callback() {
#Override
public void onSuccess() {
mProgressPicturePager.setVisibility(View.GONE);
}
#Override
public void onError() {
Log.d(LOGTAG, "picasso load error");
mProgressPicturePager.setVisibility(View.GONE);
}
});
return v;
}
The Problem:
When the ImagePager load first time, some times, Picasso call onError, showing the .error drawable.
If I press on back button and go back to the Activity that has the ImagePager, Picasso load the picture correctly.
If the ImagePager has two or more pictures and I swipe between the pictures, those are loaded correctly some times without exit and reenter to the ImagePager.
The Theories:
I think that it could be a problem of cache, but after many searches, I bet that the problem is in the weak reference of the Picasso.
Keep in mind that the problem only appears the FIRST TIME I load the activity that have the ImagePager.
In another place, currently Picasso works fine in listView with an adapter loading the pictures at first time. Calling Picasso inside the getView method of Adapter class.
Visited links
How would an anonymous class get GC'd in picasso on Android?
ViewPager unable to load images lazily with Picasso
http://square.github.io/picasso/
https://plus.google.com/communities/109244258569782858265/stream/885843f4-c8b5-4851-9de1-b0374121dfa3
Use of Target in Picasso on Adapter
https://github.com/square/picasso/pull/349
Thanks in advance.
The problem was solved in the Picasso 2.3.0.
The fix is in the Picasso changelog:
Requests will now be automatically replayed if they failed due to network errors.
I hope this save you many hours.