viewpager实现画廊效果 中间突出 两边随着滑动放大缩小只显示3个 点击左右两侧还可以切换

viewpager实现画廊效果 中间突出 两边随着滑动放大缩小只显示3个 点击左右两侧还可以切换_第1张图片

   首先放效果图,此次的需求是希望一共3张图片,中间突出显示,左右两侧随着滑动放大缩小,并且点击左右两侧可以切换图片.这样的效果其实是挺少见的,本人在网上找的大部分是用recyclerview实现的但是左右两侧都是被盖住的,如果加上左右间距之后会在滑动时有各种各样的问题,所以没有办法就试着自己做了一个.

   说一下思路,首先是使用的viewpaegr实现的切换效果,并没有使用recyclerview,此处代码是抄袭网上郭神的代码,没有传送门.之后开始分析,首先使用viewpager实现的大部分都是轮播图,我们都知道实现这样的效果都是设置了ClipChildren属性为false使得view能够超出布局显示(这也表示了view绘制时是没有边界的,限制view显示的是Layout||viewGroup),所以在左右两侧显示的图片是无法处理事件分发的.所以无论是在viewpager外滑动还是点击都会无效,所以需要在外层布局中将左右两侧的滑动事件也分发给viewpager处理,点击事件我们自己处理.

  还有一个点就是我们只需要显示3个图片,并且我希望可以自定义左右两侧和中间图片的大小,所以外层viewgroup的宽度高度都需要我们自己计算,这个计算也不是很复杂,代码注释也很详细了(自认为),就不多做赘述,代码见.

public class AnimatBanner extends ViewGroup {

    private List urls = new ArrayList<>();
    private ViewPager bannerPager;
    private int deplayedTime = 3000;
    private boolean isAutoPlay = true;
    private boolean clickSwitchable = true;

    private int imgWidth = dip2px(76);//图片宽度  默认为76dp
    private int imgHeight = dip2px(76);//图片宽度  默认为76dp
    private int pageMargin = dip2px(15);//图片之间的间距  默认为15dp
    private float scalingRatio = 0.65f;

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public AnimatBanner(Context context) {
        this(context, null);
    }

    public AnimatBanner(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AnimatBanner(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setClipChildren(false);
    }


    /**
     * 设置是否自动轮播  在setImagesToBanner前调用
     *
     * @param isAutoPlay
     */
    public void setAutoPlay(boolean isAutoPlay) {
        this.isAutoPlay = isAutoPlay;
    }

    /**
     * 设置居中显示的图片的大小 单位为dp  默认为76dp
     *
     * @param imgWidth
     * @param imgHeight
     */
    public void setImgSize(int imgWidth, int imgHeight) {
        this.imgWidth = dip2px(imgWidth);
        this.imgHeight = dip2px(imgHeight);
    }

    /**
     * 设置图片之间的间距  单位为dp  默认为15dp
     *
     * @param pageMargin
     */
    public void setPageMargin(int pageMargin) {
        this.pageMargin = dip2px(pageMargin);
    }

    /**
     * 设置是否可以点击切换  默认为可以切换
     * 此方法可在任意时间调用
     */
    public void setClickSwitchable(boolean clickSwitchable) {
        this.clickSwitchable = clickSwitchable;
    }

    /**
     * 设置缩放比例
     * @param scalingRatio
     */
    public void setScalingRatio(float scalingRatio){
        this.scalingRatio = scalingRatio;
    }

    /**
     * 开始轮播
     */
    public void startPlay() {
        if (handler != null && isAutoPlay && urls != null && urls.size() > 0) {
            handler.removeMessages(1);
            handler.sendEmptyMessageDelayed(1, deplayedTime);
        }
    }

    /**
     * 停止轮播
     */
    public void onPause() {
        if (handler != null) {
            handler.removeMessages(1);
        }
    }

    /**
     * 销毁handler  防止内存泄露
     */
    public void onDestroy() {
        if (handler != null) {
            handler.removeMessages(1);
            handler = null;
        }
    }

    /**
     * 传入图片
     *
     * @param urls
     */
    public void setImagesToBanner(final List urls) {

        removeAllViews();

        this.urls = urls;

        bannerPager = new ViewPager(getContext());

        bannerPager.setPageMargin(pageMargin);//图片之间的间距
        bannerPager.setOffscreenPageLimit((urls.size() >= 3 ? 3 : urls.size()) + 3);//解决滑动很多个条目式因预加载的数量不够而显示空白的问题
        bannerPager.setPageTransformer(true, new ScaleInTransformer(scalingRatio));//设置viewpager滑动样式   scalingRatio为缩放比例  根据蓝湖上标注计算得出
        bannerPager.setAdapter(new BannerPagerAdapter());
        bannerPager.setCurrentItem(urls.size() * 100);//无限轮播
        bannerPager.setClipChildren(false);

        bannerPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int i, float v, int i1) {

            }

            @Override
            public void onPageSelected(int i) {
                startPlay();
            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });

        addView(bannerPager);
        startPlay();
    }

    /**
     * 计算父布局宽度  使得刚好展示出左右两侧图片
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        measureChildren(widthMeasureSpec, heightMeasureSpec);
        int width = (int) (bannerPager.getMeasuredWidth() + (imgWidth * scalingRatio) * 2 + pageMargin * 2);
        setMeasuredDimension(width, imgHeight);

    }

    /**
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        View view = getChildAt(0);//拿到唯一的一个viewpager

        int left = (int) ((imgWidth * scalingRatio) + pageMargin);
        int top = 0;
        int right = (int) (((imgWidth * scalingRatio) + pageMargin) + view.getMeasuredWidth());
        int bottom = imgHeight;

        view.layout(left, top, right, bottom);//将viewpager绘制到父布局中

    }


    private boolean clickable = false;
    private int clickPosition = -1;

    //当点击的位置不在viewpager上时  事件才会返回到该布局上  再将其分发到viewpager上 嘤嘤嘤
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                clickable = true;
                if (event.getX() > bannerPager.getX()) {//判断按下的位置是在viewpager左边还是右边  1:右  0:左
                    clickPosition = 1;
                } else {
                    clickPosition = 0;
                }
                Log.e("test", clickPosition + "");
                break;
            case MotionEvent.ACTION_MOVE://如果移动那么点击事件不生效  clickable为false
                clickable = false;
                break;
            case MotionEvent.ACTION_UP://没有移动  点击事件生效  根据位置切换viewpager
                if (clickSwitchable && clickable && clickPosition != -1) {
                    if (clickPosition == 0) {//点击viewpager左侧
                        bannerPager.setCurrentItem(bannerPager.getCurrentItem() - 1);
                    } else {//点击viewpager右侧
                        bannerPager.setCurrentItem(bannerPager.getCurrentItem() + 1);
                    }
                }
                clickable = false;
                break;
        }

        return bannerPager.onTouchEvent(event);//事件最终全部分发到viewpager上
    }


    class BannerPagerAdapter extends PagerAdapter {
        @Override
        public Object instantiateItem(ViewGroup container, final int position) {

            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(imgWidth,
                    imgHeight);//设置图片大小也相当于设置viewpager的大小

            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);//viewapger居中显示

            container.setLayoutParams(layoutParams);//container为布局容器  需要手动设置container大小并将其居中


            ImageView imageView = new ImageView(getContext());//创建需要加载的图片
            imageView.setLayoutParams(layoutParams);//设置布局方式   可有可无
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//设置图片缩放模式

            RequestOptions circleOptions = new RequestOptions()
                    .circleCrop();//设置圆角

            Glide.with(getContext())
                    .load(urls.get(position % urls.size()))
                    .apply(circleOptions)
                    .into(imageView);

            container.addView(imageView);

            return imageView;
        }

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

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

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

    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                bannerPager.setCurrentItem(bannerPager.getCurrentItem() + 1);
            }
        }
    };

}
ScaleInTransformer类
public class ScaleInTransformer extends BasePageTransformer {
    private static final float DEFAULT_MIN_SCALE = 0.65f;
    private float mMinScale = DEFAULT_MIN_SCALE;

    public ScaleInTransformer() {
    }

    public ScaleInTransformer(float minScale) {
        this(minScale, NonPageTransformer.INSTANCE);
    }

    public ScaleInTransformer(ViewPager.PageTransformer pageTransformer) {
        this(DEFAULT_MIN_SCALE, pageTransformer);
    }


    public ScaleInTransformer(float minScale, ViewPager.PageTransformer pageTransformer) {
        mMinScale = minScale;
        mPageTransformer = pageTransformer;
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void pageTransform(View view, float position) {
        int pageWidth = view.getWidth();
        int pageHeight = view.getHeight();

        view.setPivotY(pageHeight / 2);
        view.setPivotX(pageWidth / 2);
        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setScaleX(mMinScale);
            view.setScaleY(mMinScale);
            view.setPivotX(pageWidth);
        } else if (position <= 1) { // [-1,1]
            // Modify the default slide transition to shrink the page as well
            if (position < 0) { //1-2:1[0,-1] ;2-1:1[-1,0]
                float scaleFactor = (1 + position) * (1 - mMinScale) + mMinScale;
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);

                view.setPivotX(pageWidth * (DEFAULT_CENTER + (DEFAULT_CENTER * -position)));

            } else { //1-2:2[1,0] ;2-1:2[0,1]

                float scaleFactor = (1 - position) * (1 - mMinScale) + mMinScale;
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
                view.setPivotX(pageWidth * ((1 - position) * DEFAULT_CENTER));
            }


        } else { // (1,+Infinity]
            view.setPivotX(0);
            view.setScaleX(mMinScale);
            view.setScaleY(mMinScale);
        }
    }
}
NonPageTransformer类
public class NonPageTransformer implements ViewPager.PageTransformer {
    @Override
    public void transformPage(View page, float position)
    {
        page.setScaleX(0.999f);//hack
    }

    public static final ViewPager.PageTransformer INSTANCE = new NonPageTransformer();
}
BasePageTransformer类
public abstract class BasePageTransformer implements ViewPager.PageTransformer {
    protected ViewPager.PageTransformer mPageTransformer = NonPageTransformer.INSTANCE;
    public static final float DEFAULT_CENTER = 0.5f;

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void transformPage(View view, float position) {
        if (mPageTransformer != null)
        {
            mPageTransformer.transformPage(view, position);
        }

        pageTransform(view, position);
    }

    protected abstract void pageTransform(View view, float position);


}

使用方法我相信大家仔细看代码之后是可以理解的,手动狗头..

你可能感兴趣的:(viewpager实现画廊效果 中间突出 两边随着滑动放大缩小只显示3个 点击左右两侧还可以切换)