Проведите между отфильтрованными изображениями для 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" />
Кратко резюмируя: viewpager располагается поверх первого изображения, которое выступает в качестве фона. Метод OnCreateView создает фрагмент наложения и вид изображения наложения из ресурса, который он помещает в макет overlay_container. Сохранение изображения (которое я не опубликовал, поскольку оно выходит за рамки этого вопроса) является простым, все, что он делает, это создает фоновое растровое изображение, растровое растровое изображение и использует холст для наложения наложения на фон, а затем пишет в файл.
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, так как эти функции должны быть протестированы. Кто-то, кто в конечном итоге реализует это в будущем, может свободно редактировать ответ и предоставлять эти функции.
Посмотрите на реализацию метода onDraw
для календарного приложения по умолчанию: DayView. Есть onFling
Реализация и перерисовка контента (например, календарной сетки) в соответствии с изменениями движения, которые имитируют бросок.
Тогда вы можете использовать ColorFilter в onDraw
в соответствии с изменениями движения. Это очень быстро.
Кроме того, вы можете использовать ViewSwitcher со списком отфильтрованных изображений (или каким-то образом создать кэш отфильтрованных изображений). Чтобы добиться возможности "рисования поверх изображения", вы можете использовать ImageView
а также ViewSwitcher
в RelativeLayout
один над другим и установите новое отфильтрованное изображение в ImageView
после окончания прокрутки.
Я считаю, что для этого приложения было бы проще всего использовать функции анимации для Android и установить значение анимации для фильтра, который вы хотите. Таким образом, вы бы сделали свою собственную анимацию измененными фильтрами, перебирающими ваш массив.