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

在很多产品,尤其是电商类社区内的网页或者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

34public class CustomCirclePageAdapter   extends CirclePageAdapter   {

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;

}

}

自定义指示器(可选),实现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

35public class CustomBannerIndicator   extends LinearLayout   implements BannerIndicator   {

private List 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);

}

}

}

配置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

27public class CustomImageBanner   extends ImageBanner   {

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   initCirclePageAdapter() {

return new CustomCirclePageAdapter(getContext());

}

@Override

protected int getBannerIndicatorViewId() {

return R.id.image_indicator;

}

}

在xml中使用:

[代码]xml代码:1

2

3

4

5

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的样式就是博文上的示意图,谢谢大家的阅读~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值