无限轮播位
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.ju.video.R;
import com.ju.video.vendor.hisense.ad.models.Image;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class ImageSlideView<T extends Image> extends FrameLayout {
private static final String TAG = ImageSlideView.class.getSimpleName();
private static final int DEFAULT_GIF_WIDTH = 385;
private static final int DEFAULT_GIF_HEIGHT = 155;
private Context ctx;
private View contentView;
protected ViewPager vp;
protected TextView tips;
protected LinearLayout llDot;
private int count;
private List<View> viewList = new ArrayList<>();
private boolean isAutoPlay;
private Handler mainHandler;
private int currentItem;
private Animator animatorToLarge;
private Animator animatorToSmall;
private SparseBooleanArray isLarge;
private List<T> mImageBeanList;
private int dotSize = 12;
private int dotSpace = 12;
private int delay = 3000;
private SlideTask task;
public ImageSlideView(Context ctx) {
this(ctx, null);
}
public ImageSlideView(Context ctx, AttributeSet attrs) {
this(ctx, attrs, 0);
}
public ImageSlideView(Context ctx, AttributeSet attrs, int defStyleAttr) {
super(ctx, attrs, defStyleAttr);
this.ctx = ctx;
mainHandler = new Handler(Looper.getMainLooper());
// 初始化View
initView();
// 初始化Animator
initAnimator();
// 初始化数据
initData();
}
private void initData() {
mImageBeanList = new ArrayList<>();
}
private void initAnimator() {
animatorToLarge = AnimatorInflater.loadAnimator(ctx, android.R.animator.fade_out);
animatorToSmall = AnimatorInflater.loadAnimator(ctx, android.R.animator.fade_in);
}
/**
* 初始化View
*/
private void initView() {
contentView = LayoutInflater.from(ctx).inflate(R.layout.video_layout_image_slide, this, true);
vp = (ViewPager) findViewById(R.id.video_vp_image_title);
tips = (TextView) findViewById(R.id.video_tv_pause_tips);
llDot = (LinearLayout) findViewById(R.id.video_ll_dot);
}
// 设置小圆点的大小
protected void setDotSize(int dotSize) {
this.dotSize = dotSize;
}
// 设置小圆点的间距
protected void setDotSpace(int dotSpace) {
this.dotSpace = dotSpace;
}
// 设置图片轮播间隔时间
protected void setDelay(int delay) {
this.delay = delay;
}
// 设置图片和标题的JavaBean数据列表
protected void setDatas(List<T> list) {
this.mImageBeanList = list;
}
// 设置完后最终提交
protected void commit() {
if (ctx == null) {
Log.e(TAG, "context已空,释放实例");
return;
}
if (mImageBeanList != null) {
count = mImageBeanList.size();
// 设置ViewPager
setViewPager(mImageBeanList);
// 设置指示器
setIndicator();
// 设置tips
setTips();
// 开始播放
starPlay();
} else {
Log.e(TAG, "数据为空");
}
}
/**
* 设置指示器
*/
private void setIndicator() {
isLarge = new SparseBooleanArray();
// 记得创建前先清空数据,否则会受遗留数据的影响。
llDot.removeAllViews();
for (int i = 0; i < count; i++) {
View view = new View(ctx);
view.setBackgroundResource(0);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dotSize, dotSize);
layoutParams.leftMargin = dotSpace / 2;
layoutParams.rightMargin = dotSpace / 2;
layoutParams.topMargin = dotSpace / 2;
layoutParams.bottomMargin = dotSpace / 2;
llDot.addView(view, layoutParams);
isLarge.put(i, false);
}
llDot.getChildAt(0).setBackgroundResource(0);
animatorToLarge.setTarget(llDot.getChildAt(0));
animatorToLarge.start();
isLarge.put(0, true);
}
/**
* 设置提示语
*/
private void setTips() {
tips.setText(R.string.video_tips_close_ad_pause);
}
/**
* 开始自动播放图片
*/
private void starPlay() {
// 如果少于2张就不用自动播放了
if (count < 2) {
isAutoPlay = false;
} else {
isAutoPlay = true;
task = new SlideTask(this);
mainHandler.postDelayed(task, delay);
}
}
private static class SlideTask implements Runnable {
private WeakReference<ImageSlideView> ref;
public SlideTask(ImageSlideView view) {
ref = new WeakReference<ImageSlideView>(view);
}
@Override
public void run() {
ImageSlideView view = ref.get();
if (view == null) {
Log.e(TAG, "SlideTask run: return cause ref get null");
return;
}
if (view.isAutoPlay) {
// 位置循环
view.currentItem = view.currentItem % (view.count + 1) + 1;
// 正常每隔3秒播放一张图片
view.vp.setCurrentItem(view.currentItem);
view.mainHandler.postDelayed(view.task, view.delay);
} else {
// 如果处于拖拽状态停止自动播放,会每隔5秒检查一次是否可以正常自动播放。
view.mainHandler.postDelayed(view.task, 5000);
}
}
};
// 创建监听器接口
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
// 声明监听器
private OnItemClickListener onItemClickListener;
// 提供设置监听器的公共方法
public void setOnItemClickListener(OnItemClickListener listener) {
onItemClickListener = listener;
}
class ImageTitlePagerAdapter extends PagerAdapter {
private List<T> imageTitleList;
public ImageTitlePagerAdapter(List<T> imageTitleList) {
this.imageTitleList = imageTitleList;
}
@Override
public int getCount() {
return viewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int pos) {
if (pos >= viewList.size()) {
pos = viewList.size() - 1;
}
final int position = pos;
View view = viewList.get(position);
// 设置Item的点击监听器
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 位置是position-1, 还原业务层data list的position
if (null != onItemClickListener) {
onItemClickListener.onItemClick(v, position - 1);
}
T image = null;
if (position -1 < mImageBeanList.size()) {
image = imageTitleList.get(position - 1);
}
Log.d(TAG, "-- image onEnter(): image = " + image);
}
});
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int pos, Object object) {
if (pos >= viewList.size()) {
pos = viewList.size() - 1;
}
final int position = pos;
container.removeView(viewList.get(position));
}
}
/**
* 设置ViewPager
*
* @param list mImageBeanList
*/
private void setViewPager(List<T> list) {
// 设置View列表
createViewList(list);
vp.setAdapter(new ImageTitlePagerAdapter(list));
// 从第1张图片开始(位置刚好也是1,注意:0位置现在是最后一张图片)
currentItem = 1;
vp.setCurrentItem(1);
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// 遍历一遍子View,设置相应的背景。
for (int i = 0; i < llDot.getChildCount(); i++) {
if (i == position - 1) {// 被选中
llDot.getChildAt(i).setBackgroundResource(0);
if (!isLarge.get(i)) {
animatorToLarge.setTarget(llDot.getChildAt(i));
animatorToLarge.start();
isLarge.put(i, true);
}
} else {// 未被选中
llDot.getChildAt(i).setBackgroundResource(0);
if (isLarge.get(i)) {
animatorToSmall.setTarget(llDot.getChildAt(i));
animatorToSmall.start();
isLarge.put(i, false);
}
}
}
T image = null;
if (position -1 < mImageBeanList.size()) {
image = mImageBeanList.get(position - 1);
}
Log.d(TAG, "-- image onShow(): image = " + image);
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
// 闲置中
case ViewPager.SCROLL_STATE_IDLE:
// “偷梁换柱”
if (vp.getCurrentItem() == 0) {
vp.setCurrentItem(count, false);
} else if (vp.getCurrentItem() == count + 1) {
vp.setCurrentItem(1, false);
}
currentItem = vp.getCurrentItem();
isAutoPlay = true;
break;
// 拖动中
case ViewPager.SCROLL_STATE_DRAGGING:
isAutoPlay = false;
break;
// 设置中
case ViewPager.SCROLL_STATE_SETTLING:
isAutoPlay = true;
break;
}
}
});
}
/**
* 根据出入的数据设置View列表
*
* @param imageList mImageBeanList
*/
private void createViewList(List<T> imageList) {
viewList.clear();
for (int i = 0; i < count + 2; i++) {
View view = LayoutInflater.from(ctx).inflate(R.layout.video_layout_image_pause, null);
ImageView ivImage = (ImageView) view.findViewById(R.id.video_iv_page_image);
// TextView tvTitle = (TextView) view.findViewById(R.id.video_tv_page_title);
int fixIndex;
if (i == 0) {// 将最前面一页设置成本来最后的那页
fixIndex = count - 1;
} else if (i == count + 1) {// 将最后面一页设置成本来最前的那页
fixIndex = 0;
} else {
fixIndex = i - 1;
}
T image = imageList.get(fixIndex);
String url = image.url;
if (url.endsWith(".gif")) {
int width = image.specWidth;
int height = image.specHeight;
if (width <= 0 || height <= 0) {
width = DEFAULT_GIF_WIDTH;
height = DEFAULT_GIF_HEIGHT;
}
Glide.with(ctx)
.load(url)
.asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.override(width, height)
.into(ivImage);
} else {
Glide.with(ctx)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.centerCrop()
.into(ivImage);
}
// 将设置好的View添加到View列表中
viewList.add(view);
}
}
/**
* 释放轮播动画
*/
protected void stop() {
cancelAnims();
mainHandler.removeCallbacksAndMessages(null);
}
private void cancelAnims() {
animatorToLarge.cancel();
animatorToSmall.cancel();
}
/**
* 释放资源, 实例对象不再使用
*/
protected void releaseResource() {
ctx = null;
mImageBeanList.clear();
}
}
slide layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/video_layout_pause"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="@+id/video_vp_image_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:gravity="center_horizontal"/>
<!--自定义Indicator-->
<LinearLayout
android:id="@+id/video_ll_dot"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="192dp"
android:gravity="center"
android:orientation="horizontal">
</LinearLayout>
<TextView
android:id="@+id/video_tv_pause_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/video_vp_image_title"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:gravity="center_horizontal"
android:shadowColor="#FF000000"
android:shadowDx="2"
android:shadowDy="2"
android:shadowRadius="2"
android:textColor="@android:color/white"
android:textSize="18sp"/>
</RelativeLayout>
user case: with state machine
public class ADPauseView extends ImageSlideView<Image> implements IADImageView {
private static final String TAG = IADImageView.TAG;
private static final int PAUSE_WIDTH_1153 = 1153;
private static final int PAUSE_HEIGHT_1153 = 559;
private static final int PAUSE_MARGIN_LEFT_1153 = 384;
private static final int PAUSE_MARGIN_TOP_1153 = 218;
private static final int PAUSE_WIDTH_680 = 680;
private static final int PAUSE_HEIGHT_680 = 680;
private static final int PAUSE_MARGIN_LEFT_680 = 620;
private static final int PAUSE_MARGIN_TOP_680 = 162;
private Context mContext;
private ImageADList mADList = new ImageADList(Constants.AD_TYPE_PAUSE);
private int mState = IADImageView.STATE_IDLE;
private IADEventListener mlistener;
public ADPauseView(Context context) {
this(context, null);
}
public ADPauseView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ADPauseView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
}
@Override
public void setParameter(ImageADList adList) {
this.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT, Gravity.CENTER));
RelativeLayout.LayoutParams vpParams = null;
if (adList == null || !adList.hasDetail()) {
vpParams = new RelativeLayout.LayoutParams(PAUSE_WIDTH_1153, PAUSE_HEIGHT_1153);
} else {
// set vp LayoutParams depends on AD's params
final Image image = adList.get(0);
float ratio = image.getRatio();
Log.d(TAG, "pause setParameter --> ratio = " + ratio);
if (ratio > 0 && ratio < 1.53) {
// 680 * 680
vpParams = new RelativeLayout.LayoutParams(PAUSE_WIDTH_680, PAUSE_HEIGHT_680);
} else {
// 1153 * 559
vpParams = new RelativeLayout.LayoutParams(PAUSE_WIDTH_1153, PAUSE_HEIGHT_1153);
}
}
if (vp != null) {
vp.setLayoutParams(vpParams);
}
}
@Override
public void setEventListener(IADEventListener listener) {
mlistener = listener;
}
@Override
public void setData(ImageADList list) {
Log.d(TAG, "setData --> type = " + Tools.getADTypeString(mADList.adType) + ", list = " + list);
if (list != null) {
mADList.set(list);
}
}
@Override
public boolean isShowing() {
// return this.getVisibility() == VISIBLE;
return mState == IADImageView.STATE_VISIBLE;
}
/**
* call by wrapper
* @param list can be null. if null, use the cache mADList
*/
@Override
public boolean show(ImageADList list) {
Log.d(TAG, "show --> type = " + Tools.getADTypeString(mADList.adType) + ", isShowing = " + isShowing() + ", list = " + list);
if (list != null) {
mADList.set(list);
}
if (isShowing()) {
return false;
} else {
if (mADList.hasDetail()) {
mState = IADImageView.STATE_VISIBLE;
this.setVisibility(VISIBLE);
setDatas(mADList);
commit();
Log.i(TAG, "-- show done --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
if (mlistener != null) {
mlistener.onShow(mADList.get(0));
}
return true;
}
}
return false;
}
@Override
public boolean resume() {
Log.d(TAG, "resume --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState)
+ ", pre list = " + mADList);
if (mState == IADImageView.STATE_HIDE && mADList.hasDetail()) {
mState = IADImageView.STATE_VISIBLE;
this.setVisibility(VISIBLE);
setDatas(mADList);
commit();
Log.i(TAG, "-- resume done --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
if (mlistener != null) {
mlistener.onResume(mADList.get(0));
}
return true;
}
return false;
}
@Override
public boolean enter() {
Log.d(TAG, "enter --> performClick, type = " + Tools.getADTypeString(mADList.adType) + ", isShowing = " + isShowing());
if (isShowing() && mADList.hasDetail()) {
this.performClick();
if (mlistener != null) {
mlistener.onEnter(mADList.get(0));
}
return true;
}
return false;
}
@Override
public boolean hide() {
Log.d(TAG, "hide --> type = " + Tools.getADTypeString(mADList.adType) + ", isShowing = " + isShowing());
if (isShowing() && mADList.hasDetail()) {
mState = IADImageView.STATE_HIDE;
this.setVisibility(INVISIBLE);
stop();
Log.i(TAG, "-- hide done --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
if (mlistener != null) {
mlistener.onHide(mADList.get(0));
}
return true;
}
return false;
}
@Override
public boolean cancel() {
Log.d(TAG, "cancel --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
if (Tools.isADShowbackState(mState) && mADList.hasDetail()) {
mState = IADImageView.STATE_GONE;
stop();
this.setVisibility(GONE);
Log.i(TAG, "-- cancel done --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
if (mlistener != null) {
mlistener.onSkip(mADList.get(0));
}
mADList.reset();
return true;
}
return false;
}
@Override
public void reset() {
Log.d(TAG, "reset --> type = " + Tools.getADTypeString(mADList.adType));
mState = IADImageView.STATE_IDLE;
mlistener = null;
cancel();
releaseResource();
this.setVisibility(GONE);
mADList.reset();
Log.i(TAG, "-- reset done --> type = " + Tools.getADTypeString(mADList.adType) + ", state = " + Tools.getADViewStateStr(mState));
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.d(TAG, "-- onAttachedToWindow type = " + Tools.getADTypeString(mADList.adType));
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d(TAG, "-- onDetachedFromWindow type = " + Tools.getADTypeString(mADList.adType));
}
}