作为一个刚工作的菜鸟,连个焦点轮播图都撸不出来,真的是感觉没脸见人,查阅多方资料,磕磕绊绊也算是做出来了,但由于不会写,大部分代码也是拼凑起来的,代码不美观不说,复用起来也是极为的不易,寻思还是从头整理一下,做个自定义View,日后复用,查看也更为顺手。
说是自定义view,其实也只是组合view,将不同功能的view组合在一起。
STEP.ONE 滑动展示图片
通过ViewPager可以容易的实现可滑动的展示图片的view。新建三个文件:
- custom_banner.xml,自定义View布局
布局文件,很简单,随着功能的完善,还会添加view
- Banner.java,自定义view
public class Banner extends RelativeLayout {
private String TAG = "Banner";
private View mView;
private ViewPager mImage;
private List mImageList = new ArrayList<>();
public Banner(Context context, AttributeSet attrs) {
super(context, attrs);
mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
init();
}
private void init() {
mImage = mView.findViewById(R.id.vp_image);
mImageList = new ArrayList<>();
mImageList.add(R.mipmap.img_1);
mImageList.add(R.mipmap.img_2);
mImageList.add(R.mipmap.img_3);
ImageAdapter adapter = new ImageAdapter(mImage,mImageList);
mImage.setAdapter(adapter);
}
}
public Banner(Context context, AttributeSet attrs)引入布局,init()中简单的添加了几个数据,通过adapter加载。
- ImageAdapter.java,adapter
public class ImageAdapter extends PagerAdapter {
private String TAG = "ImageAdapter";
private ViewPager mImage;
private List mImageList = new ArrayList<>();
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
public ImageAdapter( ViewPager mImage, List mImageImageList) {
this.mImage = mImage;
this.mImageList = mImageImageList;
this.DEFAULT_BANNER_SIZE = mImageImageList.size();
this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
}
@Override
public int getCount() {
return FAKE_BANNER_SIZE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position %= DEFAULT_BANNER_SIZE;
ImageView img = new ImageView(container.getContext());
img.setImageResource(mImageList.get(position));
container.addView(img);
return img;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//super.destroyItem(container, position, object);
container.removeView((View) object);
}
}
instantiateItem()加载子view,定义了两个变量DEFAULT_BANNER_SIZE和FAKE_BANNER_SIZE,在循环滑动时会用到。
到这里最简单的ViewPager的用法了,这里还有一点需要注意,ViewPager需要重写onMeasure()方法,测量child的高度作为自身高度,否则会match_parent,在banner.java中重写onMeasure()。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxHeight = 0;
for (int i = 0; i < mImage.getChildCount(); i++) {
View child = mImage.getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxHeight = Math.max(maxHeight,child.getMeasuredHeight());
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
测量child的高度,最大值作为父容器高度。
STEP.TWO 实现循环与自动播放
实现循环重写PagerAdapter中的finishUpdate(),自动播放使用轻量级的定时器Timer。
- 循环。在ImageAdapter.java中重写finishUpdate()。
public class ImageAdapter extends PagerAdapter {
private String TAG = "ImageAdapter";
private ViewPager mImage;
private List mImageList = new ArrayList<>();
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
public ImageAdapter( ViewPager mImage, List mImageImageList) {
this.mImage = mImage;
this.mImageList = mImageImageList;
this.DEFAULT_BANNER_SIZE = mImageImageList.size();
this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
}
@Override
public int getCount() {
return FAKE_BANNER_SIZE;
.
.
.
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
int curPosition = mImage.getCurrentItem();
if (0 == curPosition){
//滑动到最左边
mImage.setCurrentItem(DEFAULT_BANNER_SIZE, false);
}
if ((FAKE_BANNER_SIZE -1) == curPosition){
//滑动到最右边
mImage.setCurrentItem(FAKE_BANNER_SIZE - DEFAULT_BANNER_SIZE - 1, false);
}
}
}
DEFAULT_BANNER_SIZE是图片列表的大小,FAKE_BANNER_SIZE是三倍图片列表的小。
以代码为例,目前我们的ViewPager有3屏(DEFAULT_BANNER_SIZE),但是如果在PagerAdapter的getCount方法中我们返回9(FAKE_BANNER_SIZE),即告诉ViewPager我们有9屏。那么如果当前位于第3屏,由于对于ViewPager来说它认为有9屏,所以我们仍然可以向后滑动。当滑动到第9屏时(右边界),立即将第6屏的view返回给他,这样ViewPager依然可以向右滑动。同理处于第1屏时(左边界),讲第4屏的view返回给他,此时仍然可以左滑。这样便实现了循环播放。如图..............hahah
- 自动播放
public class Banner extends RelativeLayout {
private int DEFAULT_BANNER_SIZE;
private int FAKE_BANNER_SIZE;
//{start update 实现自动播放
@SuppressLint("HandlerLeak")
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
int position = (mImage.getCurrentItem() + 1) % DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
mImage.setCurrentItem(position, true);
Log.d(TAG, "handleMessage: "+position);
break;
default:
break;
}
}
};
// end }
private List mImageList = new ArrayList<>();
public Banner(Context context, AttributeSet attrs) {
super(context, attrs);
mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
init();
//调用
startTimer();
}
//{start update
private void startTimer() {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
timer.schedule(task,5000,5000);
}
//end }
}
使用Timer定义一个5秒执行的任务,通过Headler异步执行。
STEP.THREE 底部圆点
根据mImageList动态添加底部圆点。
- 修改custom_banner.xml文件
新增了LinearLayout布局,添加圆点的任务就交给他了。
- 修改Banner.java
private void initPoint() {
for (int i = 0; i < mImageList.size(); i++) {
View view = new View(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
params.leftMargin = 10;
params.rightMargin = 10;
view.setLayoutParams(params);
if (i == 0) {
view.setBackgroundResource(R.drawable.shape_point_select);
} else {
view.setBackgroundResource(R.drawable.shape_point_default);
}
final int finalI = i;
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int item = finalI+DEFAULT_BANNER_SIZE;
mImage.setCurrentItem(item, true);
}
});
mPoint.addView(view);
mPointList.add(view);
}
}
在mImageList初始化后执行该函数,添加圆点,背景,和点击事件。
- 发生点击事件后,修改圆点背景
mImage.addOnPageChangeListener(this);
@Override
public void onPageSelected(int position) {
position %= DEFAULT_BANNER_SIZE;
for (View view : mPointList) {
view.setBackgroundResource(R.drawable.shape_point_default);
}
mPointList.get(position).setBackgroundResource(R.drawable.shape_point_select);
}
添加监听,当某页面被选中时,先遍历列表,将所有圆点都设置为未选中状态,再将对应页面的背景设置为选中背景。
END
到这里这个功能就已经全部实现了,这也是我的第一篇技术文章,参考了挺多大大的博客,最后倒腾出来。文章感觉写的干巴巴,一来我本人对有些也不算特别熟悉,二来文章写的少。坚持写博客,到自己的文章干货越来越多,越写越写流畅,自己的成长了多少也就很明确了。当然也希望这篇文章可以帮助到大家。
参考链接
循环广告位组件的实现
动态圆点轮播图
解决ViewPager高度无法wrap_content问题