Проведите пальцем между отфильтрованными изображениями для Android

По сути, я повторно задаю этот вопрос, но для его реализации на Android.

Я пытаюсь разрешить пользователям переключаться между фильтрами статического изображения. Идея состоит в том, что изображение остается на месте, пока фильтр прокручивается над ним. Snapchat недавно выпустил версию, в которой реализована эта функция. В этом видео показано, чего я пытаюсь добиться на 1:05.

Я попытался заполнить список наложениями и пролистать его с помощью onFling и нарисовать с помощью onDraw, но я потерял анимацию. Есть ли способ сделать это с помощью ViewPager?

РЕДАКТИРОВАТЬ: В соответствии с запросом я предоставил свою реализацию для пейджинга представления оверлея. Он заполняет окно просмотра прозрачными изображениями в формате png, которые располагаются поверх изображения. Кроме того, этот код написан на C#, так как я использую Xamarin Android. Это очень похоже на Java для тех, кто не знаком с 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;
    }
}

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>

Фрагмент наложения 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" />

Подведем краткий итог: просмотрщик располагается поверх первого изображения, которое действует как фон. Метод OnCreateView создает фрагмент наложения и представление изображения наложения из ресурса, который он помещает в макет overlay_container. Сохранение изображения (которое я не опубликовал, поскольку оно выходит за рамки этого вопроса) простое, все, что он делает, это создает фоновое растровое изображение, растровое изображение наложения и использует холст для рисования наложения на фон, а затем записывает в файл.


person RN_    schedule 11.08.2015    source источник
comment
Привет, чувак, разве это невозможно, просто сделав фон пейджера прозрачным и поместив за ним изображение? У него 100 наград, поэтому я не чувствовал, что это достаточно полный ответ, но я бы сделал это так, и я не понимаю, почему это не сработает.   -  person Ben    schedule 13.08.2015
comment
@Ben Эй, вот как я реализовал наложения. У меня было изображение позади, а на пейджере есть несколько наложений. Однако я не думаю, что это сработает для фильтров изображений (черно-белое, сепия и т. д.).   -  person RN_    schedule 13.08.2015
comment
ааа, верно, я наблюдал за этим всего несколько секунд и увидел, как текст пролетел сверху. удачи ;Р   -  person Ben    schedule 14.08.2015
comment
Вы можете попытаться сделать это с помощью ViewPager, но тогда вам придется компенсировать анимацию слайдов ViewPager дополнительной анимацией: скользя справа, ПРАВАЯ часть изображения должна отображаться первой, а затем постепенно обнажать остальную часть. изображение. Я бы посоветовал поместить новый ImageView с эффектом поверх старого и написать эффект, который правильно показывает изображение, сдвигая индикатор выполнения. Вы можете автоматизировать эффект с помощью броска и обрабатывать все крайние случаи.   -  person Jeroen Mols    schedule 18.08.2015
comment
@jmols Это интересная идея. Я попытаюсь это сделать и опубликую как ответ, если мне это удастся. Благодарю вас!   -  person RN_    schedule 18.08.2015
comment
Было бы неплохо, если бы вы могли загрузить коды, которые вы пробовали. В вашем последнем предложении кажется, что вы уже достигли. И вы потеряли только часть анимации.   -  person Swan    schedule 20.08.2015
comment
@Ш. добавил код :)   -  person RN_    schedule 20.08.2015


Ответы (3)


Я сам работал над чем-то подобным.

Для вашего конкретного случая использования я бы просто использовал холст и альфа-смешение фильтров поперек, при броске, в качестве верхнего изображения.

Чтобы сделать альфа-смешение, установите альфа-краску первого изображения (оригинала) на 255, а альфа-канал второго (фильтра) примерно на 128.

Вам просто нужен фильтр с размером изображения, а затем вы меняете положение второго изображения, когда рисуете его. Вот и все.

Это очень быстро и отлично работает на очень, очень старых устройствах.

Вот пример реализации:

    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));
    }

А вот основные onFling и onScroll.

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
        }
    }
}

Примечание. Реализации onScroll/onFling содержат псевдокод для настройки x, поскольку эти функции необходимо протестировать. Тот, кто в конечном итоге реализует это в будущем, может свободно редактировать ответ и предоставлять эти функции.

person Sipty    schedule 20.08.2015
comment
Это умная идея. Я постараюсь внедрить это в свой вариант использования. Что касается примера кода, лично я считаю, что, поскольку этот вопрос имеет относительно высокую награду, для полноты следует предоставить некоторый пример кода. Также это приятнее для будущих читателей :) - person RN_; 20.08.2015
comment
Я согласен. Я собрал пример реализации для справки. - person Sipty; 20.08.2015
comment
Спасибо! Я награжу тебя наградой. Я поиграю с этим и посмотрю, смогу ли я получить правильные настройки x. - person RN_; 20.08.2015
comment
Благодарю вас! Когда вы найдете подходящую настройку, было бы неплохо, если бы вы могли ее отредактировать. Желаю вам прекрасного дня! - person Sipty; 20.08.2015

Взгляните на реализацию метода onDraw для приложения Календарь по умолчанию: DayView. Присутствует onFling реализация и перерисовка содержимого (например, календарной сетки) в соответствии с изменением движения, что имитирует флинг.

Затем вы можете использовать ColorFilter в onDraw в соответствии с изменения движения. Это очень быстро.

Кроме того, вы можете использовать ViewSwitcher со списком отфильтрованные изображения (или каким-то образом созданный кеш отфильтрованных изображений). Чтобы добиться возможности «рисования поверх изображения», можно использовать ImageView и ViewSwitcher в RelativeLayout одно над другим и установить новое отфильтрованное изображение в ImageView после окончания прокрутки.

person Warabei    schedule 20.08.2015

Я считаю, что для этого приложения было бы проще всего использовать функции анимации Android и установить значение анимации на нужный фильтр. Таким образом, вы бы сделали свою собственную анимацию измененных фильтров, повторяющих ваш массив.

person WIll    schedule 13.08.2015
comment
Не могли бы вы привести пример реализации? - person RN_; 13.08.2015