Swipe between filtered images for android
Asked Answered
S

3

11

Essentially, I am re-asking this question but for implementing it on android.

I am trying to allow users to swipe between filters on a static image. The idea is that the image stays in place while the filter scrolls above it. Snapchat recently released a version which implements this feature. This video shows exactly what I'm trying to accomplish at 1:05.

I tried filling a list with the overlays and paging through it with the onFling and drawing with onDraw, but I lose the animations. Is there a way this can be done with ViewPager?

EDIT: As requested, I have provided my implementation for overlay view paging. It fills the viewpager with transparent png images which sits on top of an image view. Also, this code is in C#, as I am using Xamarin Android. It's fairly similar to Java for those unfamiliar with C#

...
static List<ImageView> overlayList = new List<ImageView>();
...

public class OverlayFragmentAdapter : FragmentPagerAdapter
{
    public OverlayFragmentAdapter(Android.Support.V4.App.FragmentManager fm) : base(fm)
    {

    }



    public override int Count
    {
        get { return 5; } //hardcoded temporarily 
    }

    public override Android.Support.V4.App.Fragment GetItem(int position)
    {
        return new OverlayFragment ();
    }
}
public class OverlayFragment : Android.Support.V4.App.Fragment
{
    public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {


        View view = inflater.Inflate (Resource.Layout.fragment_overlay, container, false);

        LinearLayout l1 = view.FindViewById<LinearLayout> (Resource.Id.overlay_container);

        ImageView im = new ImageView (Activity);

        im.SetImageResource (Resource.Drawable.Overlay); //Resource.Drawable.Overlay is a simple png transparency I created. R

        l1.AddView (im);
        overlayList.AddElement (im);
        return view;
    }
}

Activity Layout XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="bottom">
    <ImageView
        android:id="@+id/background_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <RelativeLayout <!-- This second layout is for buttons which I have omitted from this code -->
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:id="@+id/edit_layout">
        <android.support.v4.view.ViewPager
            android:id="@+id/overlay_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </RelativeLayout>
</RelativeLayout>

Fragment Overlay XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/overlay_container"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center" />

To briefly summarize: the viewpager sits on top of the first imageview, which acts as a background. The OnCreateView method creates an overlay fragment and an overlay imageview from a resource, which it puts inside the overlay_container layout. Saving the image (Which I have not posted as it is outside the scope of this question) is simple, all it does is create a background bitmap, an overlay bitmap, and uses a canvas to draw the overlay onto the background, then writes to file.

Stan answered 11/8, 2015 at 15:18 Comment(7)
Hi man, is this not possible by simply by making the view pagers background transparent and putting a image view behind it? It has 100 bounty, so I didn't feel like this is complete enough of an answer, but that's how i'd do it and I can't see why it wouldn't work.Uranology
@Uranology Hey, That's how I implemented overlays. I had an image behind, and the view pager has some overlays. However, I don't think that would work for image filters (black/white, sepia etc)Stan
ahh right, I only watched for a few seconds of it and saw the text flying over the top. good luck ;PUranology
You can try to do this with a ViewPager, but then you'll have to "compensate" the ViewPager slide animations with an extra animation: sliding in from the right, the RIGHT part of the image must show up first and after gradually expose the rest of image. My advise would be to put a new ImageView with the effect on top of the old one and write an effect which reveals the image in the proper way by sliding a progressbar. The you can automate the effect using a fling and handle all edge cases.Washin
@jmols That's an interesting idea. I will attempt that and post it as an answer if I am successful. Thank you!Stan
It would be nice if you could upload the codes you have tried. In your last sentence, it seems like you already achieved. And you only lost the animations part.Enact
@SH. added code :)Stan
K
1

I've worked on something similar myself.

For your specific use case, I would just use a canvas and alpha blend the filters across, on fling, as the top image.

To do the alpha blending, set the alpha paint of the first image (the original) to 255 and the alpha of the second one (the filter) to something like 128.

You just need a filter with the size of the image and then you shift the position of the second image as you draw it. That's it.

It's extremely fast and works a treat on very, very old devices.

Here's a sample implementation:

    Bitmap filter,  // the filter
        original, // our original
        tempBitmap; // the bitmap which holds the canvas results 
                    // and is then drawn to the imageView
    Canvas mCanvas; // our canvas

    int x = 0;   // The x coordinate of the filter. This variable will be manipulated
                        // in either onFling or onScroll.
    void draw() {
        // clear canvas
        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);    

        // setup paint
        paint0.setAlpha(255);   // the original needs to be fully visible
        paint1.setAlpha(128);   // the filter should be alpha blended into the original.

        // enable AA for paint
        // filter image
        paint1.setAntiAlias(true);
        paint1.setFlags(Paint.ANTI_ALIAS_FLAG);     // Apply AA to the image. Optional.
        paint1.setFlags(Paint.FILTER_BITMAP_FLAG);  // In case you scale your image, apple
                                                    // bilinear filtering. Optional.
        // original image
        paint0.setAntiAlias(true);
        paint0.setFlags(Paint.ANTI_ALIAS_FLAG);
        paint0.setFlags(Paint.FILTER_BITMAP_FLAG);

        // draw onto the canvas    
        mCanvas.save();                

        mCanvas.drawBitmap(original, 0,0,paint0);
        mCanvas.drawBitmap(filter, x,0,paint1);    

        mCanvas.restore();

        // set the new image
        imageView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap));
    }

And here are basic onFling and onScroll implementations.

private static final int SWIPE_DISTANCE_THRESHOLD = 125;
private static final int SWIPE_VELOCITY_THRESHOLD = 75;

// make sure to have implemented GestureDetector.OnGestureListener for these to work.
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
        float velocityY) {
    float distanceX = e2.getX() - e1.getX();
    float distanceY = e2.getY() - e1.getY();
    if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > 
            SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {

        // change picture to
        if (distanceX > 0) {
            // start left increment
        }
        else {  // the left
            // start right increment
        }
    }
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

    // checks if we're touching for more than 2f. I like to have this implemented, to prevent
    // jerky image motion, when not really moving my finger, but still touching. Optional.
    if (Math.abs(distanceY) > 2 || Math.abs(distanceX) > 2) {  
        if(Math.abs(distanceX) > Math.abs(distanceY)) {
            // move the filter left or right
        }
    }
}

Note: The onScroll/onFling implementations have pseudo code for the x adjustments, as those functions need to be tested. Someone who ends up implementing this in the future, can feel free to edit the answer and provide those functions.

Kozhikode answered 20/8, 2015 at 10:43 Comment(4)
That's a clever idea. I will try to implement it into my use case. On the subject of sample code, personally, I think that since this question has a relatively high bounty, some sample code should be provided for completeness. Also it's nicer for future readers :)Stan
I agree. I put together a sample implementation for reference.Kozhikode
Thanks! I will award you the bounty. I will play around with this and see if I can get proper x adjustments.Stan
Thank you! When you find an appropriate setting, it would be nice if you could edit it in. I wish you a lovely day!Kozhikode
Z
0

Take a look in the implementation of the method onDraw for the default Calendar app: DayView. There is onFling implementation and redrawing of the content (for example, calendar grid) according to the motion changes, which imitates fling.

Then you can use ColorFilter in onDraw according to the motion changes. It is very fast.

Alternatively, you can use ViewSwitcher with a list of filtered images (or somehow created a filtered images cache). To achieve the possibility of "drawing over the image", you can use ImageView and ViewSwitcher in RelativeLayout one above another and set the new filtered image in ImageView after the end of scrolling.

Zofiazoha answered 20/8, 2015 at 12:35 Comment(0)
C
-1

For this application i feel it would be most easy to use androids animation features and set the animations value to the filter you want. So you would make your own animation the changed filters iterating over your array.

Cormophyte answered 13/8, 2015 at 18:45 Comment(1)
Could you provide an example implementation?Stan

© 2022 - 2024 — McMap. All rights reserved.