摘要:項目要求做一個廣告頁,實現幾秒更換一次廣告頁,下方還有指示第幾張廣告頁,同樣也支持手動左滑或右滑。
1.准備好粘貼5個有關廣告頁的類。
①BaseViewPager==>自定義高度的ViewPager

1 public class BaseViewPager extends ViewPager { 2 private boolean scrollable = true; 3 4 public BaseViewPager(Context context) { 5 super(context); 6 } 7 8 public BaseViewPager(Context context, AttributeSet attrs) { 9 super(context, attrs); 10 } 11 12 /** 13 * 設置viewpager是否可以滾動 14 * 15 * @param enable 16 */ 17 public void setScrollable(boolean enable) { 18 scrollable = enable; 19 } 20 21 @Override 22 public boolean onInterceptTouchEvent(MotionEvent event) { 23 if (scrollable) { 24 return super.onInterceptTouchEvent(event); 25 } else { 26 return false; 27 } 28 } 29 }
②CycleViewPager==>實現可循環、可輪播的viewPager

1 @SuppressLint("NewApi") 2 public class CycleViewPager extends Fragment implements OnPageChangeListener { 3 4 private List<ImageView> imageViews = new ArrayList<ImageView>(); 5 private ImageView[] indicators; 6 private FrameLayout viewPagerFragmentLayout; 7 private LinearLayout indicatorLayout; // 指示器 8 private BaseViewPager viewPager; 9 private BaseViewPager parentViewPager; 10 private ViewPagerAdapter adapter; 11 private CycleViewPagerHandler handler; 12 private int time = 5000; // 默認輪播時間 13 private int currentPosition = 0; // 輪播當前位置 14 private boolean isScrolling = false; // 滾動框是否滾動着 15 private boolean isCycle = false; // 是否循環 16 private boolean isWheel = false; // 是否輪播 17 private long releaseTime = 0; // 手指松開、頁面不滾動時間,防止手機松開后短時間進行切換 18 private int WHEEL = 100; // 轉動 19 private int WHEEL_WAIT = 101; // 等待 20 private ImageCycleViewListener mImageCycleViewListener; 21 private List<MyImage> infos; 22 23 @Override 24 public View onCreateView(LayoutInflater inflater, ViewGroup container, 25 Bundle savedInstanceState) { 26 View view = LayoutInflater.from(getActivity()).inflate( 27 R.layout.view_cycle_viewpager_contet, null); 28 29 viewPager = (BaseViewPager) view.findViewById(R.id.viewPager); 30 indicatorLayout = (LinearLayout) view.findViewById(R.id.layout_viewpager_indicator);//就是那個白點吧 31 viewPagerFragmentLayout = (FrameLayout) view.findViewById(R.id.layout_viewager_content); 32 33 handler = new CycleViewPagerHandler(getActivity()) { 34 35 @Override 36 public void handleMessage(Message msg) { 37 super.handleMessage(msg); 38 if (msg.what == WHEEL && imageViews.size() != 0) { 39 if (!isScrolling) { 40 int max = imageViews.size() + 1; 41 int position = (currentPosition + 1) % imageViews.size(); 42 viewPager.setCurrentItem(position, true); 43 if (position == max) { // 最后一頁時回到第一頁 44 viewPager.setCurrentItem(1, false); 45 } 46 } 47 48 releaseTime = System.currentTimeMillis(); 49 handler.removeCallbacks(runnable); 50 handler.postDelayed(runnable, time); 51 return; 52 } 53 if (msg.what == WHEEL_WAIT && imageViews.size() != 0) { 54 handler.removeCallbacks(runnable); 55 handler.postDelayed(runnable, time); 56 } 57 } 58 }; 59 60 return view; 61 } 62 63 public void setData(List<ImageView> views, List<MyImage> list, ImageCycleViewListener listener) { 64 setData(views, list, listener, 0); 65 } 66 67 /** 68 * 初始化viewpager 69 * 70 * @param views 71 * 要顯示的views 72 * @param showPosition 73 * 默認顯示位置 74 */ 75 public void setData(List<ImageView> views, List<MyImage> list, ImageCycleViewListener listener, int showPosition) { 76 mImageCycleViewListener = listener; 77 infos = list; 78 this.imageViews.clear(); 79 80 if (views.size() == 0) { 81 viewPagerFragmentLayout.setVisibility(View.GONE); 82 return; 83 } 84 85 for (ImageView item : views) { 86 this.imageViews.add(item); 87 } 88 89 int ivSize = views.size(); 90 91 // 設置指示器 92 indicators = new ImageView[ivSize]; 93 if (isCycle) 94 indicators = new ImageView[ivSize - 2]; 95 indicatorLayout.removeAllViews(); 96 for (int i = 0; i < indicators.length; i++) { 97 View view = LayoutInflater.from(getActivity()).inflate( 98 R.layout.view_cycle_viewpager_indicator, null); 99 indicators[i] = (ImageView) view.findViewById(R.id.image_indicator); 100 indicatorLayout.addView(view); 101 } 102 103 adapter = new ViewPagerAdapter(); 104 105 // 默認指向第一項,下方viewPager.setCurrentItem將觸發重新計算指示器指向 106 setIndicator(0); 107 108 viewPager.setOffscreenPageLimit(3); 109 viewPager.setOnPageChangeListener(this); 110 viewPager.setAdapter(adapter); 111 if (showPosition < 0 || showPosition >= views.size()) 112 showPosition = 0; 113 if (isCycle) { 114 showPosition = showPosition + 1; 115 } 116 viewPager.setCurrentItem(showPosition); 117 118 } 119 120 /** 121 * 設置指示器居中,默認指示器在右方 122 */ 123 public void setIndicatorCenter() { 124 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( 125 RelativeLayout.LayoutParams.WRAP_CONTENT, 126 RelativeLayout.LayoutParams.WRAP_CONTENT); 127 params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); 128 params.addRule(RelativeLayout.CENTER_HORIZONTAL); 129 indicatorLayout.setLayoutParams(params); 130 } 131 /** 132 * 是否循環,默認不開啟,開啟前,請將views的最前面與最后面各加入一個視圖,用於循環 133 * 134 * @param isCycle 135 * 是否循環 136 */ 137 public void setCycle(boolean isCycle) { 138 this.isCycle = isCycle; 139 } 140 141 /** 142 * 是否處於循環狀態 143 * 144 * @return 145 */ 146 public boolean isCycle() { 147 return isCycle; 148 } 149 150 /** 151 * 設置是否輪播,默認不輪播,輪播一定是循環的 152 * 153 * @param isWheel 154 */ 155 public void setWheel(boolean isWheel) { 156 this.isWheel = isWheel; 157 isCycle = true; 158 if (isWheel) { 159 handler.postDelayed(runnable, time); 160 } 161 } 162 163 /** 164 * 是否處於輪播狀態 165 * 166 * @return 167 */ 168 public boolean isWheel() { 169 return isWheel; 170 } 171 172 final Runnable runnable = new Runnable() { 173 174 @Override 175 public void run() { 176 if (getActivity() != null && !getActivity().isFinishing() 177 && isWheel) { 178 long now = System.currentTimeMillis(); 179 // 檢測上一次滑動時間與本次之間是否有觸擊(手滑動)操作,有的話等待下次輪播 180 if (now - releaseTime > time - 500) { 181 handler.sendEmptyMessage(WHEEL); 182 } else { 183 handler.sendEmptyMessage(WHEEL_WAIT); 184 } 185 } 186 } 187 }; 188 189 /** 190 * 釋放指示器高度,可能由於之前指示器被限制了高度,此處釋放 191 */ 192 public void releaseHeight() { 193 getView().getLayoutParams().height = RelativeLayout.LayoutParams.MATCH_PARENT; 194 refreshData(); 195 } 196 197 /** 198 * 設置輪播暫停時間,即沒多少秒切換到下一張視圖.默認5000ms 199 * 200 * @param time 201 * 毫秒為單位 202 */ 203 public void setTime(int time) { 204 this.time = time; 205 } 206 207 /** 208 * 刷新數據,當外部視圖更新后,通知刷新數據 209 */ 210 public void refreshData() { 211 if (adapter != null) 212 adapter.notifyDataSetChanged(); 213 } 214 215 /** 216 * 隱藏CycleViewPager 217 */ 218 public void hide() { 219 viewPagerFragmentLayout.setVisibility(View.GONE); 220 } 221 222 /** 223 * 返回內置的viewpager 224 * 225 * @return viewPager 226 */ 227 public BaseViewPager getViewPager() { 228 return viewPager; 229 } 230 231 /** 232 * 頁面適配器 返回對應的view 233 * 234 * @author Yuedong Li 235 * 236 */ 237 private class ViewPagerAdapter extends PagerAdapter { 238 239 @Override 240 public int getCount() { 241 return imageViews.size(); 242 } 243 244 @Override 245 public boolean isViewFromObject(View arg0, Object arg1) { 246 return arg0 == arg1; 247 } 248 249 @Override 250 public void destroyItem(ViewGroup container, int position, Object object) { 251 container.removeView((View) object); 252 } 253 254 @Override 255 public View instantiateItem(ViewGroup container, final int position) { 256 ImageView v = imageViews.get(position); 257 if (mImageCycleViewListener != null) { 258 v.setOnClickListener(new OnClickListener() { 259 260 @Override 261 public void onClick(View v) { 262 mImageCycleViewListener.onImageClick(infos.get(currentPosition - 1), currentPosition, v); 263 } 264 }); 265 } 266 container.addView(v); 267 return v; 268 } 269 270 @Override 271 public int getItemPosition(Object object) { 272 return POSITION_NONE; 273 } 274 } 275 276 @Override 277 public void onPageScrollStateChanged(int arg0) { 278 if (arg0 == 1) { // viewPager在滾動 279 isScrolling = true; 280 return; 281 } else if (arg0 == 0) { // viewPager滾動結束 282 if (parentViewPager != null) 283 parentViewPager.setScrollable(true); 284 285 releaseTime = System.currentTimeMillis(); 286 287 viewPager.setCurrentItem(currentPosition, false); 288 289 } 290 isScrolling = false; 291 } 292 293 @Override 294 public void onPageScrolled(int arg0, float arg1, int arg2) { 295 } 296 297 @Override 298 public void onPageSelected(int arg0) { 299 int max = imageViews.size() - 1; 300 int position = arg0; 301 currentPosition = arg0; 302 if (isCycle) { 303 if (arg0 == 0) { 304 currentPosition = max - 1; 305 } else if (arg0 == max) { 306 currentPosition = 1; 307 } 308 position = currentPosition - 1; 309 } 310 setIndicator(position); 311 } 312 313 /** 314 * 設置viewpager是否可以滾動 315 * 316 * @param enable 317 */ 318 public void setScrollable(boolean enable) { 319 viewPager.setScrollable(enable); 320 } 321 322 /** 323 * 返回當前位置,循環時需要注意返回的position包含之前在views最前方與最后方加入的視圖,即當前頁面試圖在views集合的位置 324 * 325 * @return 326 */ 327 public int getCurrentPostion() { 328 return currentPosition; 329 } 330 331 /** 332 * 設置指示器 333 * 334 * @param selectedPosition 335 * 默認指示器位置 336 */ 337 private void setIndicator(int selectedPosition) { 338 for (int i = 0; i < indicators.length; i++) { 339 indicators[i].setBackgroundResource(R.mipmap.icon_point); 340 } 341 if (indicators.length > selectedPosition) 342 indicators[selectedPosition].setBackgroundResource(R.mipmap.icon_point_pre); 343 } 344 345 /** 346 * 如果當前頁面嵌套在另一個viewPager中,為了在進行滾動時阻斷父ViewPager滾動,可以 阻止父ViewPager滑動事件 347 * 父ViewPager需要實現ParentViewPager中的setScrollable方法 348 */ 349 public void disableParentViewPagerTouchEvent(BaseViewPager parentViewPager) { 350 if (parentViewPager != null) 351 parentViewPager.setScrollable(false); 352 } 353 354 355 /** 356 * 輪播控件的監聽事件 357 * 358 * @author minking 359 */ 360 public static interface ImageCycleViewListener { 361 362 /** 363 * 單擊圖片事件 364 * 365 * @param position 366 * @param imageView 367 */ 368 public void onImageClick(MyImage info, int postion, View imageView); 369 } 370 }
③CycleViewPagerHandler==>為了防止內存泄漏,定義外部類,防止內部類對外部類的引用

1 public class CycleViewPagerHandler extends Handler { 2 Context context; 3 4 public CycleViewPagerHandler(Context context) { 5 this.context = context; 6 } 7 }
④ViewFactory==>創建ImageView工廠,獲取ImageView視圖的同時加載顯示url

1 public class ViewFactory { 2 3 /** 4 * 獲取ImageView視圖的同時加載顯示url 5 * 6 * @param 7 * @return 8 */ 9 public static ImageView getImageView(Context context, String url) { 10 ImageView imageView = (ImageView)LayoutInflater.from(context).inflate( 11 R.layout.view_banner, null); 12 ImageLoader.getInstance().displayImage(url, imageView);//最核心的部分 13 return imageView; 14 } 15 }
⑤MyImage==>這是廣告頁的bean類

1 class MyImage { 2 3 var link: String? = null 4 var image:String?=null 5 var title:String?=null 6 7 constructor():super() 8 constructor(image: String) : super() { 9 this.image = image 10 } 11 constructor(image:String,link:String,title:String){ 12 this.image=image 13 this.link=link 14 this.title=title 15 } 16 17 override fun toString(): String { 18 return "MyImage[link=$link,image=$image,title=$title]" 19 } 20 }
2.從服務器中拿到圖片的鏈接地址,一般廣告頁都是從服務器拿到的數據。本地的也可以,而且更加簡單了。我就拿前者舉例吧。
下面的代碼是我自己的一個請求,里面有很多東西都是自己定義的東西。
如:LogUtils.d_debugprint()是自己封裝好的一個日志輸出的類。
Constant也是自己封裝好的常量。
HttpUtil.httpPost也是自己封裝的網絡請求的類。
JsonUtil.get_key_string也是自己封裝Json解析的類。
MyImage是自己定義的一個Bean類。
Toasty是引用的第三方庫toast的類,用來提示用戶。
initialize()是自己初始化廣告頁,后面第三步就是寫這個函數了。
解釋清楚之后,請求的函數如下:

1 private fun getADfromServer() { 2 var urlPath = "" 3 var sign = "" 4 val encode = Constant.ENCODE 5 val school = Constant.SCHOOLDEFULT 6 if (LogUtils.APP_IS_DEBUG) { 7 urlPath = Constant.BASEURLFORTEST + Constant.School_AD 8 sign = MD5Util.md5(Constant.SALTFORTEST) 9 } else { 10 urlPath = Constant.BASEURLFORRELEASE + Constant.School_AD 11 sign = MD5Util.md5(Constant.SALTFORRELEASE) 12 } 13 val params = mapOf<String, Any?>("school" to school,"sign" to sign) 14 LogUtils.d_debugprint(TAG, Constant.TOSERVER + urlPath + "\n提交的map=" + params.toString()) 15 Thread(Runnable { 16 getAdfromServer= HttpUtil.httpPost(urlPath,params,encode) 17 // LogUtils.d_debugprint(TAG,Constant.GETDATAFROMSERVER+getAdfromServer) 18 val getCode:String 19 var msg:String?=null 20 var result:String 21 var getData:List<Map<String,Any?>> 22 getCode= JsonUtil.get_key_string(Constant.Server_Code,getAdfromServer!!) 23 if(Constant.RIGHTCODE.equals(getCode)) { 24 handler_result1.post { 25 msg = JsonUtil.get_key_string(Constant.Server_Msg, getAdfromServer!!) 26 result = JsonUtil.get_key_string(Constant.Server_Result, getAdfromServer!!) 27 getData = JsonUtil.getListMap("data", result)//=====這里可能發生異常 28 LogUtils.d_debugprint(TAG, "json解析出來的對象是=" + getData.toString()) 29 if (getData != null&&getData.size>0) { 30 for (i in getData.indices) { 31 val myImage=MyImage() 32 if(getData[i].getValue("link").toString()!=null&&getData[i].getValue("image").toString()!=null&&getData[i].getValue("title").toString()!=null) { 33 myImage.link = getData[i].getValue("link").toString() 34 myImage.image = getData[i].getValue("image").toString() 35 myImage.title = getData[i].getValue("title").toString() 36 myImageList.add(myImage)//=====這里保存了所有廣告信息 37 }else{ 38 Toasty.error(context,Constant.SERVERBROKEN).show() 39 } 40 } 41 initialize() //初始化頂部導航欄 42 }else{ 43 val myImage=MyImage() 44 myImage.link="" 45 myImage.image="" 46 myImage.title="" 47 myImageList.add(myImage) 48 //Toasty.error(context,Constant.SERVERBROKEN).show() 49 } 50 } 51 } 52 }).start() 53 }
3.然后就是initialize()函數了
說明一下==>>
myImageOverAD是覆蓋在廣告頁上的一張默認圖片,如果沒有網,或者請求失敗的時候,將顯示這張圖片。
cycleViewPager是廣告欄中的一個叫fragment的布局。
views是廣告欄的一個ArrayList<ImageView>(),可以動態添加ImageView
mAdCycleViewListener是廣告頁點擊的監聽器,第四步會詳細講。

1 @SuppressLint("NewApi") 2 private fun initialize() { 3 myImageOverAD!!.visibility=View.GONE 4 cycleViewPager = activity.fragmentManager.findFragmentById(R.id.oneFm_fragment_cycle_viewpager_content) as CycleViewPager 5 6 views.add(ViewFactory.getImageView(context, myImageList[myImageList.size - 1].image)) // 將最后一個ImageView添加進來 7 for (i in myImageList.indices) { 8 views.add(ViewFactory.getImageView(context, myImageList[i].image)) 9 } 10 views.add(ViewFactory.getImageView(context, myImageList[0].image)) // 將第一個ImageView添加進來 11 cycleViewPager!!.isCycle = true // 設置循環,在調用setData方法前調用 12 cycleViewPager!!.setData(views, myImageList, mAdCycleViewListener) // 在加載數據前設置是否循環 13 cycleViewPager!!.isWheel = true //設置輪播 14 cycleViewPager!!.setTime(5000) // 設置輪播時間,默認5000ms 15 cycleViewPager!!.setIndicatorCenter() //設置圓點指示圖標組居中顯示,默認靠右 16 }
4.然后是mAdCycleViewListener監聽器的實現了。
說明一下==>>
ADWebView是點擊廣告頁要調整的webView。
R.anim.slide_left_out是一個從左邊出去的動畫。
源碼如下:

1 private val mAdCycleViewListener = CycleViewPager.ImageCycleViewListener { info, position, imageView -> 2 var position = position 3 if (cycleViewPager!!.isCycle) { 4 position = position - 1 5 //Toasty.info(context,"標題:"+info.title+ "\n鏈接:" + info.link).show() 6 7 val bundle=Bundle() 8 bundle.putString("title",info.title) 9 bundle.putString("link",info.link) 10 val intent:Intent=Intent(context,ADWebView::class.java) 11 intent.putExtras(bundle) 12 startActivity(intent) 13 activity.overridePendingTransition(0,R.anim.slide_left_out) 14 } 15 }
5.廣告頁的布局代碼差點忘記了。
說明一下==>>
這里的fragment才是主角,下方的ImageView是覆蓋在廣告頁上的一張默認圖片,在沒有網獲取沒有成功請求到服務器的時候顯示的圖片。

1 <RelativeLayout 2 android:layout_width="match_parent" 3 android:layout_height="350pt"> 4 5 <fragment 6 android:id="@+id/oneFm_fragment_cycle_viewpager_content" 7 android:name="com.guangdamiao.www.mew_android_debug.banner.CycleViewPager" 8 android:layout_width="match_parent" 9 android:layout_height="350pt" 10 /> 11 12 <ImageView 13 android:id="@+id/oneFm_fragment_cycle_viewpager_content_over" 14 android:layout_width="match_parent" 15 android:layout_height="360pt" 16 android:src="@drawable/one_overad" 17 android:visibility="visible" 18 android:scaleType="centerCrop" 19 /> 20 21 </RelativeLayout>
效果如下: