無限循化Banner, ViewPager的兩種實現方法

無限循環ViewPager效果圖(設置了PageTransFormer,實現了畫廊效果的):

方法一:

在源數據列表頭和尾分別補上兩個元素, 比如我們源數據列表順序是[1,2,3], 真正使用到ViewPager中的列表變成[2,3,1,2,3,1,2]

源代碼如下:

首先是xml文件, 爲了實現"畫廊"效果, ViewPager不能match_parent, 且在ViewPager父佈局要設置 clipChildren="false"

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorWhite"
    android:clipChildren="false"
    tools:context="com.test.demoone.banner.BannerActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerHorizontal="true"
        android:background="@color/colorSeparate"
        android:clipChildren="false" />

    <View
        android:id="@+id/viewPagerHelper"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerHorizontal="true"
        android:clickable="true"
        android:focusable="true"
        android:visibility="gone" />

    <Button
        android:id="@+id/buttonChangeData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/viewPager"
        android:onClick="changeData"
        android:text="change data" />

    <Button
        android:id="@+id/buttonTurnNext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/buttonChangeData"
        android:onClick="turnNext"
        android:text="turn next" />

</RelativeLayout>

接下來是Activity中的代碼:

public class BannerActivity extends AppCompatActivity {

    private BannerAdapter adapter;
    private ViewPager viewPager;
    private List<String> dataList;
    private ZoomOutTransformer zoomOutTransformer;
    private boolean needToChangePosition = false;
    private View viewPagerHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);


        dataList = new ArrayList<>();
        for (int i = 0; i < 1; i++) {
            dataList.add("原 " + i);
        }

        viewPagerHelper = findViewById(R.id.viewPagerHelper);
        viewPager = findViewById(R.id.viewPager);
        ViewPagerScroller scroller = new ViewPagerScroller(this);
        // 時間越長,速度越慢。
        scroller.setScrollDuration(600);
        scroller.initViewPagerScroll(viewPager);

        adapter = new BannerAdapter(this, dataList);
        zoomOutTransformer = new ZoomOutTransformer();
        viewPager.setPageTransformer(true, zoomOutTransformer);
        viewPager.setOffscreenPageLimit(2);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem(adapter.getFirstPosition());

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {

                if (dataList.size() < 2) return;

                if (position == 1) {
                    needToChangePosition = true;
                    if (viewPagerHelper.getVisibility() == View.GONE)
                        viewPagerHelper.setVisibility(View.VISIBLE);
                } else if (position == dataList.size() + 2) {
                    needToChangePosition = true;
                    if (viewPagerHelper.getVisibility() == View.GONE)
                        viewPagerHelper.setVisibility(View.VISIBLE);
                } else {
                    needToChangePosition = false;
                    if (viewPagerHelper.getVisibility() == View.VISIBLE)
                        viewPagerHelper.setVisibility(View.GONE);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

                if (dataList.size() < 2) return;

                if (state == 0 && needToChangePosition) {
                    Log.d(">>>>>", "滑動結束");
                    if (viewPager.getCurrentItem() == 1) {
                        viewPager.setCurrentItem(dataList.size() + 1, false);
                        updateNearView();
                    } else if (viewPager.getCurrentItem() == dataList.size() + 2) {
                        viewPager.setCurrentItem(2, false);
                        updateNearView();
                    } else {
                        return;
                    }

                    if (viewPagerHelper.getVisibility() == View.VISIBLE) {
                        viewPagerHelper.setVisibility(View.GONE);
                    }
                }
            }
        });

    }

    public void updateNearView() {
        Log.d(">>>>>>", "getChildCount = " + viewPager.getChildCount());

        for (int i = 0; i < 5; i++) {
            View childView = viewPager.getChildAt(i);
            childView.setAlpha(0.5f);
            childView.setScaleX(0.9f);
            childView.setScaleY(0.9f);
        }

        View primaryItem = adapter.getPrimaryItem();
        primaryItem.setAlpha(1.0f);
        primaryItem.setScaleX(1.0f);
        primaryItem.setScaleY(1.0f);
    }

    public void changeData(View view) {
        dataList.clear();
        for (int i = 0; i < 10; i++) {
            dataList.add("更新 " + i);
        }
        adapter.setData(dataList);

        viewPager.setCurrentItem(adapter.getFirstPosition(), false);
        updateNearView();
    }

    public void turnNext(View view) {
        if (dataList.size() < 2) return;
        viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
    }
}

數據在Adapter中經過了處理, 頭和尾部各補充了兩個數據:

public class BannerAdapter extends PagerAdapter {

    private Context context;
    private List<String> dataList = new ArrayList<>();
    private boolean notify;
    private View primaryView;

    public BannerAdapter(Context context) {
        this.context = context;
    }

    public BannerAdapter(Context context, List<String> dataList) {
        this.context = context;
        inflateDataList(dataList);
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, final int position) {
        View bannerView = LayoutInflater.from(context).inflate(R.layout.banner_view, null);
        ImageView imageView = bannerView.findViewById(R.id.imageView);
        TextView textView = bannerView.findViewById(R.id.textView);
        textView.setText(dataList.get(position % getRealCount()));
        container.addView(bannerView);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, dataList.get(position), Toast.LENGTH_SHORT).show();
            }
        });
        return bannerView;
    }

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);

        primaryView = (View) object;
    }

    public View getPrimaryItem() {
        return primaryView;
    }

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

    @Override
    public int getCount() {
        return dataList.size();
    }

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

    public int getRealCount() {
        if (dataList.size() < 2) {
            return dataList.size();
        } else {
            return dataList.size() - 4;
        }
    }

    public int getFirstPosition() {
        if (dataList.size() < 2) {
            return dataList.size();
        } else {
            return 2;
        }
    }

    public void setData(List<String> dataList) {
        inflateDataList(dataList);

        this.notify = true;
        notifyDataSetChanged();
        this.notify = false;
    }

    private void inflateDataList(List<String> dataList) {
        this.dataList.clear();
        if (dataList.size() < 2) {
            this.dataList.addAll(dataList);
        } else {
            this.dataList.add("data " + (dataList.size() - 2));
            this.dataList.add("data " + (dataList.size() - 1));
            this.dataList.addAll(dataList);
            this.dataList.add("data " + 0);
            this.dataList.add("data " + 1);
        }
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        if (notify) {
            return POSITION_NONE;
        } else {
            return super.getItemPosition(object);
        }
    }
}

最後, 個人感覺使用viewPager.setCurrentItem翻頁的時候速度特別快, 於是使用反射的方法修改了翻頁動畫的時長, 代碼如下:

public class ViewPagerScroller extends Scroller {
    private int mDuration = 2000;/*default duration time*/

    /**
     * Set custom duration time.
     *
     * @param duration duration
     */
    public void setScrollDuration(int duration) {
        mDuration = duration;
    }

    /**
     * Get duration time.
     *
     * @return duration
     */
    public int getmDuration() {
        return mDuration;
    }

    public ViewPagerScroller(Context context) {
        super(context);
    }

    public ViewPagerScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
        super(context, interpolator, flywheel);
    }

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

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        //此處必須重寫,網上有些資料裏只重寫了上面那個,不知道他們的是怎麼工作的,我實際測試時行不通的。
        super.startScroll(startX, startY, dx, dy, mDuration);
    }

    public void initViewPagerScroll(ViewPager pager) {
        try {
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            field.set(pager, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方式一  完活兒

 

方法二: Adapter中使用Integer.MAX_VALUE

佈局文件和方法一相同

Activity中的邏輯代碼如下:(最最最重要的是changeData方法中的反射, 不然後會導致ANR)

public class BannerActivity extends AppCompatActivity {

    private BannerAdapter adapter;
    private ViewPager viewPager;
    private List<String> dataList;
    private ZoomOutTransformer zoomOutTransformer;
    private boolean needToChangePosition = false;
    private View viewPagerHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);


        dataList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            dataList.add("原 " + i);
        }

        viewPagerHelper = findViewById(R.id.viewPagerHelper);
        viewPager = findViewById(R.id.viewPager);

        ViewPagerScroller scroller = new ViewPagerScroller(this);
        // 時間越長,速度越慢。
        scroller.setScrollDuration(600);
        scroller.initViewPagerScroll(viewPager);

        adapter = new BannerAdapter(this, dataList);
        zoomOutTransformer = new ZoomOutTransformer();
        viewPager.setPageTransformer(true, zoomOutTransformer);
        viewPager.setOffscreenPageLimit(2);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem(adapter.getFirstPosition());
    }

    public void updateNearView() {
        Log.d(">>>>>>", "getChildCount = " + viewPager.getChildCount());

        for (int i = 0; i < 5; i++) {
            View childView = viewPager.getChildAt(i);
            childView.setAlpha(0.5f);
            childView.setScaleX(0.9f);
            childView.setScaleY(0.9f);
        }

        View primaryItem = adapter.getPrimaryItem();
        primaryItem.setAlpha(1.0f);
        primaryItem.setScaleX(1.0f);
        primaryItem.setScaleY(1.0f);
    }

    public void changeData(View view) {

        try {
            Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
            mFirstLayout.setAccessible(true);
            mFirstLayout.set(viewPager, true);
        } catch (Exception e) {
            e.printStackTrace();
        }

        dataList.clear();
        for (int i = 0; i < 10; i++) {
            dataList.add("更新 " + i);
        }
        adapter.setData(dataList);

        viewPager.setCurrentItem(adapter.getFirstPosition(), true);
        //updateNearView();
    }

    public void turnNext(View view) {
        if (dataList.size() < 2) return;
        viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
    }

最後是Adapter的寫法:

public class BannerAdapter extends PagerAdapter {

    private Context context;
    private List<String> dataList = new ArrayList<>();
    private boolean notify;
    private View primaryView;

    public BannerAdapter(Context context) {
        this.context = context;
    }

    public BannerAdapter(Context context, List<String> dataList) {
        this.context = context;
        this.dataList = dataList;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, final int position) {
        View bannerView = LayoutInflater.from(context).inflate(R.layout.banner_view, null);
        ImageView imageView = bannerView.findViewById(R.id.imageView);
        TextView textView = bannerView.findViewById(R.id.textView);
        textView.setText(dataList.get(position % getRealCount()));
        container.addView(bannerView);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, dataList.get(position % dataList.size()), Toast.LENGTH_SHORT).show();
            }
        });
        return bannerView;
    }

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);

        primaryView = (View) object;
    }

    public View getPrimaryItem() {
        return primaryView;
    }

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

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

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

    public int getRealCount() {
        return dataList.size();
    }

    public int getFirstPosition() {
        //無限模式下從Int最大值中間開始,若從0開始則無法左滑
        int center = Integer.MAX_VALUE / 2;
        center = center - center % getRealCount();
        return center;
    }

    public void setData(List<String> dataList) {
        this.dataList = dataList;

        this.notify = true;
        notifyDataSetChanged();
        this.notify = false;
    }

    private void inflateDataList(List<String> dataList) {
        this.dataList.clear();
        if (dataList.size() < 2) {
            this.dataList.addAll(dataList);
        } else {
            this.dataList.add("data " + (dataList.size() - 2));
            this.dataList.add("data " + (dataList.size() - 1));
            this.dataList.addAll(dataList);
            this.dataList.add("data " + 0);
            this.dataList.add("data " + 1);
        }
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        if (notify) {
            return POSITION_NONE;
        } else {
            return super.getItemPosition(object);
        }
    }
}

最最最後附上PagerTransformer類:

public class ZoomOutTransformer implements ViewPager.PageTransformer {

    private final float MAX_SCALE = 1.0f;
    private final float MIN_SCALE = 0.9f;
    private final float MIN_Alpha = 0.5f;

    @Override
    public void transformPage(@NonNull View view, float position) {
        //a頁滑動至b頁 ; a頁從 0.0 -1 ;b頁從1 ~ 0.0
        //setScaleY只支持api11以上
        if (position < -1) {
            view.setScaleX(MIN_SCALE);
            view.setScaleY(MIN_SCALE);
            view.setAlpha(MIN_Alpha);
        } else if (position <= 1) {
            // [-1,1]
            // Log.e("TAG", view + " , " + position + "");
            float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);
            float alpha = MIN_Alpha + ((1 - Math.abs(position)) * (MAX_SCALE - MIN_Alpha));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            view.setAlpha(alpha);
        } else {
            // (1,+Infinity]
            view.setScaleX(MIN_SCALE);
            view.setScaleY(MIN_SCALE);
            view.setAlpha(MIN_Alpha);
        }
    }
}

完活兒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章