AndroidWiki - 无限图文轮播

无限轮播位

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));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值