在App的開發中,不少的時候都須要實現相似京東淘寶同樣的自動無限輪播的廣告欄,因此就本身寫了一個,下面是我自定義控件的實現思路和過程。android
1、自定義控件屬性git
新建自定義控件SliderLayout繼承於RelativeLayout,首先要考慮的就是自定義的控件須要擴展那些屬性,把這些屬性列出來。在這裏是要實現相似於京東淘寶的無限輪播廣告欄,那麼首先想到的就是輪播的時長、輪播指示器的樣式等等。我在這裏列舉了一些而且結合到了代碼中。github
一、擴展屬性網絡
(1)是否開啓自動輪播的功能。框架
(2)指示器的圖形樣式,通常爲圓形和方形兩種。ide
(3)指示器的位置,通常爲底部或者頂部。函數
(4)指示器被選中和不被選中時的樣式:顏色、高度、寬度、間隔等。動畫
(5)輪播的時長。url
(6)加載的若是是網絡圖片的話,須要默認圖片和錯誤圖片等。spa
二、在attrs.xml文件中添加這些擴展的屬性。
1 <declare-styleable name="SliderLayout"> 2 <attr name="sl_is_auto_play" format="boolean"/> 3 <attr name="sl_indicator_shape" format="enum"> 4 <enum name="oval" value="0" /> 5 <enum name="rect" value="1" /> 6 </attr> 7 <attr name="sl_indicator_position" format="enum"> 8 <enum name="centerBottom" value="0" /> 9 <enum name="rightBottom" value="1" /> 10 <enum name="leftBottom" value="2" /> 11 <enum name="centerTop" value="3" /> 12 <enum name="rightTop" value="4" /> 13 <enum name="leftTop" value="5" /> 14 </attr> 15 <attr name="sl_selected_indicator_color" format="color|reference" /> 16 <attr name="sl_unselected_indicator_color" format="color|reference" /> 17 18 <attr name="sl_selected_indicator_height" format="dimension|reference" /> 19 <attr name="sl_selected_indicator_width" format="dimension|reference" /> 20 21 <attr name="sl_unselected_indicator_height" format="dimension|reference" /> 22 <attr name="sl_unselected_indicator_width" format="dimension|reference" /> 23 24 <attr name="sl_indicator_space" format="dimension|reference" /> 25 <attr name="sl_indicator_margin" format="dimension|reference" /> 26 <attr name="sl_auto_play_duration" format="integer|reference" /> 27 <attr name="sl_default_image" format="reference"/> 28 <attr name="sl_error_image" format="reference"/> 29 </declare-styleable>
2、自定義輪播控件的初始化
一、獲取到擴展屬性的值
在自定義SliderLayout中獲取到擴展的樣式,而後根據樣式獲取相應的屬性值,最好是要先設置好默認值。
1 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SliderLayout, defStyleAttr, 0); 2 if (array != null) { 3 isAutoPlay = array.getBoolean(R.styleable.SliderLayout_sl_is_auto_play, isAutoPlay); 4 //get the shape of indicator 5 int intShape = array.getInt(R.styleable.SliderLayout_sl_indicator_shape, indicatorShape.ordinal()); 6 for (IndicatorShape shape : IndicatorShape.values()) { 7 if (shape.ordinal() == intShape) { 8 indicatorShape = shape; 9 break; 10 } 11 } 12 //get the position of indicator 13 int intPosition = array.getInt(R.styleable.SliderLayout_sl_indicator_position, IndicatorPosition.centerBottom.ordinal()); 14 for (IndicatorPosition position : IndicatorPosition.values()) { 15 if (position.ordinal() == intPosition) { 16 indicatorPosition = position; 17 break; 18 } 19 } 20 unSelectedIndicatorColor = array.getColor(R.styleable.SliderLayout_sl_unselected_indicator_color, unSelectedIndicatorColor); 21 selectedIndicatorColor = array.getColor(R.styleable.SliderLayout_sl_selected_indicator_color, selectedIndicatorColor); 22 unSelectedIndicatorHeight = array.getDimension(R.styleable.SliderLayout_sl_unselected_indicator_height, unSelectedIndicatorHeight); 23 unSelectedIndicatorWidth = array.getDimension(R.styleable.SliderLayout_sl_unselected_indicator_width, unSelectedIndicatorWidth); 24 selectedIndicatorHeight = array.getDimension(R.styleable.SliderLayout_sl_selected_indicator_height, selectedIndicatorHeight); 25 selectedIndicatorWidth = array.getDimension(R.styleable.SliderLayout_sl_selected_indicator_width, selectedIndicatorWidth); 26 indicatorSpace = array.getDimension(R.styleable.SliderLayout_sl_indicator_space, indicatorSpace); 27 indicatorMargin = array.getDimension(R.styleable.SliderLayout_sl_indicator_margin, indicatorMargin); 28 autoPlayDuration = array.getInt(R.styleable.SliderLayout_sl_auto_play_duration, autoPlayDuration); 29 defaultImage = array.getResourceId(R.styleable.SliderLayout_sl_default_image, defaultImage); 30 errorImage = array.getResourceId(R.styleable.SliderLayout_sl_error_image, errorImage); 31 }
二、初始化控件
根據這裏所須要實現的功能,首先須要一個圖像切換器ImageSwticher,還要指示器,這裏就用ImageView了。
1 switcherImage = new ImageSwitcher(context); 2 switcherImage.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 3 4 for (int i = 0; i < itemCount; i++) { 5 ImageView indicator = new ImageView(context); 6 indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 7 indicator.setPadding((int) (indicatorSpace), (int) (indicatorSpace), (int) (indicatorSpace), (int) (indicatorSpace)); 8 indicator.setImageDrawable(unSelectedDrawable); 9 indicatorContainer.addView(indicator); 10 final int finalI = i; 11 indicator.setOnClickListener(new OnClickListener() { 12 @Override 13 public void onClick(View view) { 14 stopAutoPlay(); 15 switchIndicator(finalI); 16 pictureIndex = finalI; 17 handler.sendEmptyMessageDelayed(START_AUTO_PLAY,autoPlayDuration); 18 } 19 }); 20 }
三、初始化選中第一張圖片
專門寫一個針對指示器切換的函數,而後在初始化的時候直接調用,選中第一個指示器,就是選中第一張圖片了。
函數代碼以下。
1 private void switchIndicator(int index) { 2 for (int i = 0; i < indicatorContainer.getChildCount(); i++) { 3 ((ImageView) indicatorContainer.getChildAt(i)).setImageDrawable(i == index ? selectedDrawable : unSelectedDrawable); 4 } 5 loadImage(index); 6 }
調用選中第一張圖。
1 switchIndicator(0);
3、圖片的加載
一、網路圖片的加載
在這裏使用Picasso框架來加載圖片,根據url來加載顯示圖片,同時也要顯示圖片的加載進度,這裏就須要一個Dialog提示框了,Dialog的樣式最好是能夠自定義的。
1 private void loadNetImage(int pictureIndex) { 2 if (list != null && list.size() != 0) { 3 Picasso.with(context) 4 .load((String) list.get(pictureIndex)) 5 .placeholder(defaultImage) 6 .error(errorImage) 7 .tag(context) 8 .into(mTarget); 9 } 10 }
下面是圖片的加載提示過程。
1 private Target mTarget = new Target() { 2 3 @Override 4 public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { 5 dismissDialog(); 6 ((ImageView) switcherImage.getCurrentView()).setScaleType(ImageView.ScaleType.CENTER_CROP); 7 ((ImageView) switcherImage.getCurrentView()).setLayoutParams(new ImageSwitcher.LayoutParams(ImageSwitcher.LayoutParams.MATCH_PARENT, ImageSwitcher.LayoutParams.MATCH_PARENT)); 8 ((ImageView) switcherImage.getCurrentView()).setImageBitmap(bitmap); 9 } 10 11 @Override 12 public void onBitmapFailed(Drawable errorDrawable) { 13 dismissDialog(); 14 ((ImageView) switcherImage.getCurrentView()).setImageDrawable(errorDrawable); 15 } 16 17 @Override 18 public void onPrepareLoad(Drawable placeHolderDrawable) { 19 showDialog(); 20 } 21 };
二、加載資源圖片
只能加載網絡圖片是不夠的呢,還須要能夠加載資源圖片,加載資源圖片的辦法就更加簡單了。
1 private void loadFileImage(int pictureIndex) { 2 if (list != null && list.size() != 0) { 3 switcherImage.setImageResource((Integer) list.get(pictureIndex)); 4 } 5 }
4、設置圖片切換的動畫
設置圖片從左往右以及從右往左的動畫效果,而且當滑動到該圖片時,指示器也要一塊兒變化,這裏就簡單說下從左往右的動畫了。
1 private void SliderLeftToRight() { 2 // get current index 3 pictureIndex = pictureIndex == 0 ? itemCount - 1 4 : pictureIndex - 1; 5 // set Animation 6 switcherImage.setInAnimation(AnimationUtils.loadAnimation(context, 7 android.R.anim.slide_in_left)); 8 switcherImage.setOutAnimation(AnimationUtils.loadAnimation(context, 9 android.R.anim.slide_out_right)); 10 switchIndicator(pictureIndex); 11 }
從右往左滑動時的代碼和這個是同樣的,就是換了下方向,須要本身定義下。
5、定義圖片的點擊事件
一、定義interface來監聽事件
在自定義控件中自定義一個interface來監聽事件就能夠了。
1 public interface IOnClickListener { 2 3 void onItemClick(View view, int position); 4 5 }
二、在onTouch中調用點擊事件。
這裏須要說明下爲何在onTouch中處理,由於onTouch是觸摸事件,在滑動的過程當中,用戶是觸摸了屏幕的,因此根據用戶觸摸屏幕時點擊下的X座標和點擊起來時的X座標的對比來判斷是左滑仍是右滑了,這樣的話,就會和onClick事件相沖了,因此就想到了一個辦法,那就是在範圍內的話,就默認爲點擊事件,範圍外就是滑動事件了。
1 if (0==(Math.abs(touchUpX - touchDownX))||(Math.abs(touchUpX - touchDownX))<50) { 2 3 if (listener != null) { 4 5 stopAutoPlay(); 6 listener.onItemClick(view, pictureIndex); 7 handler.sendEmptyMessageDelayed(START_AUTO_PLAY,autoPlayDuration); 8 } 9 }
6、效果圖
說到了這裏,應該有所思路了吧,如今就來看下效果吧。
源代碼目前已經開放了,放在Github上面,歡迎指導建議。http://www.github.com/LT5505/SliderLayout