1.效果图
2.核心代码
(1).ViewPager的Adapter父类
/**
* Created by wanghaibo on 2020/12/29.
* 带缓存的ViewPager适配器
* Data 数据
* Holder viewHolder
*/
public abstract class BaseViewPagerAdapter<Data, Holder extends BaseViewPagerAdapter.ViewHolder> extends PagerAdapter implements View.OnClickListener, ViewPager.OnPageChangeListener {
protected Stack<View> cacheViews = new Stack<>();//缓存
protected boolean isLoop;//是否无限循环
protected Context mContext;
protected List<Data> datas = new ArrayList<>();//数据
protected List<Holder> holders = new ArrayList<>();
protected Holder curHolder;
protected int curPosition;//当前下标
protected DataItemInnerClickListener<Data> itemInnerClickListener;//内部点击事件
protected Constructor holderConstructor;//holder的构造器
public BaseViewPagerAdapter(Context mContext) {
this.mContext = mContext;
initHolderClass();
}
public BaseViewPagerAdapter(Context context, List<Data> datas) {
this.mContext = context;
this.datas.clear();
if (datas != null) {
this.datas.addAll(datas);
}
initHolderClass();
}
protected abstract int layoutId();
protected abstract void bindData(Holder holder, Data itemData, int position);
public void setDatas(List<Data> data, ViewPager viewPager) {
this.datas.clear();
if (data != null && data.size() > 0) {
this.datas.addAll(data);
}
ViewGroup parent = (ViewGroup) viewPager.getParent();
int i = parent.indexOfChild(viewPager);
parent.removeView(viewPager);
viewPager.addOnPageChangeListener(this);
notifyDataSetChanged();
viewPager.setCurrentItem(getStartIndex(), false);
parent.addView(viewPager, i);
}
public List<Data> getDatas() {
return datas;
}
public boolean isLoop() {
return isLoop;
}
/**
* 设置是否无限循环
*
* @param loop
*/
public void setLoop(boolean loop) {
isLoop = loop;
}
public Holder getCurHolder() {
return curHolder;
}
public void setItemInnerClickListener(DataItemInnerClickListener<Data> itemInnerClickListener) {
this.itemInnerClickListener = itemInnerClickListener;
}
/**
* 初始化Holder的泛型Class以及构造器
*/
private void initHolderClass() {
//从一个泛型类型中获取泛型参数的类型类数组。
Type[] actualTypeArguments = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
Class<? extends BaseViewPagerAdapter.ViewHolder> c = (Class<? extends BaseViewPagerAdapter.ViewHolder>) actualTypeArguments[1];
try {
/*以下调用带参的、私有构造函数*/
LOG.e("-------------class----------" + c.getSimpleName());
holderConstructor = c.getConstructor(new Class[]{View.class});
holderConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* bindData中给iteme的子View设置点击事件,对应外部的 {@link #setItemInnerClickListener(DataItemInnerClickListener)}
*
* @param view
* @param position
*/
protected void setItemInnerClickForView(View view, int position) {
view.setOnClickListener(this);
view.setTag(R.id.inner_view_position_tag, position);
}
/**
* 获取初始化的坐标
*
* @return
*/
public int getStartIndex() {
int size = datas.size();
if (isLoop && size > 1) {
return Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % datas.size();
} else {
return size;
}
}
/**
* 获得真实数据下标
*
* @param index
* @return
*/
public int getRealPosition(int index) {
int size = datas.size();
if (isLoop && size > 1) {
return index % size;
} else {
return index;
}
}
@Override
public int getCount() {
int size = datas.size();
if (isLoop && size > 1) {
return Integer.MAX_VALUE;
} else {
return size;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Holder holder = null;
View view = null;
if (cacheViews.empty()) {
view = LayoutInflater.from(mContext).inflate(layoutId(), container, false);
try {
holder = (Holder) holderConstructor.newInstance(new Object[]{view});
holders.add(holder);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
view.setTag(holder);
} else {
view = cacheViews.pop();
holder = (Holder) view.getTag();
}
position = getRealPosition(position);
if (position == curPosition) {
curHolder = holder;
}
holder.position = position;
bindData(holder, datas.get(position), position);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
cacheViews.push(view);
}
@Override
public int getItemPosition(Object object) {
View view = (View) object;
Holder holder = (Holder) view.getTag();
if (holder != null) {
final int curPos = holder.position;
//只刷新当前页面
if (curPosition != curPos) {
return POSITION_UNCHANGED;
}
}
return POSITION_NONE;
}
@Override
public void onClick(View v) {
Object innerTag = v.getTag(R.id.inner_view_position_tag);
if (innerTag != null) {
int position = getRealPosition((int) innerTag);
if (itemInnerClickListener != null) {
Data item = null;
if (datas.size() > position) {
item = datas.get(position);
}
itemInnerClickListener.onItemInnerClick(item, v, position);
}
}
}
/**
* 清理缓存资源
*/
public void clear() {
stopAutoScroll();
cacheViews.clear();
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
curPosition = getRealPosition(position);
for (int i = 0; i < holders.size(); i++) {
Holder holder = holders.get(i);
if (holder.position == curPosition) {
curHolder = holder;
break;
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
private static TimeHandler handler;
/**
* 开启自动轮播
*/
public void startAutoScroll(ViewPager viewPager) {
if (handler == null) {
handler = new TimeHandler(viewPager);
}
}
/**
* 停止自动轮播
*/
public void stopAutoScroll() {
if (handler != null) {
handler.cancel();
handler = null;
}
}
static class TimeHandler extends Handler {
WeakReference<ViewPager> viewPagerWeakReference;
Thread thread;
//间隔时间
private int SPACE = 2;
//自动滚动信号
private final int SCROLL = 100002;
private boolean isCancel;
public TimeHandler(ViewPager viewPager) {
this.viewPagerWeakReference = new WeakReference<>(viewPager);
thread = new Thread(new Runnable() {
@Override
public void run() {
while (!isCancel) {
try {
Thread.sleep(SPACE * 1000);
if (handler != null) {
handler.sendEmptyMessage(SCROLL);
}
} catch (InterruptedException e) {
}
}
}
});
thread.start();
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == SCROLL)
if (viewPagerWeakReference != null && viewPagerWeakReference.get() != null) {
ViewPager viewPager = viewPagerWeakReference.get();
BaseViewPagerAdapter adapter = (BaseViewPagerAdapter) viewPager.getAdapter();
if (viewPager != null && adapter != null) {
int size = adapter.getCount();
int cur = viewPager.getCurrentItem();
if (size > 0) {
if (cur < size - 1) {
viewPager.setCurrentItem(cur + 1);
} else {
viewPager.setCurrentItem(0);
}
}
}
} else {
thread.interrupt();
}
}
public void cancel() {
this.isCancel = true;
this.thread.interrupt();
}
}
/**
* viewHolder父类,BaseViewPagerAdapter子类使用holder必须集成该类
*/
public static class ViewHolder {
protected View itemView;
protected int position;
public ViewHolder(View itemView) {
this.itemView = itemView;
}
}
}
(2).自定义指示器
/**
* Created by wanghaibo on 2020/12/30.
* ViewPager指示器
*/
public class ViewPagerIndicator extends LinearLayout implements ViewPager.OnPageChangeListener {
private int indicatorDrawableRes;//选择和未选择图片资源
private int indicatorWH;//指示器宽度
private int indicatorSpace;//指示器间距
private ViewPager viewPager;
private BaseViewPagerAdapter adapter;
public ViewPagerIndicator(Context context) {
this(context, null);
}
public ViewPagerIndicator(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ViewPagerIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(attrs);
}
private void initView(AttributeSet attrs) {
setGravity(Gravity.CENTER);
setOrientation(HORIZONTAL);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
indicatorWH = typedArray.getDimensionPixelSize(R.styleable.ViewPagerIndicator_indicatorWH, 10);
indicatorSpace = typedArray.getDimensionPixelSize(R.styleable.ViewPagerIndicator_indicatorSpace, 10);
indicatorDrawableRes = typedArray.getResourceId(R.styleable.ViewPagerIndicator_indicatorDrawable, R.drawable.bg_view_pager_indicator_selector);
typedArray.recycle();
}
public void bindViewPager(ViewPager viewPager, BaseViewPagerAdapter adapter) {
this.viewPager = viewPager;
this.adapter = adapter;
this.viewPager.addOnPageChangeListener(this);
ViewTreeObserver viewTreeObserver = viewPager.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
initIndicatorViews();
}
});
}
private void initIndicatorViews() {
int size = adapter.getDatas().size();
if (getChildCount() == size) {
return;
}
removeAllViews();
for (int i = 0; i < size; i++) {
View view = new View(getContext());
LayoutParams layoutParams = new LayoutParams(indicatorWH, indicatorWH);
if (i == 0) {
view.setSelected(true);
} else {
layoutParams.setMargins(indicatorSpace, 0, 0, 0);
}
view.setBackgroundResource(indicatorDrawableRes);
addView(view, layoutParams);
}
}
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
changeIndicator(adapter.getRealPosition(i));
}
@Override
public void onPageScrollStateChanged(int i) {
}
int oldSelectedPosition = 0;
private void changeIndicator(int position) {
if (adapter != null && adapter.getCount() > 0) {
getChildAt(oldSelectedPosition).setSelected(false);
getChildAt(position).setSelected(true);
oldSelectedPosition = position;
}
}
}
(3).指示器自定义属性,values/attrs.xml里添加
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ViewPagerIndicator">
<attr name="indicatorDrawable" format="reference" />
<attr name="indicatorWH" format="dimension" />
<attr name="indicatorSpace" format="dimension" />
</declare-styleable>
</resources>
(4).点击监听器
/**
* 列表中的item被点击事件
*/
public interface DataItemInnerClickListener<T> {
void onItemInnerClick(T data, View v, int position);
}
2.使用示例
(1).继承adapter父类实现
/**
* Created by wanghaibo50 on 2021/3/3.
*/
public class RecommendViewPagerAdapter extends BaseViewPagerAdapter<AdvertisementVo, RecommendViewPagerAdapter.RecommendViewHolder> {
RequestOptions options;
public RecommendViewPagerAdapter(Context mContext) {
super(mContext);
}
public RecommendViewPagerAdapter(Context context, List<AdvertisementVo> datas) {
super(context, datas);
}
@Override
protected int layoutId() {
return R.layout.layout_recommend_image_view;
}
@Override
protected void bindData(RecommendViewHolder holder, final AdvertisementVo itemData, int position) {
//图片圆角
if (options == null) {
options = RequestOptions.bitmapTransform(new RoundedCorners(mContext.getResources().getDimensionPixelSize(R.dimen.home_round_corners)));
}
setItemInnerClickForView(holder.llContent, position);
Glide.with(mContext).load(itemData.getImgUrl()).apply(options).placeholder(R.drawable.ic_img_def).into(holder.imageView);
}
//这里注意holder必须为公共静态类
public static class RecommendViewHolder extends BaseViewPagerAdapter.ViewHolder {
public ImageView imageView;
public LinearLayout llContent;
public RecommendViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.iv_recommend);
llContent = itemView.findViewById(R.id.ll_content);
}
}
}
(2).ViewPager子View布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rl_recomment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.jdjr.smartrobot.ui.views.widget.ViewPagerIndicator
android:id="@+id/vpi_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="10dp"
app:indicatorDrawable="@drawable/bg_view_pager_indicator_selector"
app:indicatorSpace="6dp"
app:indicatorWH="5dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
(3).指示器样式资源bg_view_pager_indicator_selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape android:shape="oval">
<solid android:color="@color/white" />
</shape>
</item>
<item android:state_selected="false">
<shape android:shape="oval">
<solid android:color="@color/color_4DFFFFFF" />
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="@color/color_4DFFFFFF" />
</shape>
</item>
</selector>
(3). 调用
public class MainActivity extends AppCompatActivity {
private List<AdvertisementVo> advertisementVos;
private ViewPager viewPager;
private ViewPagerIndicator vpiIndicator;
private RecommendViewPagerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
inttData();
}
protected void initView() {
viewPager = findViewById(R.id.vp_layout);
vpiIndicator = findViewById(R.id.vpi_indicator);
adapter = new RecommendViewPagerAdapter(this);
viewPager.setAdapter(adapter);
adapter.setLoop(true);
adapter.setItemInnerClickListener(this);
vpiIndicator.bindViewPager(viewPager, adapter);
}
protected void initData() {
//此处省略了获取数据的过程
adapter.setDatas(advertisementVos, viewPager);
adapter.startAutoScroll(viewPager);
}
}
完结撒花