打造一款属于自己的Android滑动器

在android应用中很多情况都要用到图片或或者广告的轮播效果,包括可以自动切换和手动切换以及指示器,这篇文章将手把手教你自己实现这个效果,网上有较多很完善的框架也实现了效果,但在实际的项目中总会遇到各种各样奇葩的问题,还有一些兼容的问题,我不需要兼容到2.3,所以也不需要用到nineoldandroid,还有些动画效果很多都用不到,和其他的布局有冲突等系列问题,所以,打造一款属于自己的android滑动器还是很有必要的,知其然,知其所以然。

我们首先想到的就是使用ViewPager来实现效果,ViewPager可以做到图片切换,而且只能是手动的,其他的效果我们就要一点一点的加了,如轮播效果,指示器,自动切换,还有动画效果,下面就解决一个个的问题。

轮播效果实现
原生的ViewPager是不支持轮播效果的,滑动到第一页或者滑动到最后一页就不能再滑了,感觉不是那么的舒服,现在做一款app怎么能不支持轮播效果呢,这个问题网上有一种解决方案,无需改原生的ViewPager,只需要重写PagerAdapter就行了,看关键代码

public class LoopPagerAdapter extends PagerAdapter {
    private List<View> views;

    public LoopPagerAdapter(List<View> views) {
        this.views = views;
    }

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

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = views.get(position % views.size());
        if (container.equals(view.getParent())) {
            container.removeView(view);
        }
        container.addView(view);
        return view;
    }

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

其实轮播是假的,但是也可以做到以假乱真的效果,count设置的足够大,一般人滑不了这么多,但不管怎么样,这也算一种解决方案
使用

public class MainActivity extends ActionBarActivity {
    private ViewPager pager;
    private LoopPagerAdapter loopPagerAdapter;
    private List<View> views = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pager = (ViewPager) findViewById(R.id.pager);
        views.add(getView(R.mipmap.img1));
        views.add(getView(R.mipmap.img2));
        views.add(getView(R.mipmap.img3));
        loopPagerAdapter = new LoopPagerAdapter(views);
        pager.setAdapter(loopPagerAdapter);
        //设置当前item到Integer.MAX_VALUE中间的一个值,看起来像无论是往前滑还是往后滑都是ok的
        //如果不设置,用户往左边滑动的时候已经划不动了
        pager.setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % views.size());
    }

    private View getView(int res) {
        ImageView imageView = new ImageView(this);
        //利用glide加载图片比原始的setImageResource占用内存小很多
        Glide.with(this).load(res).into(imageView);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        return imageView;
    }

效果图

简单的轮播效果就出来了,效果还不是很明显,下面添加指示器

添加指示器
顾名思义,提示图片的个数以及标识当前图片位置,可以通过代码绘制形状,也可以使用图片,下面开始我就要对滑动器进行封装成一个View,新建一个SliderLayout

public class SliderLayout extends RelativeLayout {

    private ViewPager pager;
    //指示器容器
    private LinearLayout indicatorContainer;

    private Drawable unSelectedDrawable;
    private Drawable selectedDrawable;

    public SliderLayout(Context context) {
        super(context);
        initView();
    }

    public SliderLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public SliderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        //初始化pager
        pager = new ViewPager(getContext());
        //添加viewpager到SliderLayout
        addView(pager);

        //初始化indicatorContainer
        indicatorContainer = new LinearLayout(getContext());
        RelativeLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        //设置指示器的方位,如右下
        params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        //设置margin
        params.setMargins(dpToPx(10), dpToPx(10), dpToPx(10), dpToPx(10));
        //添加指示器容器布局到SliderLayout
        addView(indicatorContainer, params);

        //绘制未选中状态图形
        LayerDrawable unSelectedLayerDrawable;
        LayerDrawable selectedLayerDrawable;
        GradientDrawable unSelectedGradientDrawable;
        unSelectedGradientDrawable = new GradientDrawable();
        unSelectedGradientDrawable.setShape(GradientDrawable.OVAL);
        unSelectedGradientDrawable.setColor(Color.GRAY);
        unSelectedGradientDrawable.setSize(dpToPx(8), dpToPx(8));
        unSelectedLayerDrawable = new LayerDrawable(new Drawable[]{unSelectedGradientDrawable});
        unSelectedDrawable = unSelectedLayerDrawable;
        //绘制选中状态图形
        GradientDrawable selectedGradientDrawable;
        selectedGradientDrawable = new GradientDrawable();
        selectedGradientDrawable.setShape(GradientDrawable.OVAL);
        selectedGradientDrawable.setColor(Color.RED);
        selectedGradientDrawable.setSize(dpToPx(8), dpToPx(8));
        selectedLayerDrawable = new LayerDrawable(new Drawable[]{selectedGradientDrawable});
        selectedDrawable = selectedLayerDrawable;
    }

    //添加本地图片路径
    public void setViewRes(List<Integer> viewRes) {
        List<View> views = new ArrayList<>();
        for (Integer res : viewRes) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            Glide.with(getContext()).load(res).into(imageView);
            views.add(imageView);
        }
        setViews(views);
    }

    //添加网络图片路径
    public void setViewUrls(List<String> urls) {
        List<View> views = new ArrayList<>();
        for (String url : urls) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            Glide.with(getContext()).load(url).into(imageView);
            views.add(imageView);
        }
        setViews(views);
    }

    //添加任意View视图
    public void setViews(final List<View> views) {
        //初始化指示器,并添加到指示器容器布局
        for (int i = 0; i < views.size(); i++) {
            ImageView indicator = new ImageView(getContext());
            indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            indicator.setPadding(dpToPx(3), dpToPx(3), dpToPx(3), dpToPx(3));
            indicatorContainer.addView(indicator);
        }
        LoopPagerAdapter pagerAdapter = new LoopPagerAdapter(views);
        pager.setAdapter(pagerAdapter);
        //设置当前item到Integer.MAX_VALUE中间的一个值,看起来像无论是往前滑还是往后滑都是ok的
        //如果不设置,用户往左边滑动的时候已经划不动了
        int currentItem = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % views.size();
        pager.setCurrentItem(currentItem);
        switchIndicator(currentItem % views.size());
        //添加监听器
        pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                switchIndicator(position % views.size());
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }

    /** * 切换指示器状态 * @param currentPosition 当前位置 */
    private void switchIndicator(int currentPosition) {
        for (int i = 0; i < indicatorContainer.getChildCount(); i++) {
            ((ImageView) indicatorContainer.getChildAt(i)).setImageDrawable(i == currentPosition ? selectedDrawable : unSelectedDrawable);
        }
    }

    /** * 单位转换将dp转化为px * @param dp dp值 * @return 返回px */
    private int dpToPx(int dp) {
        DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
        return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics) + 0.5);
    }

}

xml中添加

<com.example.dongjunkun.myapplication.SliderLayout
        android:id="@+id/sliderLayout"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        />

代码中使用

public class MainActivity extends ActionBarActivity {
    private SliderLayout sliderLayout;
    private List<Integer> viewRes = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sliderLayout = (SliderLayout) findViewById(R.id.sliderLayout);
        viewRes.add(R.mipmap.img1);
        viewRes.add(R.mipmap.img2);
        viewRes.add(R.mipmap.img3);
        sliderLayout.setViewRes(viewRes);
    }
}

效果演示

到这里指示器已经ok了,下面实现自动切换

自动切换
用Handler实现计时器

public class SliderLayout extends RelativeLayout implements android.os.Handler.Callback {

    public static final int DELAY_MILLIS = 4000;
    private ViewPager pager;
    //指示器容器
    private LinearLayout indicatorContainer;

    private Drawable unSelectedDrawable;
    private Drawable selectedDrawable;

    private int WHAT_AUTO_PLAY = 1000;
    private Handler handler;

    private boolean isAutoPlay = false;

    public SliderLayout(Context context) {
        super(context);
        initView();
    }

    public SliderLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public SliderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        handler = new Handler(this);
        //初始化pager
        pager = new ViewPager(getContext());
        //添加viewpager到SliderLayout
        addView(pager);

        //初始化indicatorContainer
        indicatorContainer = new LinearLayout(getContext());
        RelativeLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        //设置指示器的方位,如右下
        params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
        params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        //设置margin
        params.setMargins(dpToPx(10), dpToPx(10), dpToPx(10), dpToPx(10));
        //添加指示器容器布局到SliderLayout
        addView(indicatorContainer, params);

        //绘制未选中状态图形
        LayerDrawable unSelectedLayerDrawable;
        LayerDrawable selectedLayerDrawable;
        GradientDrawable unSelectedGradientDrawable;
        unSelectedGradientDrawable = new GradientDrawable();
        unSelectedGradientDrawable.setShape(GradientDrawable.OVAL);
        unSelectedGradientDrawable.setColor(Color.GRAY);
        unSelectedGradientDrawable.setSize(dpToPx(8), dpToPx(8));
        unSelectedLayerDrawable = new LayerDrawable(new Drawable[]{unSelectedGradientDrawable});
        unSelectedDrawable = unSelectedLayerDrawable;
        //绘制选中状态图形
        GradientDrawable selectedGradientDrawable;
        selectedGradientDrawable = new GradientDrawable();
        selectedGradientDrawable.setShape(GradientDrawable.OVAL);
        selectedGradientDrawable.setColor(Color.RED);
        selectedGradientDrawable.setSize(dpToPx(8), dpToPx(8));
        selectedLayerDrawable = new LayerDrawable(new Drawable[]{selectedGradientDrawable});
        selectedDrawable = selectedLayerDrawable;
    }

    //添加本地图片路径
    public void setViewRes(List<Integer> viewRes) {
        List<View> views = new ArrayList<>();
        for (Integer res : viewRes) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            Glide.with(getContext()).load(res).into(imageView);
            views.add(imageView);
        }
        setViews(views);
    }

    //添加网络图片路径
    public void setViewUrls(List<String> urls) {
        List<View> views = new ArrayList<>();
        for (String url : urls) {
            ImageView imageView = new ImageView(getContext());
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            Glide.with(getContext()).load(url).into(imageView);
            views.add(imageView);
        }
        setViews(views);
    }

    //添加任意View视图
    public void setViews(final List<View> views) {
        //初始化指示器,并添加到指示器容器布局
        for (int i = 0; i < views.size(); i++) {
            ImageView indicator = new ImageView(getContext());
            indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            indicator.setPadding(dpToPx(3), dpToPx(3), dpToPx(3), dpToPx(3));
            indicatorContainer.addView(indicator);
        }
        LoopPagerAdapter pagerAdapter = new LoopPagerAdapter(views);
        pager.setAdapter(pagerAdapter);
        //设置当前item到Integer.MAX_VALUE中间的一个值,看起来像无论是往前滑还是往后滑都是ok的
        //如果不设置,用户往左边滑动的时候已经划不动了
        int currentItem = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % views.size();
        pager.setCurrentItem(currentItem);
        switchIndicator(currentItem % views.size());
        //添加监听器
        pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                switchIndicator(position % views.size());
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
        startAutoPlay();

    }


    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == WHAT_AUTO_PLAY) {
            pager.setCurrentItem(pager.getCurrentItem() + 1, true);
            handler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, DELAY_MILLIS);
        }
        return false;
    }

    /** * 开始自动轮播 */
    public void startAutoPlay() {
        if (!isAutoPlay) {
            handler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, DELAY_MILLIS);
            isAutoPlay = true;
        }
    }

    /** * 停止自动轮播 */
    public void stopAutoPlay() {
        if (isAutoPlay) {
            handler.removeMessages(WHAT_AUTO_PLAY);
            isAutoPlay = false;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                stopAutoPlay();
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                startAutoPlay();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    /** * 切换指示器状态 * * @param currentPosition 当前位置 */
    private void switchIndicator(int currentPosition) {
        for (int i = 0; i < indicatorContainer.getChildCount(); i++) {
            ((ImageView) indicatorContainer.getChildAt(i)).setImageDrawable(i == currentPosition ? selectedDrawable : unSelectedDrawable);
        }
    }

    /** * 单位转换将dp转化为px * * @param dp dp值 * @return 返回px */
    private int dpToPx(int dp) {
        DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
        return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics) + 0.5);
    }

}

经过修改SliderLayout布局已经添加了自动轮播的效果

最后就是添加各种各样的动画了,网上例子比较多,简单教大家下窍门

 //设置动画
        pager.setPageTransformer(true, new ViewPager.PageTransformer() {
            @Override
            public void transformPage(View page, float position) {
                if (position < -1.0f) {
                    // [-Infinity,-1)
                    // 左边页面处于不可见状态
                } else if (position <= 0.0f) {
                    // [-1,0]
                    // 左边页面显示
                } else if (position <= 1.0f) {
                    // (0,1]
                    //左边页面显示
                } else {
                    // (1,+Infinity]
                    // 右边页面处于不可见状态
                }

            }
        });

这个一定要理解,推荐大家看慕课网千变万化的ViewPager教程
简单演示

完整demo已上传到github SliderLayout

你可能感兴趣的:(android,viewpager,广告,滑动器)