修正了一个圆形ViewPager的动画 [英] Fix the Animation of a Circular ViewPager

查看:212
本文介绍了修正了一个圆形ViewPager的动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标

构建循环ViewPager。

第一个元素可以让你高峰的最后一个元素,并刷卡它,反之亦然。你应该能够在任意方向轻扫,直到永远。

现在这项工作完成之前,但这些问题不会对我实施工作。这里有一些供参考:

我是如何尝试解决问题的

我们将使用尺寸7数组作为一个例子。的内容如下:

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

当你在元素0,ViewPagers不要让你刷卡离开!多么可怕:(。为了解决这个问题,我加了1元至前和结束。

 [0] [1] [2] [3] [4] [5] [6] //原
[0] [1] [2] [3] [4] [5] [6] [7] [8] //新映射
 

在ViewPageAdapter要求(instantiateItem())元素0,我们返回元素7.当ViewPageAdapter要求元件8,我们归元1。

同样,在OnPageChangeListener在ViewPager,当onPageSelected被调用0,我们setCurrentItem(7),而当它被称为具有8我们setCurrentItem(1)。

这工作。

问题

当你刷卡的左侧从1到0,而我们setCurrentItem(7),这将动画一路向右6全屏幕。这不给一个圆形ViewPager的外观,它给appearence冲到用户请求的与他们的扫动动作,在相反方向的最后一个元素!

这是非常非常不和谐的。

我如何试图解决这个

我的第一个倾向是关闭平滑(即所有)动画。这是一个好一点,但它现在是断断续续,当您从最后一个元素移动到第一,反之亦然。

然后我做了我自己的滚轮。

<一个href="http://developer.android.com/reference/android/widget/Scroller.html">http://developer.android.com/reference/android/widget/Scroller.html

我发现的是,总有1调用startScroll()元素之间移动时,除了当我移动从1到7,7到1。

首先调用的是正确的动画方向和大小。

第二个电话是一切移动到右侧多页的动画。

这就是事情变得非常棘手。

我认为解决方案是只跳过第二个动画。所以我做了。什么情况是从1流畅的动画,以7 0打嗝。完善!但是,如果你刷卡,甚至是点击屏幕,你会突然(没有动画)在单元6!如果从7已经刷卡1,你实际上是在单元2。还有就是要setCurrentItem没有呼叫(2),甚至调用OnPageChangeListener表明您来到2在任意时间点。

但你没有真正在单元2,这是一种不错的。你还在为1元,但视图元素2将被显示。然后当你刷到左边,你去元件1.即使你真的在单元1已经..怎么样code,帮助明确的事情了:

动画坏了,但没有奇怪的副作用

  @覆盖
公共无效startScroll(INT STARTX,诠释startY,诠释DX,诠释DY,INT持续时间){
    super.startScroll(STARTX,startY,DX,DY,持续时间);
}
 

动画作品!但是,一切都是陌生和可怕的......

  @覆盖
公共无效startScroll(INT STARTX,诠释startY,诠释DX,诠释DY,INT持续时间){
    如果(DX&GT; 480 || DX&LT; -480){
    } 其他 {
        super.startScroll(STARTX,startY,DX,DY,持续时间);
    }
}
 

唯一的区别是,当第二个动画(大于480像素的屏幕宽度)被调用时,我们忽略它。

通过Android源$ C ​​$下滚轮看完后,我发现startScroll不启动滚动东西。它设置所有要滚动的数据,但不启动任何东西。

我的预感

当你做圆形的动作(1至7或7比1),有两个调用startScroll()。我认为有些事情在两个电话之间引起的问题。

  1. 用户卷轴从元件1至元件7使从0跳转到7这应该动画到左侧。
  2. startScroll()被调用表示动画短片左侧。
  3. 事情发生,让我哭PROBABLY我觉得
  4. startScroll()被调用表示动画的右侧
  5. 龙动画向右发生。

如果我注释掉4,然后5成为短正确的动画到左侧,东西发疯

摘要

我实现一个循环ViewPager的作品,但动画被打破。当试图修复动画,它打破了ViewPager的功能。我目前纺纱我的车轮试图找出如何使它工作。帮我! :)

如果有什么不清楚,请在下面评论,我会澄清。我知道我是不是很precise有怎样的东西被破坏。这是很难形容,因为它甚至不是清楚我所看到的在屏幕上。如果我的解释是一个问题,我可以在它的工作,让我知道!

干杯, Coltin

code

这code稍微修改,以使自己更具可读性,虽然功能相同,我目前的code迭代。

OnPageChangeListener.onPageSelected

  @覆盖
公共无效onPageSelected(INT _position){
    布尔动画= TRUE;
    如果(_position&小于1){
        //刷卡离开了过去的第一要素,去元件(9  -  2)= 7
        setCurrentItem(getAdapter()getCount将() -  2,动画);
    }否则如果(_position&GT; = getAdapter()getCount将() -  1){
        //刷卡右过去的最后一个元素
        setCurrentItem(1,动画);
    }
}
 

CircularScroller.startScroll

  @覆盖
公共无效startScroll(INT _startX,诠释_startY,诠释_dx,诠释_dy,诠释_duration){
    // 480是屏幕的宽度
    如果(DX&GT; 480 || DX&LT; -480){
        //在该块无为显示正确的动画,
        //但它会导致上述问题的

        //取消注释做的大卷轴!
        // super.startScroll(_startX,_startY,_dx,_dy,_duration);

        // lastDX是试图重置卷轴是previous
        //正确的滚动距离;它没有任何效果
        // super.startScroll(_startX,_startY,lastDx,_dy,_duration);
    } 其他 {
        lastDx = _dx;
        super.startScroll(_startX,_startY,_dx,_dy,_duration);
    }
}
 

CircularViewPageAdapter.CircularViewPageAdapter

 私有静态最终诠释m_Length = 7; //对于我们的例子中只
私有静态语境m_Context;
私人布尔[]创建= NULL; //不是最好的做法..

公共CircularViewPageAdapter(上下文_context){
    m_Context = _context;
    创建=新的布尔[m​​_Length]
    的for(int i = 0; I&LT; m_Length;我++){
        //所以我们没有创造的东西多次
        //我认为这是导致我的问题,但它不是
        创建[我] = FALSE;
    }
}
 

CircularViewPageAdapter.getCount

  @覆盖
公众诠释getCount将(){
    返回m_Length + 2;
}
 

CircularViewPageAdapter.instantiateItem

  @覆盖
公共对象instantiateItem(查看_collection,INT _position){

    INT virtualPosition = getVirtualPosition(_position);
    如果(创建[virtualPosition  -  1]){
        返回null;
    }

    TextView的电视=新的TextView(m_Context);
    //第一种观点元1与标签0! :)
    tv.setText(卓悦,MERCI!+(virtualPosition  -  1));
    tv.setTextColor(Color.WHITE);
    tv.setTextSize(30);

    ((ViewPager)_collection).addView(电视,0);

    返回电视;
}
 

CircularViewPageAdapter.destroyItem

  @覆盖
公共无效destroyItem(ViewGroup中的容器,INT位置,对象视图){
    ViewPager viewPager =(ViewPager)容器中;
    //如果虚拟距离是距离2而去,它应该被销毁。
    //如果它不是直观的,为什么是这样的话,请在下面评论
    //我要澄清
    INT virtualDistance = getVirtualDistance(viewPager.getCurrentItem(),getVirtualPosition(位置));
    如果((virtualDistance == 2)||((m_Length  -  virtualDistance)== 2)){
        ((ViewPager)容器).removeView((视图));
        创建[getVirtualPosition(位置) -  1] = FALSE;
    }
}
 

解决方案

我认为最好的可行的办法是,而不是使用正常的名单有包装的列表,当获取(POS)的方法执行,以获取该对象以创建视​​图,你让这样的事情获取(POS%numberOfViews),当它要求你把该名单是List的大小<强> Integer.MAX_VALUE的并启动您的列表,中间的,所以你可以说,大部分是不可能有一个错误,除非他们真的刷到同一侧,直至到达列表的末尾。我会尝试发布的概念以后这种弱证明,如果时间允许我这样做。

编辑:

我已经试过这片code,我所知道的是每个视图中显示一个简单的文本框,但事实是,它完美的作品,它可能会比较慢取决于意见的总量,但概念验证在这儿。我所做的是,在 MAX_NUMBER_VIEWS 再presents是什么时代的最大数量用户可以完全放弃,他被停止了。正如你可以看到,我开始了viewpager在我的数组的长度,这样会出现第二次让你有一圈额外的左,右,但是,因为你需要它,你可以改变它。我希望我没有得到更多的负分了一个解决方案,实际上做的工作。

活动:

 寻呼机=(ViewPager)findViewById(R.id.viewpager);
的String []的文章= {第1条,第2条,第3条,第4条};
pager.setAdapter(新ViewPagerAdapter(此,文章));
pager.setCurrentItem(articles.length);
 

适配器:

 公共类ViewPagerAdapter扩展PagerAdapter {

私人上下文CTX;
私有String []的文章;
私人最终诠释MAX_NUMBER_VIEWS = 3;

公共ViewPagerAdapter(上下文CTX,字符串[]的文章){
    this.ctx = CTX;
    this.articles = articles.clone();
}

@覆盖
公众诠释getCount将(){
    返回articles.length * this.MAX_NUMBER_VIEWS;
}

@覆盖
公共对象instantiateItem(ViewGroup中的容器,INT位置){
    TextView的视图=新的TextView(CTX);
    view.setLayoutParams(新的LayoutParams(LayoutParams.MATCH_PARENT,
                                          LayoutParams.MATCH_PARENT));
    INT realPosition =位置%articles.length;
    view.setText(this.articles [realPosition]);
    ((ViewPager)容器).addView(视图);
    返回查看;
}

@覆盖
公共无效destroyItem(ViewGroup中的容器,INT位置,Object对象){
    ((ViewPager)容器).removeView((查看)对象);
}

@覆盖
公共布尔isViewFromObject(查看视图,Object对象){
    返回查看==((查看)对象);
}

@覆盖
公共Parcelable saveState和(){
    返回null;
}

}
 

Goal

Build a Circular ViewPager.

The first element lets you peak to the last element and swipe to it, and vice versa. You should be able to swipe in either direction forever.

Now this has been accomplished before, but these questions do not work for my implementation. Here are a few for reference:

How I Tried to Solve the Problem

We will use an array of size 7 as an example. The elements are as follows:

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

When you are at element 0, ViewPagers do not let you swipe left! How terrible :(. To get around this, I added 1 element to the front and end.

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

When the ViewPageAdapter asks for (instantiateItem()) element 0, we return element 7. When the ViewPageAdapter asks for element 8 we return element 1.

Likewise in the OnPageChangeListener in the ViewPager, when the onPageSelected is called with 0, we setCurrentItem(7), and when it's called with 8 we setCurrentItem(1).

This works.

The Problem

When you swipe to the left from 1 to 0, and we setCurrentItem(7), it will animate all the way to right by 6 full screens. This doesn't give the appearance of a circular ViewPager, it gives the appearence rushing to the last element in the opposite direction the user requested with their swipe motion!

This is very very jarring.

How I Tried to Solve This

My first inclination was to turn off smooth (ie, all) animations. It's a bit better, but it's now choppy when you move from the last element to the first and vice versa.

I then made my own Scroller.

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

What I found was that there is always 1 call to startScroll() when moving between elements, except when I move from 1 to 7 and 7 to 1.

The first call is the correct animation in direction and amount.

The second call is the animation that moves everything to the right by multiple pages.

This is where things got really tricky.

I thought the solution was to just skip the second animation. So I did. What happens is a smooth animation from 1 to 7 with 0 hiccups. Perfect! However, if you swipe, or even tap the screen, you are suddenly (with no animation) at element 6! If you had swiped from 7 to 1, you'll actually be at element 2. There is no call to setCurrentItem(2) or even a call to the OnPageChangeListener indicating that you arrived at 2 at any point in time.

But you're not actually at element 2, which is kind of good. You are still at element 1, but the view for element 2 will be shown. And then when you swipe to the left, you go to element 1. Even though you were really at element 1 already.. How about some code to help clear things up:

Animation is broken, but no weird side effects

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

Animation works! But everything is strange and scary...

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

The ONLY difference is that when the second animation (bigger than the width of the 480 pixel screen) is called, we ignore it.

After reading through the Android Source code for Scroller, I found that startScroll does not start scrolling anything. It sets up all the data to be scrolled, but doesn't initiate anything.

My Hunch

When you do the circular action (1 to 7 or 7 to 1), there are two calls to startScroll(). I think something in between the two calls is causing an issue.

  1. User scrolls from element 1 to element 7 causing a jump from 0 to 7. This should animate to the left.
  2. startScroll() is called indicating a short animation to the left.
  3. STUFF HAPPENS THAT MAKES ME CRY PROBABLY I THINK
  4. startScroll() is called indicating a long animation to the right.
  5. Long animation to the right occurs.

If I comment out 4, then 5 becomes "Short correct animation to the left, things go crazy"

Summary

My implementation of a Circular ViewPager works, but the animation is broken. Upon trying to fix the animation, it breaks the functionality of the ViewPager. I am currently spinning my wheels trying to figure out how to make it work. Help me! :)

If anything is unclear please comment below and I will clarify. I realize I was not very precise with how things are broken. It's difficult to describe because it's not even clear what I'm seeing on the screen. If my explanation is an issue I can work on it, let me know!

Cheers, Coltin

Code

This code is slightly modified to make it more readable on its own, though the functionality is identical to my current iteration of the code.

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

解决方案

I think the best doable approach would be instead of using a normal list to have a wrapper to the List that when the get(pos) method is executed to obtain the object to create the view, you make something like this get(pos % numberOfViews) and when it ask for the size of the List you put that the List is Integer.MAX_VALUE and you start your List in the middle of it so you can say that is mostly impossible to have an error, unless they actually swipe to the same side until the reach the end of the List. I will try to post a proof of concept later this weak if the time allows me to do so.

EDIT:

I have tried this piece of code, i know is a simple textbox shown on each view, but the fact is that it works perfectly, it might be slower depending on the total amount of views but the proof of concept is here. What i have done is that the MAX_NUMBER_VIEWS represents what is the maximum numbers of times a user can completely give before he is stopped. and as you can see i started the viewpager at the length of my array so that would be the second time it appears so you have one turn extra to the left and right but you can change it as you need it. I hope i do not get more negative points for a solution that in fact does work.

ACTIVITY:

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

ADAPTER:

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

}

这篇关于修正了一个圆形ViewPager的动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆