Android自定義圖片輪播Banner控件使用解析
圖片輪播控件,可以說是每個(gè)App基本上都會(huì)用到的。它可以用來動(dòng)態(tài)的展示多個(gè)圖片,之前寫過兩篇博客:實(shí)現(xiàn)ViewPager無限循環(huán)的方式一和實(shí)現(xiàn)ViewPager無限循環(huán)的方式二,在這兩篇博客中,分析了兩種實(shí)現(xiàn)ViewPager無限循環(huán)的原理,但是在使用的過程中,代碼的解偶性很低,所以就使用自定義View的方式,實(shí)現(xiàn)無限循環(huán)的圖片輪播的封裝。
先看看效果:
功能特點(diǎn)
- 支持自定義寬高比例
- 支持自定義圖片切換時(shí)間
- 支持自定義指示點(diǎn)的顏色
- 支持自定義指示點(diǎn)的背景色
- 支持自定義指示點(diǎn)的高度
- 支持是否顯示指示點(diǎn)
- 支持每個(gè)圖片設(shè)置不同的點(diǎn)擊事件
使用簡單
<com.xiaomai.bannerview.BannerView android:id="@+id/bannerView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:aspectRatio="1.5" app:defaultSrc="@mipmap/ic_launcher" app:indicatorBackground="@color/white" app:indicatorHeight="50dp" app:indicatorPositionSize="8dp" app:updateTime="3000" />
實(shí)現(xiàn)步驟
- 聲明自定義的屬性
- 創(chuàng)建一個(gè)類繼承RelativeLayout
- 解析屬性
聲明自定義的屬性
在values/attrs文件中創(chuàng)建自定義的屬性
<resources> <declare-styleable name="BannerView"> <attr name="aspectRatio" format="float" /> <!-- 寬高比 --> <attr name="defaultSrc" format="integer|reference" /> <!-- 占位圖 --> <attr name="updateTime" format="integer" /> <!-- 圖片切換時(shí)間 --> <attr name="indicatorVisible" format="boolean" /> <!-- 是否顯示指示器 --> <attr name="indicatorHeight" format="dimension" /> <!-- 指示器的高度 --> <attr name="indicatorBackground" format="color|reference" /> <!-- 指示器的背景顏色 --> <attr name="indicatorPositionSize" format="dimension" /> <!-- 指示點(diǎn)的大小 --> </declare-styleable> </resources>
創(chuàng)建BannerView類
public class BannerView extends RelativeLayout { public BannerView(Context context) { this(context, null); } public BannerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BannerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);}
在BannerView中聲明變量屬性
private Context context; private Handler handler; private ImageLoader imageLoader; private DisplayImageOptions options; private boolean isHaveHandler = true;// 當(dāng)用戶點(diǎn)擊輪播圖時(shí),取消handler隊(duì)列,也就是取消滾動(dòng) // 控件Start private ViewPager viewPager; private LinearLayout indicator;// 指示器 private onItemClickListener listener; // 控件End // 自定義屬性Start private float mAspectRatio; // 寬高比 private int defaultImageResource; // 默認(rèn)占位圖 private int updateTime; // 圖片切換的時(shí)間間隔,默認(rèn)3秒 private boolean showIndicator; // 是否顯示指示器,默認(rèn)顯示 private int indicatorHeight;// 指示器的高度,默認(rèn)35dp private int indicatorPositionSize; // 指示器的大小 private int indicatorBackground; // 指示器的背景顏色 // 自定義屬性End // 數(shù)據(jù)Start private int imageCount; private int lastPosition; private List<Integer> imageResources; private List<Image> imageUrls; // 數(shù)據(jù)End
解析自定義屬性的值
接下來為自定義的屬性賦值,在3個(gè)參數(shù)的構(gòu)造方法中初始化變量。
public BannerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); parseCustomAttributes(context, attrs); this.context = context; handler = new Handler(); imageLoader = ImageLoader.getInstance(); options = HSApplication.getDisplayOptions().build(); initViews(); } /** * 解析自定義屬性 * * @param context * @param attrs */ private void parseCustomAttributes(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerView); mAspectRatio = typedArray.getFloat(R.styleable.BannerView_aspectRatio, 0f); defaultImageResource = typedArray.getResourceId(R.styleable.BannerView_defaultSrc, R.drawable.about_us); updateTime = typedArray.getInt(R.styleable.BannerView_updateTime, 3000); showIndicator = typedArray.getBoolean(R.styleable.BannerView_indicatorVisible, true); indicatorHeight = (int) (typedArray.getDimension(R.styleable.BannerView_indicatorHeight, Utils.dip2px(context, 35))); indicatorBackground = typedArray.getResourceId(R.styleable.BannerView_indicatorBackground, R.color.white_alpha00); indicatorPositionSize = (int) typedArray.getDimension( R.styleable.BannerView_indicatorPositionSize, Utils.dip2px(context, 5)); typedArray.recycle(); } private void initViews() { viewPager = new ViewPager(context); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if (showIndicator) { for (int i = 0; i < indicator.getChildCount(); i++) { indicator.getChildAt(i).setSelected(false); } indicator.getChildAt(position % imageCount).setSelected(true); } lastPosition = position; } @Override public void onPageScrollStateChanged(int state) { switch (state) { case ViewPager.SCROLL_STATE_IDLE:// 空閑狀態(tài) if (!isHaveHandler) { isHaveHandler = true; handler.postDelayed(updateRunnable, updateTime); } break; case ViewPager.SCROLL_STATE_DRAGGING:// 用戶滑動(dòng)狀態(tài) handler.removeCallbacks(updateRunnable); isHaveHandler = false; break; } } }); addView(viewPager, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); if (showIndicator) { indicator = new LinearLayout(context); indicator.setOrientation(LinearLayout.HORIZONTAL); indicator.setGravity(Gravity.CENTER); indicator.setBackgroundResource(indicatorBackground); RelativeLayout.LayoutParams layoutParams = new LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, indicatorHeight); layoutParams.addRule(ALIGN_PARENT_BOTTOM); addView(indicator, layoutParams); } }
控件和自定義的屬性都經(jīng)過賦值和初始化了,接下來,該為設(shè)置圖片資源了。
public void setImageResources(List<Integer> imageResources) { if (imageResources == null || imageResources.size() == 0) { throw new RuntimeException("圖片資源為空"); } this.imageResources = imageResources; imageCount = imageResources.size(); } public void setImageUrls(List<Image> imageUrls) { if (imageUrls == null || imageUrls.size() == 0) { throw new RuntimeException("圖片地址資源為空"); } this.imageUrls = imageUrls; imageCount = imageUrls.size(); loadImages(); } private void loadImages() { if (showIndicator) { addIndicationPoint(); } viewPager.setAdapter(new MyViewPageAdapter()); viewPager.setCurrentItem(200 - (200 % imageCount)); handler.removeCallbacks(updateRunnable); handler.postDelayed(updateRunnable, updateTime); } /** * 添加指示點(diǎn)到指示器中 */ private void addIndicationPoint() { // 防止刷新重復(fù)添加 if (indicator.getChildCount() > 0) { indicator.removeAllViews(); } View pointView; int margin = Utils.dip2px(context, 5f); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( indicatorPositionSize, indicatorPositionSize); layoutParams.setMargins(margin, margin, margin, margin); for (int i = 0; i < imageCount; i++) { pointView = new View(context); pointView.setBackgroundResource(R.drawable.indicator_selector); if (i == lastPosition) { pointView.setSelected(true); } else { pointView.setSelected(false); } indicator.addView(pointView, layoutParams); } } private class MyViewPageAdapter extends PagerAdapter { @Override public int getCount() { return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, final int position) { final ImageView imageView = new ImageView(container.getContext()); imageView.setImageResource(defaultImageResource); imageView.setScaleType(ImageView.ScaleType.FIT_XY); final Image image = imageUrls.get(position % imageCount); imageLoader.displayImage(image.getImageUrl(), imageView, options); imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onClick(v, position % imageCount, image.getAction(), image.getUrl()); } } }); container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } private Runnable updateRunnable = new Runnable() { @Override public void run() { viewPager.setCurrentItem(lastPosition + 1); handler.postDelayed(updateRunnable, updateTime); } /** * 點(diǎn)擊監(jiān)聽回調(diào)事件 */ public interface onItemClickListener { void onClick(View view, int position, String action, String url); } public void setOnItemClickListener(onItemClickListener listener) { this.listener = listener; }
時(shí)間太倉促,解釋的不是太詳細(xì),可以移步我的GitHub查看完整代碼。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flutter UI實(shí)現(xiàn)側(cè)拉抽屜菜單
這篇文章主要為大家詳細(xì)介紹了Flutter UI實(shí)現(xiàn)側(cè)拉抽屜菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Android Wear計(jì)時(shí)器開發(fā)
這篇文章主要介紹了Android Wear計(jì)時(shí)器開發(fā),需要的朋友可以參考下2014-11-11Flutter移動(dòng)端進(jìn)行多渠道打包發(fā)布的全過程
在使用flutter開發(fā)的過程中,需要根據(jù)不同的環(huán)境,不同的包名來打包,下面這篇文章主要給大家介紹了關(guān)于Flutter移動(dòng)端進(jìn)行多渠道打包發(fā)布的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06Android AsyncTask實(shí)現(xiàn)異步處理任務(wù)的方法詳解
這篇文章主要介紹了Android AsyncTask實(shí)現(xiàn)異步處理任務(wù)的方法詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04Android編程判斷網(wǎng)絡(luò)是否可用及調(diào)用系統(tǒng)設(shè)置項(xiàng)的方法
這篇文章主要介紹了Android編程判斷網(wǎng)絡(luò)是否可用及調(diào)用系統(tǒng)設(shè)置項(xiàng)的方法,涉及Android針對(duì)網(wǎng)絡(luò)連接的判定及屬性設(shè)置的調(diào)用,需要的朋友可以參考下2016-03-03Android 通過webservice上傳多張圖片到指定服務(wù)器詳解
這篇文章主要介紹了Android 通過webservice上傳多張圖片到指定服務(wù)器詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02Android編程使用ListView實(shí)現(xiàn)數(shù)據(jù)列表顯示的方法
這篇文章主要介紹了Android編程使用ListView實(shí)現(xiàn)數(shù)據(jù)列表顯示的方法,實(shí)例分析了Android中ListView控件的使用技巧,需要的朋友可以參考下2016-01-01Android編程實(shí)現(xiàn)橫豎屏切換時(shí)不銷毀當(dāng)前activity和鎖定屏幕的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)橫豎屏切換時(shí)不銷毀當(dāng)前activity和鎖定屏幕的方法,涉及Android屬性設(shè)置及activity操作的相關(guān)技巧,需要的朋友可以參考下2015-11-11Android入門之PopupWindow用法實(shí)例解析
這篇文章主要介紹了Android入門之PopupWindow用法,對(duì)于Android初學(xué)者來說有一定的學(xué)習(xí)借鑒價(jià)值,需要的朋友可以參考下2014-08-08