先看一下效果图(左右无限滑动,自动轮播)
地铁上看了一个大神的博客,自定义了通用viewpager,但是没有自动轮播和左右无限滑动功能,自己尝试写了一下。在这给大家分享一下,也给大神传播一下自定义控件的思想。(后面会给大神的链接)
实现(作者仿recyclerview的封装方法)
第一步:定义一个ViewHolder接口来提供布局和绑定数据
ViewPagerHolder 接收一个泛型T,其实是绑定数据要用的实体类。其中有2个方法,一个提供给Adapter布局,另一个则用于绑定数据。
public interface ViewPagerHolder<T> {
/**
* 创建View
*/
View createView(Context context);
/**
* 绑定数据
*/
void onBind(Context context, int position, T data);
}
第二步: 封装一个ViewPagerHolderCreator,用来创建各种ViewHolder
该类接受一个 泛型,但是必须得是ViewPagerHolder 的子类
public interface ViewPagerHolderCreator<VH extends ViewPagerHolder> {
/**
* 创建ViewHolder
*/
public VH createViewHolder();
}
第三步: 重写 ViewPager 的Adapter:
public class CommonViewPagerAdapter<T> extends PagerAdapter {
private List<T> mDatas;//数据
private int MAXSIZE;//传入最大值,用来左右无限滑动功能
private ViewPagerHolderCreator mCreator;//ViewHolder生成器
public CommonViewPagerAdapter(List<T> datas, ViewPagerHolderCreator creator, int MAXSIZE) {
this.mDatas = datas;
this.mCreator = creator;
this.MAXSIZE = MAXSIZE;
}
@Override
public int getCount() {
//返回最大个数
return MAXSIZE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//重点就在这儿了,不再是把布局写死,而是用接口提供的布局
// 也不在这里绑定数据,数据绑定交给Api调用者。
View view = getView(position, null, container);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
/**
* 获取viewPager 页面展示View
*/
private View getView(int position, View view, ViewGroup container) {
position = position % mDatas.size();
ViewPagerHolder holder = null;
if (view == null) {
//创建Holder
holder = mCreator.createViewHolder();
view = holder.createView(container.getContext());//创建自己需求的view
view.setTag(R.id.mz_banner_item_tag, holder);//id在values文件下ids
} else {
holder = (ViewPagerHolder) view.getTag(R.id.mz_banner_item_tag);
}
if (holder != null && mDatas != null && mDatas.size() > 0) {
// 数据绑定
holder.onBind(container.getContext(), position, mDatas.get(position));
}
return view;
}
}
第四部:包装ViewPager
viewpager布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ViewPager-->
<android.support.v4.view.ViewPager
android:id="@+id/common_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- 指示器 -->
<LinearLayout
android:id="@+id/common_view_points"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:orientation="horizontal" />
</RelativeLayout>
public class CommonViewPager<T> extends RelativeLayout implements ViewPager.OnPageChangeListener {
private ViewPager mViewPager;
private CommonViewPagerAdapter mAdapter;
private LinearLayout mPoints;//指示器
private int MAXSIZE = 0;//最大值
private int delayTime = 0;//自动轮播时间
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
//System.out.println("ViewPager切换到下一页");
// 把ViewPager切换到下一页
mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
// 再次发送消息(自身的方法中也能使用自己的handler方法,实现死循环)
handler.sendMessageDelayed(Message.obtain(), delayTime);
};
};
public CommonViewPager(@NonNull Context context) {
super(context);
init();
}
public CommonViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CommonViewPager(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CommonViewPager(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init(){
View view = LayoutInflater.from(getContext()).inflate(R.layout.common_view_pager_layout,this,true);
mViewPager = (ViewPager) view.findViewById(R.id.common_view_pager);
mViewPager.addOnPageChangeListener(this);
mPoints = (LinearLayout) view.findViewById(R.id.common_view_points);
}
/**
* 设置数据
* @param data
* @param creator
*/
public void setPages(List<T> data, ViewPagerHolderCreator creator,int pointMargin,int delayTime ){
this.delayTime = delayTime;
//设置指示器的点数
initPoints(data.size(),pointMargin);
//设置最大值 img在自己初始化数据以后才可以显示
MAXSIZE = 500000 * data.size() * 2;
mAdapter = new CommonViewPagerAdapter(data,creator,MAXSIZE);
mViewPager.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
// 初始化第0个白点
View childAt = mPoints.getChildAt(0);
childAt.setBackgroundResource(R.mipmap.white);
// 往左无限滑动,给ViewPager显示一个中间的比较大的位置
mViewPager.setCurrentItem(MAXSIZE / 2);
// 使用Handler发送延迟消息
handler.sendMessageDelayed(Message.obtain(), delayTime);
}
private void initPoints(int size,int pointMargin) {
// 添加图片数据
for (int i = 0; i < size; i++) {
// 有几张图片创建几个点
ImageView point = new ImageView(MyApplication.getContext());
point.setBackgroundResource(R.mipmap.black);
// 吧点添加线性布局 points是一个lineaulayout的布局
mPoints.addView(point);
// 设置点之间的距离(注意LayoutParams的导包,这里导包LinearLayout的)
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) point.getLayoutParams();
layoutParams.leftMargin = pointMargin;
point.setLayoutParams(layoutParams);
}
}
public void setCurrentItem(int currentItem){
mViewPager.setCurrentItem(currentItem);
}
public int getCurrentItem(){
return mViewPager.getCurrentItem();
}
public void setOffscreenPageLimit(int limit){
mViewPager.setOffscreenPageLimit(limit);
}
/**
* 设置切换动画,see {@link ViewPager#setPageTransformer(boolean, ViewPager.PageTransformer)}
*/
public void setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer){
mViewPager.setPageTransformer(reverseDrawingOrder,transformer);
}
public void setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer,
int pageLayerType) {
mViewPager.setPageTransformer(reverseDrawingOrder,transformer,pageLayerType);
}
/**
* see {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)}
* @param listener
*/
public void addOnPageChangeListener(ViewPager.OnPageChangeListener listener){
mViewPager.addOnPageChangeListener(listener);
}
/**
* 设置是否显示Indicator
* @param visible
*/
private void setIndicatorVisible(boolean visible){
if(visible){
mPoints.setVisibility(VISIBLE);
}else{
mPoints.setVisibility(GONE);
}
}
public ViewPager getViewPager() {
return mViewPager;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//记录之前白点位置
int preWhitePosition = 0;
@Override
public void onPageSelected(int position) {
//position取余,防止越界
position = position % mPoints.getChildCount();
// 先把之前的白点变黑色
mPoints.getChildAt(preWhitePosition).setBackgroundResource(
R.mipmap.black);
// 当滑动到某一页时,把当前位置的点变白色
mPoints.getChildAt(position).setBackgroundResource(R.mipmap.white);
// 记录白点的位置//这个顺序不能换
preWhitePosition = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
简便使用
public class MainActivity extends AppCompatActivity {
private CommonViewPager mCommonViewPager;
private List<ViewpagerData> mData = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initData() {
mData.clear();
mData.add(new ViewpagerData(R.mipmap.banners1, "这是第一张图片"));
mData.add(new ViewpagerData(R.mipmap.banners2, "这是第二张图片"));
mData.add(new ViewpagerData(R.mipmap.banners3, "这是第三张图片"));
mData.add(new ViewpagerData(R.mipmap.banners4, "这是第四张图片"));
mData.add(new ViewpagerData(R.mipmap.banners5, "这是第五张图片"));
}
private void initView() {
mCommonViewPager = (CommonViewPager) findViewById(R.id.activity_common_view_pager);
// 设置数据
//参数1 数据 2 holder 3 指示器间距 4 自动轮播时间
mCommonViewPager.setPages(mData, new ViewPagerHolderCreator<ViewImageHolder>() {
@Override
public ViewImageHolder createViewHolder() {
// 返回ViewPagerHolder
return new ViewImageHolder();
}
}, 10,3000);
}
/**
* 提供ViewPager展示的ViewHolder
* <P>用于提供布局和绑定数据</P>
*/
class ViewImageHolder implements ViewPagerHolder<ViewpagerData> {
private ImageView mImageView;
private TextView mTextView;
@Override
public void onBind(Context context, int position, ViewpagerData data) {
// 数据绑定
// 自己绑定数据,灵活度很大
mImageView.setImageResource(data.getImageID());
mTextView.setText(data.getDesc());
}
@Override
public View createView(Context context) {
// 返回ViewPager 页面展示的布局(根据自己的需求自己定义)
View view = LayoutInflater.from(context).inflate(R.layout.view_pager_item, null);
mImageView = (ImageView) view.findViewById(R.id.viewPager_item_image);
mTextView = (TextView) view.findViewById(R.id.item_desc);
return view;
}
}
}
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.rmyh.myviewpager.MainActivity">
<com.rmyh.myviewpager.CommonViewPager
android:id="@+id/activity_common_view_pager"
android:layout_width="match_parent"
android:layout_height="200dp"></com.rmyh.myviewpager.CommonViewPager>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/viewPager_item_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
<TextView
android:id="@+id/item_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:gravity="center"
android:layout_centerInParent="true"
android:textColor="@android:color/white"
/>
</RelativeLayout>
写在后头
不足的地方很多,还望指出。如果有幸和您需求一样,拷过去直接使用ok了。
使用提醒
1.每个项目用的图片加载框架是不一样的,Picasso、Glide、ImageLoader等等各不相同,那么我们还需要在显示图片的时候换成自己用的图片加载框架才行。
2.并不是所有的Banner 都只是显示一张图片,还有各种个样的文案展示等等,因此不能个性化定制,这是比较致命的。