摘要:项目要求做一个广告页,实现几秒更换一次广告页,下方还有指示第几张广告页,同样也支持手动左滑或右滑。
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 + " 提交的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+ " 链接:" + 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>
效果如下: