android ViewPager 轮播

package com.xin.ui.widget;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.VelocityTrackerCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.Scroller;

import com.xin.dbmbase.R;

import java.util.ArrayList;

/**
 * viewpager 包一层,添加了对应展示第几张的标识,并且可以无限滑动
 */

public class LoopViewPager extends FrameLayout {

    public static final int SCROLL_STATE_IDLE = 0;
    public static final int SCROLL_STATE_DRAGGING = 1;
    public static final int SCROLL_STATE_SETTLING = 2;
    private static final String TAG = "LoopViewPager";
    private static final boolean DEBUG = true;
    private static final int MIN_FLING_VELOCITY = 400;
    private static final int MIN_DISTANCE_FOR_FLING = 200;
    private static final Interpolator sInterpolator = new Interpolator() {
        public float getInterpolation(float t) {
            t -= 1.0f;
            return t * t * t * t * t + 1.0f;
        }
    };
    private int mOffsetLimit = 1;
    private int mCurrentPos;
    private NextRunnable runnable;
    private PagerAdapter mAdapter;
    private VelocityTracker mVelocityTracker;
    private ArrayList<ViewPager.OnPageChangeListener> mListener;
    private float mDownX;
    private float mDownY;
    private int mTouchSlop;
    private int mMinimumVelocity;
    private int mMaximumVelocity;
    private int mFlingDistance;
    private Scroller mScroller;
    private int mState = SCROLL_STATE_IDLE;
    private Drawable mDotChosed;
    private Drawable mDotNormal;
    private int mMarginDot;
    private int mMinShow = 2;//有两个才显示,1个点太奇怪了
    private FrameLayout.LayoutParams mParamsDot;
    private int mDotTop;
    private int mDotLeft;
    private DataSetObserver mObserever = new DataSetObserver() {
        @Override
        public void onChanged() {
            removeAllViews();
            populate(mCurrentPos);
            postDelayed(runnable, runnable.mDelay);
        }
    };

    public LoopViewPager(@NonNull Context context) {
        super(context);
        init();
    }

    public LoopViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LoopViewPager(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoopViewPager(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        runnable = new NextRunnable(this);
        mScroller = new Scroller(getContext(), sInterpolator);
        final float density = getResources().getDisplayMetrics().density;
        ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
        mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
        mDotChosed = getContext().getResources().getDrawable(R.drawable.action_index_bj);
        mDotNormal = getContext().getResources().getDrawable(R.drawable.action_index_deg_bj);
        mDotNormal.setBounds(0, 0, mDotNormal.getMinimumWidth(), mDotNormal.getMinimumHeight());
        mDotChosed.setBounds(0, 0, mDotChosed.getMinimumWidth(), mDotChosed.getMinimumHeight());
        mMarginDot = mDotNormal.getMinimumWidth();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mDotNormal != null && mDotChosed != null && mAdapter != null) {
            if (mParamsDot == null) {
                final float density = getResources().getDisplayMetrics().density;
                mParamsDot = generateDotLayoutParams((int) (density * 10));
            }
            mDotTop = (mParamsDot.gravity & Gravity.TOP) == Gravity.TOP ? mParamsDot.topMargin : ((mParamsDot.gravity & Gravity.BOTTOM) == Gravity.BOTTOM ? getMeasuredHeight() - mParamsDot.bottomMargin - mDotNormal.getBounds().height() : (getMeasuredHeight() - mDotNormal.getBounds().height()) / 2);
            mDotLeft = (mParamsDot.gravity & Gravity.LEFT) == Gravity.LEFT ? mParamsDot.leftMargin : ((mParamsDot.gravity & Gravity.RIGHT) == Gravity.RIGHT ? getMeasuredWidth() - mParamsDot.leftMargin - mDotNormal.getBounds().width() : (getMeasuredWidth() - (mDotNormal.getBounds().width() + mMarginDot) * (mAdapter.getCount() - 1) - mDotChosed.getBounds().width()) / 2);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int count = getChildCount();
        int parentWidth = right - left;
        int parentHeight = bottom - top;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();
            int childLeft = lp.layout * parentWidth + ((lp.gravity & Gravity.LEFT) != 0 ? lp.leftMargin : parentWidth - width - lp.rightMargin);
            int childTop = (lp.gravity & Gravity.TOP) != 0 ? lp.topMargin : parentHeight - height - lp.topMargin;
            child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
        scrollTo(mCurrentPos * parentWidth, 0);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mDotChosed != null && mDotNormal != null && mAdapter != null && mAdapter.getCount() >= mMinShow) {
            canvas.save();
            canvas.translate(getScrollX() + mDotLeft, mDotTop);
            for (int i = 0; i < mAdapter.getCount(); i++) {
                if (i == mCurrentPos) {
                    mDotChosed.draw(canvas);
                    canvas.translate(mDotChosed.getMinimumWidth() + mMarginDot, 0);
                } else {
                    mDotNormal.draw(canvas);
                    canvas.translate(mDotNormal.getMinimumWidth() + mMarginDot, 0);
                }
            }
            canvas.restore();
        }
    }

    /**
     * 自动循环播放
     *
     * @param delay,下一张的时间间隔,如果小于等于0取消自动播放
     */
    public void setAutoNext(final int delay) {
        postDelayed(runnable, runnable.mDelay = delay);
    }


    public void setDot(Drawable dotChosed, Drawable dotNormal) {
        this.mDotChosed = dotChosed;
        this.mDotNormal = dotNormal;
    }

    public void setDotLayoutParams(FrameLayout.LayoutParams params) {
        this.mParamsDot = params;
    }

    public void setMarginDot(int mMarginDot) {
        this.mMarginDot = mMarginDot;
    }

    public PagerAdapter getAdapter() {
        return mAdapter;
    }

    public void setAdapter(PagerAdapter adapter) {
        if (mAdapter != null) {
            removeAllViews();
            mAdapter.unregisterDataSetObserver(mObserever);
        }
        if (adapter != null) {
            mAdapter = adapter;
            mAdapter.registerDataSetObserver(mObserever);
            mAdapter.notifyDataSetChanged();
        } else {
            mAdapter = null;
        }
    }

    public void showDot(FrameLayout.LayoutParams params) {

    }

    public int getOffscreenPageLimit() {
        return mOffsetLimit;
    }

    public void setOffscreenPageLimit(int limit) {
        mOffsetLimit = limit;
        mAdapter.notifyDataSetChanged();
    }

    public void addOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
        if (mListener == null) {
            mListener = new ArrayList<ViewPager.OnPageChangeListener>();
        }
        mListener.add(listener);
    }

    public void clearOnPageChangeListeners() {
        if (mListener != null) {
            mListener.clear();
        }
    }


    public int getCurrentItem() {
        return mCurrentPos;
    }

    public void setCurrentItem(int item) {
        setCurrentItem(item, true);
    }

    public void setCurrentItem(final int item, final boolean smoothScroll) {
        final int width = getWidth();
        if (width == 0) {
            getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    setCurrentItemInner(smoothScroll, item * getWidth());
                }
            });
        } else {
            setCurrentItemInner(smoothScroll, item * width);
        }

    }

    private void setCurrentItemInner(boolean smoothScroll, int x) {
        if (smoothScroll) {
            smoothScrollTo(x);
        } else {
            scrollTo(x, 0);
        }
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean touchEvent = super.dispatchTouchEvent(ev);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                removeCallbacks(runnable);
                mVelocityTracker = VelocityTracker.obtain();
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.addMovement(ev);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mVelocityTracker.clear();
                mVelocityTracker.recycle();
                postDelayed(runnable, runnable.mDelay);
                break;
        }
        return touchEvent;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownX = ev.getRawX();
                mDownY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                float diffX = Math.abs(ev.getRawX() - mDownX);
                float diffY = Math.abs(ev.getRawY() - mDownY);
                if (mAdapter != null && mAdapter.getCount() > 1 && diffX > mTouchSlop && diffX > diffY) {//只有一个 item 不要滑动, 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                    if (mState == SCROLL_STATE_SETTLING) {//还没有停止,又按下了
                        setScrollState(SCROLL_STATE_IDLE);
                        mScroller.abortAnimation();
                        int newPos = mScroller.getFinalX() / getWidth();
                        if (newPos != mCurrentPos) {
                            populate(newPos);
                            setPageSelected(newPos);
                        }
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                getParent().requestDisallowInterceptTouchEvent(true);
                scrollTo((int) (mDownX - event.getRawX() + mCurrentPos * getWidth()), 0);
                setScrollState(SCROLL_STATE_DRAGGING);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, 0);
                if (Math.abs(mDownX - event.getRawX()) > mFlingDistance || Math.abs(initialVelocity) > mMinimumVelocity) {
                    if (mDownX < event.getRawX()) {
                        smoothScrollTo((mCurrentPos - 1) * getWidth());
                    } else {
                        smoothScrollTo((mCurrentPos + 1) * getWidth());
                    }
                } else {
                    smoothScrollTo(mCurrentPos * getWidth());
                }
                break;
        }
        return true;
    }

    private void populate(int newPos) {
        if (mAdapter == null) {
            return;
        }
        int count = mAdapter.getCount();
        if (count > 0) {
            if (DEBUG) {
                Log.i(TAG, mCurrentPos + " newpos " + newPos + "   populate  " + hashCode());
            }
            if (newPos <= -1) {
                mCurrentPos = count - 1;
            } else if (newPos >= count) {
                mCurrentPos = 0;
            } else {
                mCurrentPos = newPos;
            }
            int offsetLimit = Math.max(0, Math.min(count - 1, mOffsetLimit));
            int fist = mCurrentPos - offsetLimit;
            int last = mCurrentPos + offsetLimit;
            int childCount = getChildCount();
            ArrayList<LayoutParams> list = new ArrayList<LayoutParams>();
            SparseArray<View> cache = new SparseArray<View>();
            for (int i = 0; i < childCount; i++) {//如果是第一个和最后一个之间 就缓存上,其他的已经不需要缓存了
                View view = getChildAt(i);
                LayoutParams layoutParmas = (LayoutParams) view.getLayoutParams();
                if (layoutParmas.index < fist || layoutParmas.index > last) {
                    list.add(layoutParmas);
                } else {
                    cache.put(layoutParmas.index, view);
                }
            }
            for (LayoutParams layoutParams : list) {
                mAdapter.destroyItem(this, layoutParams.index, layoutParams.obj);
            }

            for (int i = fist, j = 0; i <= last; i++, j++) {//
                if (i < 0) {
                    View itemInfo = cache.get(i + count);
                    LayoutParams params;
                    if (itemInfo == null) {
                        Object res = mAdapter.instantiateItem(this, i + count);
                        params = (LayoutParams) getChildAt(getChildCount() - 1).getLayoutParams();
                        params.obj = res;
                    } else {
                        params = (LayoutParams) itemInfo.getLayoutParams();
                    }
                    params.index = i + count;//i=-1 当是0的时候,缓存前一个,index 是 adapter 的最后一个
                    params.layout = i;//i=-1  记录布局所在的位置
                } else if (i >= count) {
                    View itemInfo = cache.get(i);
                    LayoutParams params;
                    if (itemInfo == null) {
                        Object res = mAdapter.instantiateItem(this, i - count);
                        params = (LayoutParams) getChildAt(getChildCount() - 1).getLayoutParams();
                        params.obj = res;
                    } else {
                        params = (LayoutParams) itemInfo.getLayoutParams();
                    }
                    params.index = i - count;
                    params.layout = i;
                } else {
                    View itemInfo = cache.get(i);
                    LayoutParams params;
                    if (itemInfo == null) {
                        Object res = mAdapter.instantiateItem(this, i);
                        params = (LayoutParams) getChildAt(getChildCount() - 1).getLayoutParams();
                        params.obj = res;
                    } else {
                        params = (LayoutParams) itemInfo.getLayoutParams();
                    }
                    params.index = i;
                    params.layout = i;
                }
            }
            requestLayout();
        }
    }

    private void smoothScrollTo(int x) {
        if (DEBUG) {
            Log.i(TAG, mCurrentPos + "   smoothScrollTo  " + hashCode());
        }
        mScroller.startScroll(getScrollX(), 0, x - getScrollX(), 0);
        setScrollState(SCROLL_STATE_SETTLING);
        invalidate();
    }

    public void setScrollState(int state) {
        mState = state;
        if (mListener != null) {
            for (ViewPager.OnPageChangeListener onPageChangeListener : mListener) {
                onPageChangeListener.onPageScrollStateChanged(state);
            }
        }
    }

    @Override
    public void computeScroll() {
        int width = getWidth();
        if (SCROLL_STATE_SETTLING == mState && width > 0) {
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                if (mListener != null) {
                    int dx = mScroller.getFinalX() - getScrollX();
                    for (ViewPager.OnPageChangeListener onPageChangeListener : mListener) {
                        if (dx < 0) {
                            onPageChangeListener.onPageScrolled(mCurrentPos - 1, -dx / width, -dx);
                        } else {
                            onPageChangeListener.onPageScrolled(mCurrentPos, dx / width, dx);
                        }
                    }
                }
                invalidate();
            } else {
                setScrollState(SCROLL_STATE_IDLE);
                int newPos = getScrollX() / width;
                if (newPos != mCurrentPos) {
                    populate(newPos);
                    setPageSelected(newPos);
                }
            }
        }
    }

    private void setPageSelected(int newPos) {
        if (mListener != null) {
            for (ViewPager.OnPageChangeListener onPageChangeListener : mListener) {
                onPageChangeListener.onPageSelected(newPos);
            }
        }
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (visibility == View.VISIBLE) {
            postDelayed(runnable, runnable.mDelay);
        } else {
            removeCallbacks(runnable);
        }
    }

    public void setLoop(boolean loop) {
        if (loop) {
            postDelayed(runnable, runnable.mDelay);
        } else {
            removeCallbacks(runnable);
        }
    }

    public FrameLayout.LayoutParams generateDotLayoutParams(int density) {
        FrameLayout.LayoutParams paramsDot = new FrameLayout.LayoutParams(-2, -2);
        paramsDot.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        paramsDot.bottomMargin = density;
        return paramsDot;
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, -1, params);//每次都添加到最后一个 view
    }

    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(-1, -1);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p.width, p.height);
    }

    @Override
    public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    public static class LayoutParams extends FrameLayout.LayoutParams {
        private Object obj;
        private int index;
        private int layout;

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }
    }

    private static class NextRunnable implements Runnable {
        private final LoopViewPager vp;
        private int mDelay;

        public NextRunnable(LoopViewPager loopViewPager) {
            this.vp = loopViewPager;
        }

        @Override
        public void run() {
            vp.removeCallbacks(this);
            if (mDelay == 0 || vp.mAdapter == null || vp.mAdapter.getCount() <= 1 || (vp.getContext() instanceof Activity && ((Activity) vp.getContext()).isFinishing())) {//如果没有任何引用了,证明 activity 销毁了,停止循环
                return;
            }
            vp.setCurrentItem(vp.mCurrentPos + 1, true);
            vp.postDelayed(this, mDelay);
            if (DEBUG) {
                Log.i(TAG, "run  " + hashCode() + "  " + vp.hashCode());
            }
        }
    }

}
AndroidViewPager是一种常用的布局容器,用于在屏幕上滑动显示多个页面。在实现轮播效果时,可以通过以下步骤来实现: 1. 首先,在XML布局文件中添加ViewPager组件。可以设置该组件的宽度和高度,以及其他属性,如指示器dots等。 2. 创建一个适配器类(如PagerAdapter),用于管理ViewPager中的页面。适配器需要重写一些方法,如获取页面数量、创建页面和销毁页面等。 3. 在创建页面的方法中,可以使用LayoutInflater.inflate()方法来加载布局文件,然后将其添加到ViewPager中。可以根据需求自定义每个页面的布局。 4. 在Activity或Fragment中,通过findViewById()方法获取ViewPager组件的实例,并设置适配器。例如: ViewPager viewPager = findViewById(R.id.viewPager); viewPager.setAdapter(adapter); 5. 如果需要实现轮播效果,可以通过设置ViewPager的setCurrentItem()方法来切换页面。可以使用Handler类来实现定时切换页面的效果。例如: Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { int currentItem = viewPager.getCurrentItem(); if (currentItem < adapter.getCount() - 1) { currentItem++; } else { currentItem = 0; } viewPager.setCurrentItem(currentItem); handler.postDelayed(this, 3000); // 间隔3秒切换页面 } }; handler.postDelayed(runnable, 3000); 以上就是使用AndroidViewPager实现轮播效果的简要步骤。通过设置适配器和定时切换页面,可以让多个页面在屏幕上自动滑动显示,从而实现轮播效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值