Android轮播图(指示器随页面滑动)

2017-5-19 liuyingcong 安卓开发

先上图:

S70519-15400949_00_00_00__00_00_13.gif

与喜马拉雅FM中轮播图效果是一样的,切换平缓,并且可以设置切换时间,以及每个页面停留的时间间隔,最大的特点是页面指示器随页面滚动,到最后一个页面时,滑动到一半时圆点指示到第一个页面,触摸停止,应用glide加载网络图片,可以设置默认图片和缓存以及失败图片,每一个页面具备点击事件。


项目在github上:https://github.com/986558210/Banner


代码如下:


核心代码Banner.java:

package com.example.banner;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.banner.listener.OnBannerClickListener;
import com.example.banner.listener.OnLoadImageListener;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class Banner extends FrameLayout implements ViewPager.OnPageChangeListener {

    private int mIndicatorWidth;
    private int mIndicatorHeight;
    private int mIndicatorMargin;
    private int mDelayTime;
    private int mDuration;
    private boolean isAutoPlay;
    private int mIndicatorSelectedResId;
    private int mIndicatorUnselectedResId;
    private int defaultImage = -1;
    private int count = 0;
    private int currentItem;
    private int gravity = -1;
    private int scaleType = 0;
    private List<ImageView> imageViews;//图片集合
    private Context context;
    private ViewPager viewPager;
    private LinearLayout indicator_bottom, indicator_top;
    private Handler handler = new Handler();
    private OnLoadImageListener imageListener;
    private BannerPagerAdapter adapter;
    private ViewPager.OnPageChangeListener mOnPageChangeListener;
    private OnBannerClickListener listener;
    private ImageView imageView_sel;
    private LinearLayout.LayoutParams mLayoutParams;

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

    public Banner(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Banner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        imageViews = new ArrayList<>();
        initView(context, attrs);
    }

    private void initView(Context context, AttributeSet attrs) {
        View view = LayoutInflater.from(context).inflate(R.layout.banner, this, true);
        viewPager = (ViewPager) view.findViewById(R.id.viewpager);

        //指示器的底层点的父布局
        indicator_bottom = (LinearLayout) view.findViewById(R.id.indicator_bottom);
        //上层滑动的点的父布局
        indicator_top = (LinearLayout) view.findViewById(R.id.indicator_top);

        handleTypedArray(context, attrs);
        initViewPagerScroll();
    }

    private void handleTypedArray(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner);
        mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_width, 7);
        mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_height, 7);
        mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_margin, 5);
        mIndicatorSelectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_selected, R.drawable.gray_radius);
        mIndicatorUnselectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_unselected, R.drawable.white_radius);
        scaleType = typedArray.getInt(R.styleable.Banner_image_scale_type, 0);
        defaultImage = typedArray.getResourceId(R.styleable.Banner_default_image, -1);
        mDelayTime = typedArray.getInt(R.styleable.Banner_delay_time, 2000);
        mDuration = typedArray.getInt(R.styleable.Banner_duration, 700);
        isAutoPlay = typedArray.getBoolean(R.styleable.Banner_is_auto_play, true);
        typedArray.recycle();
    }

    /**
     * 为VP添加滚动器,为了控制切换的时间
     */
    private void initViewPagerScroll() {
        try {
            Field mField = ViewPager.class.getDeclaredField("mScroller");
            mField.setAccessible(true);
            ViewPagerScroller mScroller = new ViewPagerScroller(viewPager.getContext());
            mField.set(viewPager, mScroller);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加图片的方法
     *
     * @param imagesUrl     图片的地址集合
     * @param imageListener
     */
    public void setImageList(List<?> imagesUrl, OnLoadImageListener imageListener) {
        if (imagesUrl == null || imagesUrl.size() <= 0) {
            return;
        }
        count = imagesUrl.size();
        imageViews.clear();
        createIndicator();
        for (int i = 1; i <= count; i++) {
            ImageView iv = new ImageView(context);
            if (scaleType == 0)
                iv.setScaleType(ScaleType.FIT_XY);
            else
                iv.setScaleType(ScaleType.CENTER_CROP);
            Object url;
            if (i == 0) {
                url = imagesUrl.get(count - 1);
            } else if (i == count + 1) {
                url = imagesUrl.get(0);
            } else {
                url = imagesUrl.get(i - 1);
            }
            imageViews.add(iv);
            if (imageListener != null) {
                imageListener.OnLoadImage(iv, url);
            } else {
                if (defaultImage != -1)
                    Glide.with(context)
                            .load(url)
                            .diskCacheStrategy(DiskCacheStrategy.ALL)
                            .crossFade().placeholder(defaultImage)
                            .error(defaultImage)
                            .into(iv);
                else
                    Glide.with(context)
                            .load(url)
                            .crossFade()
                            .into(iv);
            }
        }
        setData();
    }

    private void setData() {
        currentItem = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % count;
        if (adapter == null) {
            adapter = new BannerPagerAdapter();
            viewPager.setAdapter(adapter);
        } else {
            adapter.notifyDataSetChanged();
        }
        viewPager.setFocusable(true);
        viewPager.setCurrentItem(currentItem);
        viewPager.addOnPageChangeListener(this);
        if (gravity != -1) {
            indicator_bottom.setGravity(gravity);
            indicator_top.setGravity(gravity);
        }
        if (isAutoPlay)
            startAutoPlay();
    }

    private void createIndicator() {

        //创建底部的点
        indicator_bottom.removeAllViews();
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIndicatorWidth, mIndicatorHeight);
        params.leftMargin = mIndicatorMargin;
        params.rightMargin = mIndicatorMargin;
        for (int i = 0; i < count; i++) {
            ImageView imageView = new ImageView(context);
            imageView.setScaleType(ScaleType.CENTER_CROP);
            imageView.setImageResource(mIndicatorUnselectedResId);
            indicator_bottom.addView(imageView, params);
        }

        //创建选中的点
        indicator_top.removeAllViews();
        imageView_sel = new ImageView(context);
        imageView_sel.setScaleType(ScaleType.CENTER_CROP);
        imageView_sel.setImageResource(mIndicatorSelectedResId);
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(count * mIndicatorWidth + (count - 1) * 2 * mIndicatorMargin, RelativeLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
        indicator_top.setLayoutParams(layoutParams);
        indicator_top.addView(imageView_sel, new LinearLayout.LayoutParams(mIndicatorWidth, mIndicatorHeight));
    }

    private void startAutoPlay() {
        handler.removeCallbacks(task);
        handler.postDelayed(task, mDelayTime);
    }

    public void stopAutoPlay() {
        handler.removeCallbacks(task);
    }

    private final Runnable task = new Runnable() {
        @Override
        public void run() {
            if (count > 1 && isAutoPlay) {
                currentItem += 1;
                viewPager.setCurrentItem(currentItem);
                handler.postDelayed(task, mDelayTime);
            }
        }
    };

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (isAutoPlay) {
            int action = ev.getAction();
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_OUTSIDE) {
                startAutoPlay();
            } else if (action == MotionEvent.ACTION_DOWN) {
                stopAutoPlay();
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    class BannerPagerAdapter extends PagerAdapter {

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

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public Object instantiateItem(ViewGroup container, final int position) {
            container.addView(imageViews.get(position % count));
            ImageView view = imageViews.get(position % count);
            if (listener != null) {
                view.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        listener.OnBannerClick(position);
                    }
                });
            }
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(imageViews.get(position % count));
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        currentItem = viewPager.getCurrentItem();
        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageScrollStateChanged(state);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }

        //俩点之间的距离
        int dis = (int) (indicator_bottom.getChildAt(1).getX() - indicator_bottom.getChildAt(0).getX());
        mLayoutParams = (LinearLayout.LayoutParams) imageView_sel.getLayoutParams();
        if(! (position % count == count - 1)) {
            mLayoutParams.leftMargin = Math.round(dis * (position % count + positionOffset));
        }else if(positionOffset<=0.5){
            mLayoutParams.leftMargin = Math.round(dis * (position % count));
        }else {
            mLayoutParams.leftMargin = Math.round(0);
        }
        imageView_sel.setLayoutParams(mLayoutParams);
    }

    @Override
    public void onPageSelected(int position) {
        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageSelected(position);
        }
    }

    public void setOnBannerClickListener(OnBannerClickListener listener) {
        this.listener = listener;
    }

    class ViewPagerScroller extends Scroller {

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

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

        @Override
        public void startScroll(int startX, int startY, int dx, int dy) {
            super.startScroll(startX, startY, dx, dy, mDuration);
        }
    }
}
 
 布局文件banner.xml:



<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="7dp"
        android:layout_gravity="bottom">

        <LinearLayout
            android:id="@+id/indicator_bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:orientation="horizontal"/>
        <LinearLayout
            android:id="@+id/indicator_top"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:orientation="horizontal"/>
    </RelativeLayout>
</FrameLayout>

属性文件attr.xml:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <declare-styleable name="Banner">
        <attr name="default_image" format="reference" />
        <attr name="delay_time" format="integer" />
        <attr name="duration" format="integer" />
        <attr name="is_auto_play" format="boolean" />
        <attr name="indicator_width" format="dimension" />
        <attr name="indicator_height" format="dimension" />
        <attr name="indicator_margin" format="dimension" />
        <attr name="indicator_drawable_selected" format="reference" />
        <attr name="indicator_drawable_unselected" format="reference" />

        <!--图片拉伸方式-->
        <attr name="image_scale_type" format="enum">
            <enum name="fit_xy" value="0" />
            <enum name="center_crop" value="1" />
        </attr>
    </declare-styleable>
</resources> 

选中的点 gray_radius.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#fff" />
    <corners android:radius="5dp" />
</shape>
 
未选中的点 white_radius.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadius="3.15dp"
    android:shape="ring"
    android:thickness="0.35dp"
    android:useLevel="false" >

    <stroke
        android:width="0.35dp"
        android:color="#ffffffff" />
</shape>
应用:

主界面代码MainActivity:
package com.example.banner;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Banner mBanner;//轮播图
    private List<String> mImages=new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBanner = (Banner) findViewById(R.id.banner);
        mImages.add("http://fdfs.xmcdn.com/group26/M00/92/06/wKgJWFkVdbCSfGEaAALFo21GXxo938_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group27/M01/8E/80/wKgJW1kVePLzosGjAAIg7qid8nw914_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group26/M00/91/C9/wKgJRlkVf6Ow3z_EAAI7VV-UZew523_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group26/M06/91/62/wKgJRlkVeS6z6RWDAAHb0lueHgY111_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group27/M06/67/00/wKgJW1kSuNThZLySAARxLtV2RkI156_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group27/M09/CD/9D/wKgJR1kH5j3wIeceAAJme6hHjsg639_android_large.jpg");
        mImages.add("http://fdfs.xmcdn.com/group23/M05/B6/BA/wKgJNFiAc7Hhvbl1AAW3aA1GUqU096_android_large.jpg");
        mBanner.setImageList(mImages, null);

    }
}
主界面布局activity_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <com.example.banner.Banner
        android:id="@+id/banner"
        android:layout_width="match_parent"
        android:layout_height="174.0dip"
        android:background="@drawable/banner_default"
        app:default_image="@drawable/banner_default"
        app:delay_time="2000"
        app:duration="2000"
        app:image_scale_type="fit_xy"
        app:indicator_drawable_selected="@drawable/selected_radius"
        app:indicator_drawable_unselected="@drawable/unselected_radius"
        app:indicator_height="7dip"
        app:indicator_margin="4dip"
        app:indicator_width="7dp"
        app:is_auto_play="true"/>
</LinearLayout>

项目在github上:https://github.com/986558210/Banner



网站备案号:京ICP备11043289号-1 北京市公安局网络备案 海1101084571
版权所有 北京育灵童科技发展有限公司 Copyright © 2002-2024 www.elight.cn, All Rights Reserved