Is it possible to change the position of gridview images while swiping? - android

Myself trying to load the images(from webservice) in gridview and it was succesfully done.
I used Lazy Adapter to load all images from web service.
If i click on some image it should be displayed in next Activity and while swiping that image next image should be displayed. Any Ideas???

This can be relatively easy realised using a ViewPager, I'd say. Consider the flow as follows: When the user selects a list item, pass on a list of all the images (e.g. their URIs) and an index indicating the one selected to the activity containing the ViewPager. You can then initialise the ViewPager's adapter with the passed on list and set the current item to display to the index.
Have a look at the API demos for some more hints on how to use a ViewPager, or read the blog post on developers.android.com. In stead of feeding Fragments as Views to the ViewPager, simply instantiate an ImageView - you may find this Q/A on SO useful for some pointers on how to do that. Also, probably equally important to read through is the documentation on PagerAdapter.
//Edit: Some pointers for coding up above suggestion:
Have your onItemClick create a new Intent, add the relevant data for retrieving the images as extra as well as the selected index and start the Activity:
#Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(this, ImageViewer.class);
intent.putExtra(PAGE_POSITION, position);
intent.putStringArrayListExtra(IMAGE_LIST, mImages)
// or add a serializable, e.g. an ArrayList<T> with your POJOs
intent.putExtra(IMAGE_LIST, mImages);
startActivity(intent);
}
In your ImageViewer Activity containing the ViewPager, retrieve all the extras and initialise the ViewPager to the given position/index:
if (getIntent() != null && getIntent().getExtras() != null {
mImagePosition = getIntent().getExtras().getInt(PAGE_POSITION, 0);
mImageList = getIntent().getExtras().getStringArrayList(IMAGE_LIST);
// or if you used Serializables
mImageList = (ArrayList<T>) getIntent().getExtras().getSerializable(IMAGE_LIST);
mViewPager.setAdapter(new ImagePagerAdapter());
mViewPager.setCurrentItem(mImageIndex);
}
In the ImagePaderAdapter you instantiate the ImageViews containing the images you want to display. There are tons of examples out there, but it'll look something like this in the basis:
private class ImagePagerAdapter extends PagerAdapter {
public int getCount() {
return mImageList == null ? 0 : mImageList.size();
}
public Object instantiateItem(View pager, int position) {
if (mInflater == null) mInflater = (LayoutInflater) container.getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = mInflater.inflate(R.layout.image_pager_item_layout, null, false);
ImageView photoImageView = (ImageView) view.findViewById(R.id.photo_imageview);
mImageLoader().loadImage(mImageList.get(position), photoImageView);
((ViewPager) pager).addView(view, 0);
return view;
}
public void destroyItem(View pager, int position, Object object) {
((ViewPager) pager).removeView((View) object);
}
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
public void finishUpdate(View container) { }
public void restoreState(Parcelable state, ClassLoader loader) { }
public void startUpdate(View container) { }
public Parcelable saveState() { return null; }
}
Note that I have not tested the code above, nor is it meant to be complete. For instance, I assume you know how to get a reference to a LayoutInflater yourself and have a 'lazy' image loader that asynchronously sets an image against an ImageView. The image_pager_item_layout file can simply be an ImageView, but also a more complex hierarchy of views inside a ViewGroup (like with ListView and GridView), e.g. you can easily add an caption to images. It's quite similar to

Related

Android: call adapter directly in viewpager or call fragment per page?

this question is related with this one that I asked before.
I create a viewpager in MainActivity.java like this:
final ViewPager viewPager = (ViewPager) findViewById(R.id.vp_horizontal_ntb);
viewPager.setAdapter(new PagerAdapter() {
#Override
public int getCount() {
return 5;
}
#Override
public boolean isViewFromObject(final View view, final Object object) {
return view.equals(object);
}
#Override
public void destroyItem(final ViewGroup container, final int position, final Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
if(position==0) {
// here is important!
} else if(position == 1) {
}
...
}
});
Now I want fill each page with some json RecyclerView data list.(get json from network).
each page has independent data list.
For first time, I create a fragment for each page like this:
if (position == 0) {
final View view = LayoutInflater.from(getActivity().getBaseContext()).inflate(R.layout.fragment_tab0, null, false);
tabFragment.MyAdapter adapter = new tabFragment.MyAdapter(getActivity().getSupportFragmentManager());
adapter.addFragment(new tab0Fragment(), getResources().getString(R.string.tab0));
container.addView(view);
return view;
}
(so for 5 page, I have 5 fragment.
DEMO
But My application run slow (laggy) when I swipe pages.(with tap buttom is normal)
So I tried write an Adapter class directly for each page like this:
if (position == 0) {
final View view = LayoutInflater.from(getBaseContext()).inflate(R.layout.item_vp_list, null, false));
final RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getBaseContext(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new Tab0RecycleAdapter());
container.addView(view);
return view;
}
with top code,my application run fast again with swap pages!
Is it important to create fragment per each page?
why I must use fragment?(because some programmer recommended it in viewpager)
my method (second method without fragment) is true or false for a real application?
(I am noob and this is my first app)
Now I want fill each page with some json RecyclerView data list.(get json from network).
If you perform this network task on the UI thread, it will block and could cause laggy performance. This could be the reason your pages load slowly. You should perform network tasks on a separate thread.
So I tried write an Adapter class directly for each page like this
You only need one adapter per recycler view. If you want to support multiple views within the same adapter, override getItemViewType(). Example here: https://stackoverflow.com/a/26245463/7395923
Is it important to create fragment per each page?
Why I must use fragment? (because some programmer recommended it in view pager)
It is possible to use a view pager without fragments. Base on your previous question (linked at the top), it does seem overkill to load an entire fragment just to inflate a view. Here is a link to an example of a view pager without fragments: https://stackoverflow.com/a/18710626/7395923
I hope this helps.

destroyItem being called when position ==2 why? android pagerAdapter

I have a very simple PagerAdapter that for some reason, removes the views at position 0 and
1 when it is scrolled to position 2.
Specifically, when I first run the app, there are 3 views. I scroll to position 2 and position 1's view will disappear. View 0 is still there. If I scroll to view 0 and back to view 2 and again back to view 0, View 0 suddenly disappears. I do the same again, and I can actually see view 0 being instantiated and immediately destroyed.
what is going on here?
Main Activity
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyPagerAdapter adapter = new MyPagerAdapter(this);
final ViewPager myPager = (ViewPager) findViewById(R.id.mypanelpager);
myPager.setAdapter(adapter);
myPager.setCurrentItem(1);
}
public class MyPagerAdapter extends PagerAdapter {
private Context ctx;
Calendar c = Calendar.getInstance();
int month = c.get(Calendar.MONTH);
int year = c.get(Calendar.YEAR);
ViewGroup collection;
public MyPagerAdapter (Context ctx){
this.ctx = ctx ;
}
#Override
public int getCount() {
return 3;
}
public Object instantiateItem(ViewGroup container, int position ){
this.collection = (ViewPager)container;
NewMonth monthObject = new NewMonth(ctx, month+position-1,2012);
View monthLayout = monthObject.newParentLayout;
collection.addView(monthLayout);
return monthLayout;
return addViewAt(position);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
collection.removeViewAt(position);
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public boolean isViewFromObject(View view, Object arg1) {
return view==arg1;
}
}
You have to maintain all tabs in memory specifying the OffscreenPageLimit to N-1, in your case put this inside the onCreate method:
myPager.setOffscreenPageLimit(2);
Checkout populate() function from ViewPager source - it has clear checks then to remove currentIndex+1 and currentIndex-1 items. Removing is done based on view sizes and it seems to be entirely internal ViewPager logic. ViewPager source is located
<android sdk folder>\extras\android\support\v4\src\java\android\support\v4\view\ViewPager.jav‌​a
.
You might debug ViewPager to know that is happening exactly, but I wouldn't suggest so until You faced some serious issue with ViewPager. What's why: if you dig into ViewPager code it might lead You to write some hackish code (even not intentionally) on it based on its internal structure and not on its public interface and documentation. So, the main idea of data encapsulation would be ruined which is definitely not good (sadly, sometimes we need to do so in Android due to lack of documentation / unclear naming / android internal issues etc., check Code Complete by Steve McConnell for more details on good encapsulation etc.).
Here the key is that position is different then index. If they give you a position there is no guarantee that your collections haven't removed other positions.
Example:
Let's say your at position 2. destroyItem might have been called for position 0 which means in your collections now position 2 is actually at index 1. So your indexes will quickly become out of sync from your positions. The same thing happens with listview children. I would recommend using a sparseArray instead.
private final SparseArray<View> views = new SparseArray<>();
public View instantiateItem(ViewGroup container, final int position) {
...
views.put(position, view);
...
}
public void destroyItem(ViewGroup container, int position, Object object) {
View view = views.get(position);
if (view != null) {
container.removeView(view);
views.remove(position);
}
}

Outofmemeoryerror (viewpager + imageviews)

i am showing 150+ images in viewpager, when page size crossed 70 app is crashing ,
all images are loading from network , and i have fallowed [link]: Strange out of memory issue while loading an image to a Bitmap object
and i am recycling it whenever page swiping reaches 4,
for 70 page app taking 200 MB of memory.
i need help from you, how to handle it
i have to show all pages with swiping...
i have also used Runtime.getRuntime().gc();
is any way to releasing memory if app memory is reaches the 50+ MB
thanks in advance
The complete solution can be found below, the important lines are those in the destroyItem method:
private class ContentPagerAdapter extends PagerAdapter {
#Override
public void destroyItem(View collection, int position, Object o) {
View view = (View)o;
((ViewPager) collection).removeView(view);
view = null;
}
#Override
public void finishUpdate(View arg0) {
// TODO Auto-generated method stub
}
#Override
public int getCount() {
return ids.length;
}
#Override
public Object instantiateItem(View context, int position) {
ImageView imageView = new ImageView(getApplicationContext());
imageView.findViewById(R.id.item_image);
imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), ids[position]));
((ViewPager) context).addView(imageView);
return imageView;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view==((ImageView)object);
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// TODO Auto-generated method stub
}
#Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}
#Override
public void startUpdate(View arg0) {
// TODO Auto-generated method stub
}
I think this happens because you have a memory leak, double check your variables, don't use static vars for anything big, use final when possible, make all members private.
i suggest you make a commit (or save your current code) and then try to do what i asked and see if it fixes it.
a code sample would let me tell you if you have memory leaks, maybe you can post the code somewhere like on github or google code
Bottom line: you could be doing everything right but a variable still holds a reference to your images so the garbage collector can't touch them.
I know saying you have a memory leak hurts but please don't be alarmed this happens to the best of the best, because it's so easy to happen.
NOTE: No matter how big the data i load from network apps never needed more than the size of 1 file if handled correctly.
Thanks
Sheetal,
Having looked at your code can you try the following:
#Override
public void destroyItem(View collection, int position, Object o) {
Log.d("DESTROY", "destroying view at position " + position);
View view = (View) o;
((ViewPager) collection).removeView(view);
view = null;
}
This should release the imageView for garbage collection.
Are you loading your images in the onCreateView() view method?
The framework seems to takes care of the memory management requirements when doing it this way. I had tried loading my images in my FragmentPageAdapter passing them into my Fragment constructor preloaded or as part of the Fragment instaniateItem method but these both gave me the issue that you are facing. In the end I passed the information needed to load each image into the Fragment constructor and then used these details in the onCreateView() method.
Mark
Sheetal,
I don't have my code in front of me at the minute but it should be something similar to the following:
public class MyFragment extends Fragment {
private Resources resources; // I load my images from different resources installed on the device
private int id;
public MyFragment() {
setRetainInstance(true); // this will prevent the app from crashing on screen rotation
}
public MyFragment(Resources resources, int id) {
this(); // this makes sure that setRetainInstance(true) is always called
this.resources = resources;
this.id = id;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ImageView imageView = (ImageView)inflater.infale(R.layout.fragmentview, container, false);
imageView.setImageBitmap(BitmapFactory.decodeResource(resources, id));
return imageView;
}
}
This is pretty much off the top of my head, so it might not be exact, but it is pretty close.
If you need any more help just shout, don't forget to mark this answer as having helped you:).
Sheetal,
As requested, I use the above code as follows:
In my FragmentActivity I do the following in the onCreate() method:
pager = (ViewPager)findViewById(R.id.pager);
imageAdapter = new MyPagerAdapter(this, pager, theResources, listOfIds.pageBitMapIds);
imageAdapter.setRecentListener(this);
pager.setAdapter(imageAdapter);
Then in my PagerAdapter I do the following:
#Override
public Object instantiateItem(ViewGroup container, int position) {
MyFragment myFragment = new MyFragment(resources, resourceIds[position], recentListener, position);
myFragments[position] = myFragment;
return super.instantiateItem(container, position);
}
#Override
public Fragment getItem(int position) {
return myFragments[position];
}
And then I use the code in my previous answer above.
This is obviously not complete code and it has been adapted from my actual code, but it should give you a good idea of what to do and where to do it.
Mark

Android: Viewpager and FragmentStatePageAdapter

I'm designing an app that allows users to flip between multiple pages in a ViewPager. I've been struggling trying to figure out how it is possible to remove a Fragment instance from a page when it is no longer visible on screen, cache it (to, say, a HashMap), and then restore it so that when the user flips back to that page, the views and everything else in it will be in the same state it was before removal. For example, my first page is a login screen that makes certain layout elements on that particular page visible/invisible on a successful login. When I flip forward enough pages then flip back to the first page, the layout is reset. This becomes more of a problem for another one of my pages which contains a huge, horizontal/vertical scrolling grid of data that I use a thread in the background to draw when it initializes. I use a progress dialog to notify the user of loading progress and that becomes really annoying everytime I have to load it.
So I did some research...
I browsed through the source code for FragmentStatePageAdapter and in the destroyItem() callback, the state of the Fragment instance being removed is saved to an ArrayList. When a new instance of the Fragment is being created in the instantiateItem() callback, if an instance of an item doesn't already exist (they keep track of this by using an ArrayList), a new Fragment instance is created and its saved state is initialized with the corresponding Fragment.SavedState data. Unfortunately, this data does not include the state that the Views were in although I noticed that for pages with a GridView/ListView, the state of the Views were somehow restored (if I scrolled to some random position, flipped a few pages and came back, it would not be reset).
According to the API:
The saved state can not contain dependencies on other fragments --
that is it can't use putFragment(Bundle, String, Fragment) to store a
fragment reference because that reference may not be valid when this
saved state is later used. Likewise the Fragment's target and result
code are not included in this state.
Being a noob to Android, I'm not quite sure I understand the last statement.
That being said, is there any way to cache View state? If not, I think I'll just go ahead and go with leaving all the fragment pages in memory.
I had the same problem problem and solved it by implementing these two functions
public void onSaveInstanceState (Bundle outState)
public void onActivityCreated (Bundle savedInstanceState)
on the fragments that I wanted to save. On the first function, you should save in the Bundle the date that you need to restore the views ( in my case I had a bunch of spinner so I used
an int array to save their positions). The second function, which is called when restoring your fragment, is where you implement the restoring process.
I hope this helps. I also made my adapter to inherit from FragmentStatePageAdapter but I am not sure that this is mandatory.
Listing of main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:text="Page 1" android:id="#+id/textViewHeader"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:gravity="center" android:padding="10dip" android:textStyle="bold"></TextView>
<android.support.v4.view.ViewPager
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="#+id/viewPager" />
</LinearLayout>
Setting up the ViewPager
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this);
viewPager.setAdapter(adapter);
The PagerAdapter
#Override
public void destroyItem(View view, int arg1, Object object) {
((ViewPager) view).removeView((View)object);
}
#Override
public int getCount() {
return views.size();
}
#Override
public Object instantiateItem(View view, int position) {
View view = views.get(position);
((ViewPager) view).addView(view);
return view;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
look here for more details view pager example
Looking at the various documentation pieces, my best guess is that the views you are creating do not have an ID attached to them. Assuming that the fragment's saved state is created from Fragment.onSaveInstanceState, then the fragment will automatically save any view's state that has an id. You probably have a default id associated with your ListView/GridView if you created them from a layout file. You can also associate an id with the views by calling setId.
Also, for your custom filled fragment, you may also have to do something custom in onSaveInstanceState.
Here's an example of how I implemented caching in PagerAdapter. After filling the cache all future view requests are served from cache, only data is replaced.
public class TestPageAdapter extends PagerAdapter{
private int MAX_SIZE = 3;
private ArrayList<SoftReference<View>> pageCache = new ArrayList<SoftReference<View>>(3);
public TestPageAdapter(Context context){
// do some initialization
}
#Override
public int getCount() {
// number of pages
}
private void addToCache(View view){
if (pageCache.size() < MAX_SIZE){
pageCache.add(new SoftReference<View>(view));
} else {
for(int n = (pageCache.size()-1); n >= 0; n--) {
SoftReference<View> cachedView = pageCache.get(n);
if (cachedView.get() == null){
pageCache.set(n, new SoftReference<View>(view));
return;
}
}
}
}
private View fetchFromCache(){
for(int n = (pageCache.size()-1); n>= 0; n--) {
SoftReference<View> reference = pageCache.remove(n);
View view = reference.get();
if (view != null) {
return view;
}
}
return null;
}
#Override
public Object instantiateItem(View collection, int position) {
View view = fetchFromCache();
if (view == null) {
// not in cache, inflate manually
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.page, null);
}
setData(view, position);
((ViewPager) collection).addView(view, 0);
return view;
}
private void setData(View view, int position){
// set page data (images, text ....)
}
public void setPrimaryItem(ViewGroup container, int position, Object object) {
currentItem = (View)object;
}
public View getCurrentItem() {
return currentItem;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public void destroyItem(View collection, int arg1, Object view) {
((ViewPager) collection).removeView((View) view);
addToCache((View) view);
}
}
I also ran into this problem when I was using PagerSlidingTabStrip and using and instance of FragmentPagerAdapter, switching to FragmentStatePagerAdapter definitely worked.
Then I use onSaveInstanceState() to save sate

Display Empty View in Android Gallery

Folks -
I'm trying to implement a Gallery widget that displays an ArrayList of images, and I have started with the Hello, Gallery example on the dev site. This part is all working great.
I need to have the gallery display an empty view (a special view when the ArrayList has no contents), but I cannot seem to get the Gallery to do this. I have done this with ListView and other AdapterViews in the past, but I cannot get it to work with Gallery. What do I need to override/implement in the Adapter, Gallery, or both to get an empty view displayed? This is my adapter code:
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private ArrayList<Drawable> images;
public ImageAdapter(Context c) {
mContext = c;
TypedArray a = c.obtainStyledAttributes(R.styleable.Gallery1);
mGalleryItemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0);
a.recycle();
images = new ArrayList<Drawable>();
}
public void addImage(Drawable d) {
images.add(d);
}
public boolean isEmpty() {
return getCount() == 0;
}
public int getCount() {
return images.size();
}
public Drawable getItem(int position) {
return images.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View contentView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageDrawable(images.get(position));
i.setLayoutParams(new Gallery.LayoutParams(160, 120));
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setBackgroundResource(mGalleryItemBackground);
return i;
}
}
When the view is to be displayed with an empty ArrayList, getCount() does get called (returning 0), but the Gallery never checks isEmpty, and when I had defined getEmptyView() in the Gallery, it was never called either. Did I miss another required method in BaseAdapter to properly notify the empty state?
Thanks!
With the assistance of this article, I found the answer:
Correct use of setEmtpyView in AdapterView
The key to the issue was that (once I got the Gallery/AdapterView to properly call the empty status check using the addendum information) AdapterView is designed only to switch the View visibility settings between the content and empty views (swapping View.GONE and View.VISIBLE). Therefore, if you didn't do the legwork of properly creating and laying out both the content and empty views in the parent layout, they will not display properly.
In may case, I had created the empty view programmatically (just a TextView) and used setEmptyView() to attach it to the adapter view. The TextView was never attached to the LinearLayout that represented the Activity, so it didn't show up even after the AdapterView so kindly set it View.VISIBLE.

Categories

Resources