继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android 图片轮播器的实现及源码解析

繁华开满天机
关注TA
已关注
手记 134
粉丝 15
获赞 68

在很多产品,尤其是电商类社区内的网页或者app中,我们经常会看到一个图片轮播墙,一页一页的广告/活动/商品介绍每隔一段时间就切换到下一张。那在安卓中我们该如何实现图片轮播器呢?面对自定义样式、自定义图片加载框架等等复杂的自定义需求,如何设计接口使得使用者可以很方便的自定义属性呢?接下来我从wangyeming/ImageBanner源码出发,探讨下我对这个小小功能框架的设计和实现。



图片轮播,一页两页,一页两页

首先明确需求:

1.       图片轮播器由若干张不定的页面构成,每个页面上的元素包括:图片(必选) + 指示器(可选,可能是点点点,可能是数字等)

2.       可以手势滑动图片的切换

3.       闭环展示,第一张的左边是最后一张,最后一张的右边是第一张,无限循环播放。

看一下我画的设计结构图(很丑,轻拍)


可以看到有这么几个对象:

ImageBanner:自定义控件,包含定时任务管理器TimerController、增强ViewPager、指示器BannerIndicator。内部包含了诸如开启,关闭轮播等逻辑。设计为抽象类,通过钩子方法实现UI样式的自定义。

TimerController: 定时任务管理器, 无论是Timer也好,手动设计的定时Handler也好,它的职责就是执行定时任务,具体到图片轮播器里,职责就通知CirclePageAdapter和BannerIndicator切换到下一张。

CustomSwipeViewPager: 增强ViewPager, 方便随时禁止和开启手势滑动。

CirclePageAdapter: ViewPager的adapter, 通过在左右两边各增加一个伪Pager,滑动到0,和最后一个时,无动画切换到最后一个和0,从而实现循环滑动。同样设计为抽象类,ImageView的样式,图片加载的方式等同样通过钩子方法留出来供使用者自定义。

BannerIndicator: 指示器,设计成接口形式,实现该接口的自定义View都可以作为轮播器当中的指示器。最大程度自定义UI。

如何使用?

派生CirclePageAdapter,实现单个图片加载的样式和点击事件

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public class CustomCirclePageAdapter   extends CirclePageAdapter<bannerimage>   {

    public CustomCirclePageAdapter(Context context)   {

        super(context);

    }

    @Override

    protected void showImage(ImageView vImage, BannerImage bannerImage) {

        //自定义采用何种图片加载方式

        Glide.with(mContext)

            .load(bannerImage.getImagePath())

            .placeholder(R.drawable.default_loading)

            .error(R.drawable.topic_pic)

            .dontAnimate()

            .into(vImage);

    }

    @Override

    protected void onClickImage(final BannerImage bannerImage) {

        //自定义每张图片的点击事件

        Uri   uri = Uri.parse(bannerImage.getLink());

        Intent   intent = new Intent(Intent.ACTION_VIEW,   uri);

        try {

            mContext.startActivity(intent);

        }   catch (Exception   e) {

           e.printStackTrace();

        }

    }

    @Override

    protected ImageView createImageView() {

        //自定义图片的样式

        ImageView   vImage = new ImageView(mContext);

        vImage.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,   DemoUtil.dp2px(mContext, 100)));

        vImage.setScaleType(ImageView.ScaleType.FIT_XY);

        return vImage;

    }

}</bannerimage>

自定义指示器(可选),实现BannerIndicator接口即可

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

public class CustomBannerIndicator   extends LinearLayout   implements BannerIndicator   {

    private List<imageview> vimg = new ArrayList<>();

    public CustomBannerIndicator(Context context) {

        this(context,   null);

    }

    public CustomBannerIndicator(Context context,   AttributeSet attrs) {

        this(context,   attrs, 0);

    }

    public CustomBannerIndicator(Context context,   AttributeSet attrs, int defStyleAttr) {

        super(context,   attrs, defStyleAttr);

        init();

    }

    private void init() {

        setOrientation(HORIZONTAL);

    }

    @Override

    public void showInitState(int imageCount) {

        for (int i = 0; i < imageCount; i++) {

            ImageView   vImage = new ImageView(getContext());

            LinearLayout.LayoutParams   layoutParams = new LinearLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,   FrameLayout.LayoutParams.WRAP_CONTENT);

            int margin = DemoUtil.dp2px(getContext(), 10);

            layoutParams.setMargins(margin,   0, margin, 0);

            vImage.setLayoutParams(layoutParams);

            vimg.add(vImage);

            vImage.setBackgroundResource(i   == 0 ?   R.drawable.dot_choosen_ic : R.drawable.dot_unchoosen_ic);

            addView(vImage);

        }

    }

    @Override

    public void notifyIndexChanged(int indexOfImage) {

        for (int i = 0; i < vimg.size(); i++) {

            vimg.get(i).setBackgroundResource(i   == indexOfImage ? R.drawable.dot_choosen_ic : R.drawable.dot_unchoosen_ic);

        }

    }

}</imageview>

配置ImageBanner

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public class CustomImageBanner   extends ImageBanner<bannerimage>   {

    public CustomImageBanner(Context context) {

        super(context);

    }

    public CustomImageBanner(Context context,   AttributeSet attrs) {

        super(context,   attrs);

    }

    public CustomImageBanner(Context context,   AttributeSet attrs, int defStyleAttr) {

        super(context,   attrs, defStyleAttr);

    }

    @Override

    protected int getLayoutRes() {

        return R.layout.custom_banner;

    }

    @Override

    protected int getImagePagerViewId() {

        return R.id.image_parer;

    }

    @Override

    protected CirclePageAdapter<bannerimage>   initCirclePageAdapter() {

        return new CustomCirclePageAdapter(getContext());

    }

    @Override

    protected int getBannerIndicatorViewId() {

        return R.id.image_indicator;

    }

}</bannerimage></bannerimage>

在xml中使用:

[代码]xml代码:

?

1

2

3

4

5

<com.tianyeguang.imagebanner.banner.CustomImageBanner

        android:id="@+id/custom_image_banner"

        android:layout_width="match_parent"

        android:layout_height="136dp"

        android:layout_margin="16dp"/>

传入数据,正确的开启和关闭轮播的定时器:

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

@Override

protected void onResume()   {

    super.onResume();

    vBanner.start();

}

@Override

protected void onPause()   {

    vBanner.finish();

    super.onPause();

}

具体的代码大家可以查看demo, demo的样式就是博文上的示意图,谢谢大家的阅读~

原文链接:http://www.apkbus.com/blog-705730-62533.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP