Android:实现一个轮播图(Banner)

前言 && 需求

在Android App中,轮播图还是很常见的,如Splash闪屏页的引导图,电商App首页上的广告轮播图等等。其实github是有很多此类组件,但是实现起来也不难,不妨自己去尝试尝试。

思路

此博文是基于ViewPager实现的轮播图。

  1. 我们也知道,ViewPager是不能实现循环播放的,但我们可以巧妙的利用ViewPager的方法:

setCurrentItem(int item, boolean smoothScroll)

如果当前item与下一个item的视图一致时,smoothScroll置为false,UI上是看不到视图重新绘制过程的,这是实现ViewPager无限轮播的关键

  1. 基于上述的方法,我们将要显示的视图,设计如下:

轮播流程:
View1 -->View2 --> View n --> View1(完成一次循环)–>View2 -->View3…
当显示View n的时候,立刻切换到View1(Viewn和View1显示的内容是相同的),这样就实现了图片轮播(利用setCurrentItem())。

实现

自定义一个轮播控件Banner:

package cn.snaptech.loopviewpager.widget.Banner;import android.content.Context;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.view.ViewParent;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.LinearLayout;import java.util.ArrayList;import java.util.List;import cn.snaptech.loopviewpager.R;public class Banner extends FrameLayout {    private static final String TAG = "Banner";    private ViewPager mVp_dataset;    private LinearLayout mLl_indicator;// 指示器的ViewGroup    private List mDataView = new ArrayList<>();// 数据View集合    private List mIndicators = new ArrayList<>();// 指示器View集合    private List mDatas = new ArrayList<>();// 数据集合    private Context mContext;    private int mCurrentIndex;// 当前要显示的View的下标    private GetViewPagerItemView mGetItemViewInterface;// ViewPager中itemview显示的相关接口    public Banner(@NonNull Context context) {        this(context, null);    }    public Banner(@NonNull Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public Banner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;        View rootView = View.inflate(context, R.layout.banner, this);        initViews(rootView);    }    private void initViews(View rootView) {        mVp_dataset = rootView.findViewById(R.id.vp_dataset);        mLl_indicator = rootView.findViewById(R.id.ll_indicator);    }    private void initDataSets(ArrayList list,GetViewPagerItemView itemView) {        mDataView.clear();        mDatas.clear();        // 添加轮播图View,数量为集合数+2        setGetItemViewInterface(itemView);        try {            mDataView.add(mGetItemViewInterface.getViewPagerItemView(list.get(list.size() - 1),list.size() - 1));            mDatas.add(list.get(list.size() - 1));            for (int i = 0; i < list.size(); i++) {                mDataView.add(mGetItemViewInterface.getViewPagerItemView(list.get(i),i));                mDatas.add(list.get(i));            }            mDataView.add(mGetItemViewInterface.getViewPagerItemView(list.get(0),0));            mDatas.add(list.get(0));        } catch (IndexOutOfBoundsException e) {            e.printStackTrace();        }    }    public void setGetItemViewInterface(GetViewPagerItemView interfaze){        this.mGetItemViewInterface = interfaze;    }    /**     * 设置要显示的数据     * @param list     * @param itemView     */    public void setDataSet(ArrayList list,GetViewPagerItemView itemView) {        if (list == null || list.size() == 0) {            return;        }        initDataSets(list,itemView);        initIndicators(list.size());        setCurrentIndicatorShow(0);        mVp_dataset.setFocusable(true);        mCurrentIndex = 1;        mVp_dataset.addOnPageChangeListener(new ViewPagerChangeListener());        mVp_dataset.setAdapter(new ViewPagerAdapter(mContext, (ArrayList) mDataView));        mVp_dataset.setCurrentItem(mCurrentIndex);    }    private void initIndicators(int size) {        for (int i = 0; i < size; i++) {            ImageView indicator = new ImageView(mContext);            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);            params.setMargins(10, 0, 10, 0);            indicator.setLayoutParams(params);            mIndicators.add(indicator);            mLl_indicator.addView(indicator);        }    }    private void setCurrentIndicatorShow(int position) {        for (int i = 0; i < mIndicators.size(); i++) {            if (i == position) {                mIndicators.get(i).setImageResource(R.drawable.shape_indicator_background_focus);            } else {                mIndicators.get(i).setImageResource(R.drawable.shape_indicator_background);            }        }    }    public class ViewPagerAdapter extends PagerAdapter {        private Context mContext;        private List mDatas = new ArrayList<>();        public ViewPagerAdapter(Context context, ArrayList datas) {            this.mContext = context;            this.mDatas.clear();            this.mDatas.addAll(datas);        }        @Override        public int getCount() {            return mDatas.size();        }        @Override        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {            return view == object;        }        @Override        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {            Log.d(TAG, "--destroyItem() position = " + position);            container.removeView((View) object);        }        @NonNull        @Override        public Object instantiateItem(@NonNull final ViewGroup container, int position) {            final View childView = mDatas.get(position);            final ViewParent viewParent = childView.getParent();            Log.d(TAG, "--instantiateItem() position = " + position + " parenetView is null ?= " + (viewParent == null) + " , container = " + container);            // TODO Auto-generated method stub            if (viewParent == null) {                container.addView(childView);            } else {                Log.d(TAG, "--instantiateItem() position = " + position + " parenetView = " + viewParent);                ((ViewGroup) viewParent).removeView(childView);                // viewParent设置background的目的:防止最后一张图循环滑动至第一张图时,造成页面闪屏                if (position == 0) {                    mGetItemViewInterface.setItemBackground((ViewGroup) viewParent,Banner.this.mDatas.get(1));                } else if (position == getCount() - 1) {                    mGetItemViewInterface.setItemBackground((ViewGroup) viewParent,Banner.this.mDatas.get(mDatas.size() -1));                } else {                    ((ViewGroup) viewParent).setBackground(null);                }                container.post(new Runnable() {                    @Override                    public void run() {                        container.addView(childView);                        ((ViewGroup) viewParent).setBackground(null);                    }                });            }            return childView;        }        @Override        public int getItemPosition(Object object) {            return POSITION_NONE;        }    }    public class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {        @Override        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        }        @Override        public void onPageSelected(int position) {            Log.d(TAG, "onPageSelected() position = " + position + ", mDataView.size() = " + mDataView.size());            mCurrentIndex = position;            if (position == mDataView.size() - 1) {// 最后一张,4                mCurrentIndex = 1;                position = 0;            } else if (position == 0) {// 第一张,0                mCurrentIndex = mDataView.size() - 2;                position = mIndicators.size() - 1;            } else {// 1,2,3                position = mCurrentIndex - 1;            }            setCurrentIndicatorShow(position);        }        @Override        public void onPageScrollStateChanged(int state) {            switch (state) {                case ViewPager.SCROLL_STATE_IDLE:                    Log.d(TAG, "onPageScrollStateChanged() SCROLL_STATE_IDLE mCurrentIndex = " + mCurrentIndex);                    mVp_dataset.setCurrentItem(mCurrentIndex, false);                    break;                default:                    break;            }        }    }}

banner的布局:

<?xml version="1.0" encoding="utf-8"?>                

GetViewPagerItemView:

package cn.snaptech.loopviewpager.widget.Banner;import android.view.View;import android.view.ViewGroup;public interface GetViewPagerItemView {    /**     * 获取每个ViewPager 的 item 要显示的视图     * @param url     * @param position     * @return     */    View getViewPagerItemView(String url, int position);    /**     * 设置item的背景     * @param parentView     * @param url     */    void setItemBackground(ViewGroup parentView, String url);}

说明:

轮播过程中出现:

java.lang.IllegalStateException: The specified child already has a parent

意思是一个子View已经存在一个父View,你必须先调用该子视图的父视图的 removeView() 方法,这种情况通常出现在动态添加视图的情况下,出现这种错误的原因是一个子控件只允许存在一个父控件,而很多时候在动态添加视图的时候,我们不知道该子视图是否已存在父视图,当已存在的时候就会报错。

那么解决方式就很明了了:

public Object instantiateItem(@NonNull final ViewGroup container, int position) {            final View childView = mDatas.get(position);            final ViewParent viewParent = childView.getParent();            Log.d(TAG, "--instantiateItem() position = " + position + " parenetView is null ?= " + (viewParent == null) + " , container = " + container);            // TODO Auto-generated method stub            if (viewParent == null) {                container.addView(childView);            } else {                Log.d(TAG, "--instantiateItem() position = " + position + " parenetView = " + viewParent);                ((ViewGroup) viewParent).removeView(childView);                // viewParent设置background的目的:防止最后一张图循环滑动至第一张图时,造成页面闪屏                if (position == 0) {                    mGetItemViewInterface.setItemBackground((ViewGroup) viewParent,Banner.this.mDatas.get(1));                } else if (position == getCount() - 1) {                    mGetItemViewInterface.setItemBackground((ViewGroup) viewParent,Banner.this.mDatas.get(mDatas.size() -1));                } else {                    ((ViewGroup) viewParent).setBackground(null);                }                container.post(new Runnable() {                    @Override                    public void run() {                        container.addView(childView);                        ((ViewGroup) viewParent).setBackground(null);                    }                });            }            return childView;        }

设置background,是因为采取以上方法解决IllegalStateException问题后,首尾轮播图片时,会造成闪屏,所以为了过渡自然,设置背景。

效果图

源码

https://github.com/wudongjiang333/LoopViewPager

更多相关文章

  1. Android去除TextView文本中的默认内边距
  2. Android实现简单底部导航栏 Android仿微信滑动切换效果
  3. Toast用法
  4. 五步学会Android的ListView控件
  5. [置顶] Android(安卓)View视图------Android如何创建一个view。
  6. android中如何美化ListView的背景
  7. Android四大控件之ContentProvider
  8. Android(安卓)webview控件(一) 使用webview开发浏览器实例
  9. Android(安卓)UI开源组件库BottomView ,第三方自定义UI控件

随机推荐

  1. Android(安卓)深入解析AsyncTask(doInBack
  2. Android多媒体分析(六)对AudioManager的一
  3. android WebView html网页设置长按的时候
  4. android内核读取file文件
  5. 获取 Andorid 手机WIFI连接的Mac地址和IP
  6. android设置定时关闭的dialog
  7. [转]Android(安卓)获取当前Activity的屏
  8. Android标题栏渐变色,沉寂式状态栏
  9. Android(安卓)studio 设计简易的计算器
  10. ANR问题