Android - Creating an Airbnb-like Horizontal Image Slider - android

I'm looking to replicate airbnb's app image slider where you have one image at the top of a view that you can horizontally swipe and it automatically goes to the next image upon swipe. I created a horizontalscrollview but it doesn't quite have the right functionality. It doesn't flip from image to image, upon swipe it sort of scrolls along the image and eventually to the next image. I've googled around quite a bit and haven't seen a non deprecated solution.
Any ideas?
EDIT - adding some of my code per request:
Basically at a high level I'm pulling in imageURLs from my AWS database (in a different class not shown below) and then trying to load those URLs into a horizontalscrollview using Picasso.
//generic instructions to allow insertion of pictures into a horizontalimageslider
public View insertPhoto(String path){
LinearLayout layout = new LinearLayout(getActivity());
layout.setGravity(Gravity.CENTER);
ImageView imageView = new ImageView(getActivity());
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
Picasso.with(getActivity() ).load(path).into(imageView); //tried this but got errors when running > resize(layout.getWidth(), layout.getHeight()), also tried .fit() after .load image wouldn't load
layout.addView(imageView);
return layout;
}
private class GetApartmentTask extends AsyncTask<ApiConnector,Long,Apartment >
//after a doInBackground
#Override
protected void onPostExecute(Apartment apartment) {
//to get image URL from database to then use with Picasso to download the image
mImageURLArraylist = mApartment.getApartmentImageArrayList();
for (int z = 0 ;z<mImageURLArraylist.size(); z++) {
mLinearLayout.addView(insertPhoto("http:" + mImageURLArraylist.get(z)) );
}
}}
in onCreateView method (alongside other things obviously)
mLinearLayout = (LinearLayout) v.findViewById(R.id.details_page_apartment_picture);
MyXML (relevant part)
<HorizontalScrollView
android:id="#+id/horizontal_scroll_view"
android:layout_width="match_parent"
android:layout_height="500dp"
android:fillViewport="true">
<LinearLayout
android:id="#+id/details_page_apartment_picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>

After many hours of frustration and some help getting over the finish line, the answer to swiping some part of the screen in this way is to use a viewpager.
Some great references:
swipe some part of the screen
http://developer.android.com/training/animation/screen-slide.html
What is the role of " isViewFromObject (View view, Object object)" in FragmentStatePagerAdapter?
my working code is provided in another question I posted that got me over the finish line :) --
Android ViewPager Won't Show Second Page?

I don't know the specifics of the airbnb app image slider, but it sounds like you could use flexslider. It's basically a JQuery toolkit. I have used it before in a website and it seems to have the functionality you are looking for.
https://www.woothemes.com/flexslider/

Related

Add image programmatically

I have several images in my mipmap folder and I want to add them to the screen at a specific location and then move them. Here is the code I thought should work:
ImageView item = new ImageView(this);
item.setImageResource(R.mipmap.ball);
Where ball is a valid png file. Why doesn't this code show the image?
i assumed that your xml is look like this.
Xml code is
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/layout">
</RelativeLayout>
And try this one in your class file(i.e in activity)
RelativeLayout layout=(RelativeLayout)findViewById(R.id.layout);
ImageView imageView=new ImageView(this);
imageView.setImageResource(R.mipmap.add_symbol);
//which adds the imageview to your layout
layout.addView(imageView);
As stated in the comments, you are creating an image view unattached to any view. The instance is there, but it doesn't know where to render.
If you know where you want to insert it, lets say, some LinearLayout with id image_container, you could do something like (and I'm being very simplistic about it!):
ImageView item = new ImageView(this);
item.setImageResource(R.mipmap.ball);
LinearLayout layout = activity.findById(R.id.image_container);
layout.addView(item)
You might also want to use item.setScaleType( ... ) in order to get the desired scaling.
That being said, please keep in mind that the proper way of adding several items programatically would be using something like a RecyclerView.
Also, in order to get a nice performance for you images loading and rendering, something like Glide or Picasso would be very useful.

Pinterest like custom GridView

I am new to android, and I am searching for a logic for grid view like pinterest(homescreen) app that has been build for i-phone. A large no. of images are coming from the server that I need to show in following form with pagination effect i.e loading images on scroll.
please reply if it is possible other way around. I'll be highly thankful.
If you want to perform loading of image on scroll then it will similar to List View.First save all data from WS URL then load on demand Now
Commonsware Endless Adapter For Listview,you can integrate it with GridView too
EndLessAdapter
Another way is to put your grid views in a ViewFlipper and then flip with an animation.
Use setInAnimation() and setOutAnimation() to set the animations and flip the pages with showNext() and showPrevious()
Create layout like as follow
<ScrollView...>
<LinearLayout....
android:id="#+id/linear1"
orientation="horizontal">
<LinearLayout....
android:id="#+id/linear2"
android:layout_weight="0.33"
orientation="vertical">
<LinearLayout....
android:id="#+id/linear3"
android:layout_weight="0.33"
orientation="vertical">
<LinearLayout....
android:layout_weight="0.33"
orientation="vertical">
</LinearLayout>
</ScrollView>
Now add your ImageView dynamically in layouts
linear1 = (LinearLayout) findViewById(R.id.linear1);
linear2 = (LinearLayout) findViewById(R.id.linear2);
linear3 = (LinearLayout) findViewById(R.id.linear3);
for(int i=0;i<n;i++)
{
ImageView iv = new ImageView(this);
iv.setImageResource(R.id.icon);
int j = count % 3; <----
if(j==0)
linear1.addView(iv);
else if(j==1)
linear2.addView(iv);
else
linear3.addView(iv);
}
Check: Staggered GridView
The StaggeredGridView allows the user to create a GridView with uneven rows similar to how Pinterest looks. Includes own OnItemClickListener and OnItemLongClickListener, selector, and fixed position restore.
This is old question, but for those that have similar problem:
Easiest way to accomplish this layout style is to use RecyclerView with StaggeredGridLayoutManager, like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
View recyclerView = findViewById(R.id.recycle_view);
assert recyclerView != null;
StaggeredGridLayoutManager gaggeredGridLayoutManager = new
StaggeredGridLayoutManager(2, 1);
recyclerView.setLayoutManager(gaggeredGridLayoutManager);
}
For the other part of question (pagging) it's best to receive your images in chunks (for example 50 images per request) and when user scrolls down (comes to the end) load more.

Multiple pages at the same time on a ViewPager

Is there a possibility to display two pages at the same time, when using a ViewPager? I'm not looking for an edge effect, but rather for two full pages at the same time.
Please have a look at the getPageWidth Method in the corresponding PagerAdapter. Override it and return e.g. 0.8f to have all child pages span only 80% of the ViewPager's width.
More info:
http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getPageWidth(int)
See my more up-to-date answer here: Can ViewPager have multiple views in per page?
I discovered that a perhaps even simpler solution through specifying a negative margin for the ViewPager. I've created the MultiViewPager project on GitHub, which you may want to take a look at:
https://github.com/Pixplicity/MultiViewPager
Although this question specifically asks for a solution without edge effect, some answers here propose the workaround by CommonsWare, such as the suggestion by kaw.
There are various problems with touch handling and hardware acceleration with that particular solution. A simpler and more elegant solution, in my opinion, is to specify a negative margin for the ViewPager:
ViewPager.setPageMargin(
getResources().getDimensionPixelOffset(R.dimen.viewpager_margin));
I then specified this dimension in my dimens.xml:
<dimen name="viewpager_margin">-64dp</dimen>
To compensate for overlapping pages, each page's content view has the opposite margin:
android:layout_marginLeft="#dimen/viewpager_margin_fix"
android:layout_marginRight="#dimen/viewpager_margin_fix"
Again in dimens.xml:
<dimen name="viewpager_margin_fix">32dp</dimen>
(Note that the viewpager_margin_fix dimension is half that of the absolute viewpager_margin dimension.)
We implemented this in the Dutch newspaper app De Telegraaf Krant:
You have to override the getPageWidth() method on the viewpager's Adapter. For Example:
#Override
public float getPageWidth(int position) {
return 0.9f;
}
Try that code in your Adapter and then you'll understand.
See that blog entry.PagerContainer technique is solved my problem.
EDIT:
I found same answer.
How to migrate from Gallery to HorizontalScrollView & ViewPager?
You can solve this problem by using getPageWidth in PagerAdapter class.
Be sure about whether your JAR is up-to-date.Since previously ViewPager was not supporting multiple pages at the same time.
public float getPageWidth(){
return 0.4f;(You can choose it .For full screen per page you should give 1f)
}
create your layout file: my_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="240dp"
android:clipChildren="false">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="200dp"
android:clipToPadding="false"
android:layout_height="200dp"
android:layout_marginLeft="70dp"
android:layout_marginRight="70dp"
android:layout_gravity="center" />
</FrameLayout>
</layout>
the viewpager is about as small as you want your pages to be. With android:clipToPadding="false" the pages outside the pager are visible as well. But now dragging outside the viewpager has no effect This can be remedied by picking touches from the pager-container and passing them on to the pager:
MyLayoutBinding binding = DataBindingUtil.<MyLayoutBinding>inflate(layoutInflater, R.layout.my_layout, parent, false)
binding.pager.setOffscreenPageLimit(3);
binding.pager.setPageMargin(15);
binding.pagerContainer.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return chatCollectionLayoutBinding.pager.onTouchEvent(event);
}
});
I don't think that's possible with the limitations of a View Pager. It only allows a user to view pages 1 at a time. You might be able to work something out with the use of fragments, and have 2 ViewPager fragments side by side and use buttons to work out the page flipping you want to implement, but the code may become very complex.
You can try something like a ViewFlipper - where you can do a lot more customizations (including animations).
Hope this helps.
It can be don in the Item Layout of the Viewpager. Assume that, you want two pages at a time.
This is the Item Layout (photo_slider_item.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" style="#style/photosSliderItem">
<ImageView android:id="#id/photoBox1" style="#style/photoBox"/>
<ImageView android:id="#id/photoBox2" style="#style/photoBox"/>
</LinearLayout>
And in your PagerAdapter:
#Override
public View instantiateItem(ViewGroup container, int position){
View sliderItem = LayoutInflater.from(container.getContext()).inflate(photo_slider_item, container, false);
ImageView photoBox1 = (ImageView) sliderItem.findViewById(R.id.photoBox1);
ImageView photoBox2 = (ImageView) sliderItem.findViewById(R.id.photoBox2);
photoBox1.setImageResource(photosIds[position]);
if(position < photosIds.length-1){
photoBox2.setImageResource(photosIds[position+1]);
} else {photoBox2.setImageResource(photosIds[0]);}
container.addView(sliderItem);
return sliderItem;
}
Edited version for more than two items
if you want more than two items, first add your items to the LinearLayout then use following algorithm:
photoBox1.setImageResource(photosIds[position]);
if(position < photosIds.length-i-1){
photoBox2.setImageResource(photosIds[position+1]);
photoBox3.setImageResource(photosIds[position+2]);
.
.
.
photoBox(i).setImageResource(photosIds[position+i-1]);
} else if(position < photosIds.length-i-2){
photoBox2.setImageResource(photosIds[position+1]);
photoBox3.setImageResource(photosIds[position+2]);
.
.
.
photoBox(i-1).setImageResource(photosIds[position+i-2]);
photoBox(i).setImageResource(photosIds[0]);
} . . . else if(position < photosIds.length-1){
photoBox2.setImageResource(photosIds[position+1]);
photoBox3.setImageResource(photosIds[0]);
photoBox4.setImageResource(photosIds[1]);
.
.
.
photoBox(i-1).setImageResource(photosIds[i-2-2]);
photoBox(i).setImageResource(photosIds[i-2-1]);
} else {
photoBox2.setImageResource(photosIds[0]);
photoBox3.setImageResource(photosIds[1]);
.
.
.
photoBox(i-1).setImageResource(photosIds[i-1-2]);
photoBox(i).setImageResource(photosIds[i-1-1]);
}
i : number of your items
[i-2-2] : number 2 in the middle is number of items in the last page of the viewpager.

Quickly switching and animating views in a FrameLayout messes up the views

I have a very strange problem with Android animations, I tried many different approaches and components, and still couldn't find any explanation.
I have a FrameLayout which is a container for views, and a Button.
This FrameLayout should display only one view at a time, and when I click on the Button, I want the FrameLayout to display another view, and start an animation on the view that is removed from the FrameLayout.
The specificity here is that I only use two views, and I want to switch between these two views.
The problem:
When I click the button multiple times really fast, and then stop clicking, the two views are shown at the same time one on top off the other, and won't disappear. The container still contains only one view though... definitely strange!
error screenshot http://i.minus.com/jDXyvUsE1LCOx.png
I was able to reproduce this with a simple example:
public class TestAnimActivity extends Activity implements OnClickListener {
private FrameLayout container;
private TextView current;
private TextView next;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
current = new TextView(this);
current.setText("View 1 YEAH");
next = new TextView(this);
next.setText(" View 2 DOH");
container = (FrameLayout) findViewById(R.id.container);
container.addView(current);
findViewById(R.id.button).setOnClickListener(this);
}
public void onClick(View v) {
Animation outAnimation = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
current.startAnimation(outAnimation);
container.addView(next);
container.removeView(current);
TextView temp = current;
current = next;
next = temp;
}
}
As you can see the animations are being started on the views while previous ones are still running, and the way I do that may somehow be the source of the problem.
If I comment the animation related code, it works perfectly:
// Animation outAnimation = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
// current.startAnimation(outAnimation);
If I stop reusing views and create new views instead, it also works perfectly:
// next = temp;
next = new TextView(this);
next.setText("View: " + new Random().nextInt());
But I don't want to create new views :-) .
It seems that the problem is related to starting animations several times on a view while adding / removing this view from its parent.
I first tried with a ViewFlipper, then a ViewAnimator, then looked up the Android source code and ended up doing this manually to reproduce the problem.
If you want the layout to be able to test by yourself, here is my main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Click me fast" />
</LinearLayout>
Thank you for your time!
I finally found a way to fix the issue.
When we remove a view from a ViewGroup, if there is an animation running on this view, it is automatically added to the "disappearing children" list of the ViewGroup.
The issue seemed to appear when we tried to add to the ViewGroup a view that has been removed from it but was still in its disappearing children.
There's an easy way to fix that : viewGroup.clearDisappearingChildren();
Here would be the new implementation of the onClick() method :
public void onClick(View v) {
next.clearAnimation();
container.clearDisappearingChildren();
Animation outAnimation = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
current.startAnimation(outAnimation);
container.addView(next);
container.removeView(current);
TextView temp = current;
current = next;
next = temp;
}
If it works without quickly pressing the button, and works without the animation then you may be seeing how animations actually work. From my understanding what is displayed in the animation is separate from the View objects they are animating. You can try animating from the parent view instead of the ones that keep being added and removed. Or make sure to clean up the animation before removing the view, as it sounds like the view isn't able to get the onAnimationEnd method where it can remove the phantom image being displayed (setFillAfter(false) which is the default behavior of an Animation).

Gallery ImageAdapter convertView is always null

I am using a Gallery with an ImageAdapter to load it with ImageViews that pull images out of my resources. My problem is that the convertView that gets passed to the getView() method in my adapter is always null. This means that a new ImageView is created each and every time getView() is called. This leads to horrible preformance because the GC is constantly running to wipe away all of these created and no longer used ImageView's.
This is apparently a known bug: Gallery's view cache is broken; never converts views..
My two preferred solutions are either 1. handle a cache of views in the adapter itself and take care of all the logic required to re-use them properly.
or 2. include a my own copy of the Gallery widget and try to fix it so it properly returns recycled views.
I've started implementing option one but am quickly realizing I don't exactly know how to make all of the logic behind that operation. I am begining to think that option two might be easier.
I've found the code for the Gallery widget here: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.1_r2/android/widget/Gallery.java
I don't fully understand it, but I can see that it is calling
child = mAdapter.getView(position, null, this);
on line 745. My (shot in the dark) guess that this is the root of the problem.
Does anyone have experience with this bug. Or can anyone point me in the right direction for figuring out how the recycler situation works so that I can tweak this widget to work correctly? Or even suggest some alternate option that I may be overlooking.
EDIT: The best solution that I ever found was an implementation called EcoGallery. The only place I can find reference to it online anymore is here. To get it working you have to put each chunk from there in the correct place within your project.
No experience with that bug particularly, but I have done custom caching before with a 3rd party view pager (before the support lib).
It's really not too difficult honestly. In my case, I knew there would be, at most, one item on the screen. But I also wanted the item to the left and right to be preloaded (these were webviews pulling data off the net). So I have a simple array of 3 views.
[V1, V2, V3]
Now the only thing you need to do is correlate a position in your adapter to a position in your cache. There's a variety of ways you could tackle this. When I first spiked it out, I just made whatever my current view was to be V2 and the rotated the items of the array around when flipping the pager. So flipping to the next view would alter my array
[V2, V3, V1]
Was simple to keep up with. Or you could just do the math and calculate a position to a relative position of the cache.
Another approach is to create a last in, first out queue. Recycle views by pushing them into the queue, and when you need a view, just pop one from it.
I do not have experience with Gallery widget, but I'am using a lot ListView with images. According to your problem, and link to Google issue, they still haven't fix this problem.
So, there is solutio with nice library (and examples within it), which solve cache/ajax problems, and much more.
Link to library
or, more concrete, link to image examples
If you download their examples, you will find how they implemented gallery with Gallery widget, using their AQuery utility class in
com.androidquery.test.image.ImageLoadingGalleryActivity class.
Snippet from code:
final List<Photo> entries;// here only to show what enteries are...
listAq = new AQuery(this); //define as Action class member, here only to show what it is
ArrayAdapter<Photo> aa = new ArrayAdapter<Photo>(this, R.layout.gallery_item, entries){
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = getLayoutInflater().inflate(R.layout.gallery_item, parent, false);
}
Photo photo = getItem(position);
AQuery aq = listAq.recycle(convertView);
aq.id(R.id.name).text(photo.title);
String tbUrl = photo.tb;
if(!aq.shouldDelay(position, convertView, parent, tbUrl)){
aq.id(R.id.tb).image(tbUrl);
aq.id(R.id.text).text(photo.title).gone();
}else{
aq.id(R.id.tb).clear();
aq.id(R.id.text).text(photo.title).visible();
}
return convertView;
}
};
aq.id(R.id.gallery).adapter(aa);
Where Photo is just POJO object (fetched from remote):
class Photo {
String tb;
String url;
String title;
String author;
}
R.id.gallery refers to
<Gallery
android:id="#+id/gallery"
android:layout_width="fill_parent"
android:layout_height="200dip" />
And R.layout.gallery_item refers to:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dip"
android:layout_height="75dip" >
<ProgressBar
android:id="#+id/progress"
android:layout_width="15dip"
android:layout_height="15dip"
android:layout_centerInParent="true" />
<ImageView
android:id="#+id/tb"
style="#style/GalleryItem"
android:layout_width="100dip"
android:layout_height="75dip" />
<TextView
android:id="#+id/text"
android:layout_width="100dip"
android:layout_height="75dip"
android:gravity="center"
android:maxLines="4"
android:padding="15dip"
android:text="Dummy TextDummy TextDummy TextDummy TextDummy Text"
android:textColor="#FFFFFFFF"
android:textSize="8sp" />
</RelativeLayout>
Hope you'll find this library useful in solving your problem.
I've gotten around this by using a custom cache as dskinner suggests.
I pre-calculate (screenwidth/minwidth of item) the max # of items that can be show in the gallery on the screen at one time and add a few more (the gallery will need extra to show items on the left and right as you scroll thru it). I create an array of this size - the views get created as requested and placed in the cache. Use position % cachesize to figure out which cached view to return when getView is called.

Categories

Resources