15

ゴール

Circular ViewPager を構築します。

最初の要素を使用すると、最後の要素に到達してスワイプできます。その逆も同様です。どちらの方向にも永遠にスワイプできるはずです。

これは以前に達成されましたが、これらの質問は私の実装では機能しません。参照用にいくつかを次に示します。

問題を解決する方法

例として、サイズ 7 の配列を使用します。要素は次のとおりです。

[0][1][2][3][4][5][6]

要素 0 にいるとき、ViewPagers では左にスワイプできません! なんてひどい:(。これを回避するために、フロントとエンドに1つの要素を追加しました。

   [0][1][2][3][4][5][6]      // Original
[0][1][2][3][4][5][6][7][8]   // New mapping

ViewPageAdapter が (instantiateItem()) 要素 0 を要求すると、要素 7 を返します。ViewPageAdapter が要素 8 を要求すると、要素 1 を返します。

同様に、ViewPager の OnPageChangeListener では、onPageSelected が 0 で呼び出された場合は CurrentItem(7) を設定し、8 で呼び出された場合は CurrentItem(1) を設定します。

これは機能します。

問題

1 から 0 まで左にスワイプし、CurrentItem(7) を設定すると、6 画面分の右端までアニメーション化されます。これは、円形の ViewPager の外観を与えるのではなく、ユーザーがスワイプ モーションで要求した反対方向の最後の要素に急いでいる外観を与えます!

これは非常に耳障りです。

これを解決しようとした方法

私の最初の傾向は、滑らかな (つまり、すべての) アニメーションをオフにすることでした。少しは良くなりましたが、最後の要素から最初の要素に、またはその逆に移動すると途切れます。

次に、独自のスクローラーを作成しました。

http://developer.android.com/reference/android/widget/Scroller.html

私が見つけたのは、1 から 7 および 7 から 1 に移動する場合を除いて、要素間を移動するときに startScroll() への呼び出しが常に 1 回あることです。

最初の呼び出しは、方向と量の正しいアニメーションです。

2 番目の呼び出しは、すべてを複数ページ右に移動するアニメーションです。

これは、物事が本当にトリッキーになった場所です。

解決策は、2 番目のアニメーションをスキップすることだと思いました。だから私はしました。発生するのは、1 から 7 までの滑らかなアニメーションで、しゃっくりは 0 です。完全!ただし、画面をスワイプまたはタップすると、突然 (アニメーションなしで) 要素 6 になります! 7 から 1 にスワイプした場合、実際には要素 2 になります。setCurrentItem(2) への呼び出しはなく、任意の時点で 2 に到達したことを示す OnPageChangeListener への呼び出しさえありません。

しかし、あなたは実際には要素 2 にいるわけではありません。まだ要素 1 にいますが、要素 2 のビューが表示されます。そして、左にスワイプすると、要素 1 に移動します。実際には既に要素 1 にいたのに.. 物事を解決するのに役立つコードはどうですか:

アニメーションは壊れていますが、奇妙な副作用はありません

@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    super.startScroll(startX, startY, dx, dy, duration);
}

アニメーション作品!でも全てが奇妙で怖い…

@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    if (dx > 480 || dx < -480) {
    } else {
        super.startScroll(startX, startY, dx, dy, duration);
    }
}

唯一の違いは、2 番目のアニメーション (480 ピクセルの画面の幅よりも大きい) が呼び出されたときに、それを無視することです。

Scroller の Android ソース コードを読んだ後、startScroll がスクロールを開始しないことがわかりました。スクロールするすべてのデータを設定しますが、何も開始しません。

私の予感

循環アクション (1 対 7 または 7 対 1) を実行する場合、startScroll() への呼び出しが 2 つあります。2 つの呼び出しの間に何か問題が発生していると思います。

  1. ユーザーが要素 1 から要素 7 にスクロールすると、0 から 7 にジャンプします。これは左にアニメーション化する必要があります。
  2. startScroll() が呼び出され、左側に短いアニメーションが表示されます。
  3. たぶん泣くようなことが起こる
  4. startScroll() が呼び出され、右側に長いアニメーションが表示されます。
  5. 右への長いアニメーションが発生します。

4 をコメントアウトすると、5 は「左の短い正しいアニメーション、おかしくなりました」になります。

概要

Circular ViewPager の実装は機能しますが、アニメーションが壊れています。アニメーションを修正しようとすると、ViewPager の機能が壊れます。私は現在、それを機能させる方法を見つけようとして車輪を回転させています。助けて!:)

不明な点がある場合は、以下にコメントしてください。明確にします。物事がどのように壊れているかについて、私はあまり正確ではなかったことに気づきました。画面に何が表示されているかさえ明確ではないため、説明するのは困難です。私の説明が私が取り組むことができる問題である場合は、お知らせください!

乾杯、コルティン

コード

このコードは、コード自体を読みやすくするためにわずかに変更されていますが、機能は現在のコードの繰り返しと同じです。

OnPageChangeListener.onPageSelected

@Override
public void onPageSelected(int _position) {
    boolean animate = true;
    if (_position < 1) {
        // Swiping left past the first element, go to element (9 - 2)=7
        setCurrentItem(getAdapter().getCount() - 2, animate);
    } else if (_position >= getAdapter().getCount() - 1) {
        // Swiping right past the last element
        setCurrentItem(1, animate);
    }
}

CircularScroller.startScroll

@Override
public void startScroll(int _startX, int _startY, int _dx, int _dy, int _duration) {
    // 480 is the width of the screen
    if (dx > 480 || dx < -480) {
        // Doing nothing in this block shows the correct animation,
        // but it causes the issues mentioned above

        // Uncomment to do the big scroll!
        // super.startScroll(_startX, _startY, _dx, _dy, _duration);

        // lastDX was to attempt to reset the scroll to be the previous
        // correct scroll distance; it had no effect
        // super.startScroll(_startX, _startY, lastDx, _dy, _duration);
    } else {
        lastDx = _dx;
        super.startScroll(_startX, _startY, _dx, _dy, _duration);
    }
}

CircularViewPageAdapter.CircularViewPageAdapter

private static final int m_Length = 7; // For our example only
private static Context m_Context;
private boolean[] created = null; // Not the best practice..

public CircularViewPageAdapter(Context _context) {
    m_Context = _context;
    created = new boolean[m_Length];
    for (int i = 0; i < m_Length; i++) {
        // So that we do not create things multiple times
        // I thought this was causing my issues, but it was not
        created[i] = false;
    }
}

CircularViewPageAdapter.getCount

@Override
public int getCount() {
    return m_Length + 2;
}

CircularViewPageAdapter.instantiateItem

@Override
public Object instantiateItem(View _collection, int _position) {

    int virtualPosition = getVirtualPosition(_position);
    if (created[virtualPosition - 1]) {
        return null;
    }

    TextView tv = new TextView(m_Context);
    // The first view is element 1 with label 0! :)
    tv.setText("Bonjour, merci! " + (virtualPosition - 1));
    tv.setTextColor(Color.WHITE);
    tv.setTextSize(30);

    ((ViewPager) _collection).addView(tv, 0);

    return tv;
}

CircularViewPageAdapter.destroyItem

@Override
public void destroyItem(ViewGroup container, int position, Object view) {
    ViewPager viewPager = (ViewPager) container;
    // If the virtual distance is distance 2 away, it should be destroyed.
    // If it's not intuitive why this is the case, please comment below
    // and I will clarify
    int virtualDistance = getVirtualDistance(viewPager.getCurrentItem(), getVirtualPosition(position));
    if ((virtualDistance == 2) || ((m_Length - virtualDistance) == 2)) {
        ((ViewPager) container).removeView((View) view);
        created[getVirtualPosition(position) - 1] = false;
    }
}
4

1 に答える 1

1

ビューを作成するオブジェクトを取得するためにget(pos)メソッドが実行されるときに、通常のリストを使用して List へのラッパーを使用する代わりに、このget(pos % numberOfViews )そして、リストがInteger.MAX_VALUEであることを入力したリストのサイズを尋ねるとき、リストの途中でリストを開始するので、実際に同じものにスワイプしない限り、エラーが発生することはほとんど不可能であると言えますリストの最後に到達するまでサイド。時間が許せば、この弱いコンセプトの後で投稿しようと思います。

編集:

このコードを試してみましたが、各ビューに表示される単純なテキストボックスであることはわかっていますが、実際には完全に機能し、ビューの合計量によっては遅くなる可能性がありますが、概念実証はここにあります. 私が行ったことは、MAX_NUMBER_VIEWSが、ユーザーが停止する前に完全に与えることができる最大回数を表すことです。ご覧のとおり、配列の長さでビューページャーを開始したので、表示されるのは2回目なので、左右に1回転余分ですが、必要に応じて変更できます。実際に機能するソリューションに対して、これ以上否定的な点が得られないことを願っています。

アクティビティ:

pager = (ViewPager)findViewById(R.id.viewpager);
String[] articles = {"ARTICLE 1","ARTICLE 2","ARTICLE 3","ARTICLE 4"};
pager.setAdapter(new ViewPagerAdapter(this, articles));
pager.setCurrentItem(articles.length);

アダプタ:

public class ViewPagerAdapter extends PagerAdapter {

private Context ctx;
private String[] articles;
private final int MAX_NUMBER_VIEWS = 3;

public ViewPagerAdapter(Context ctx, String[] articles) {
    this.ctx = ctx;
    this.articles = articles.clone();
}

@Override
public int getCount() {
    return articles.length * this.MAX_NUMBER_VIEWS;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    TextView view = new TextView(ctx);
    view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                          LayoutParams.MATCH_PARENT));
    int realPosition = position % articles.length;
    view.setText(this.articles[realPosition]);
    ((ViewPager) container).addView(view);
    return view;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    ((ViewPager) container).removeView((View) object);
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return view == ((View) object);
}

@Override
public Parcelable saveState() {
    return null;
}

}
于 2012-07-27T15:51:09.537 に答える