(出处:https://cloud.tencent.com/developer/user/1148436/activities)
前序:
网上的这类 ViewPager 很多,但是很多都不够好,体现在 bug多、对少页面不支持,例如1~2张图片、功能整合不全(无限+页码)等等,本类由我从零到无完成的,基本已找完 bug,注释丰富,方便大家理解。
特点:
1,代码量少 , 共两个类,约合 310 行代码 (除去注释)
2,可扩展 , 再加个 handler 即可实现自动轮播
3,时间复杂度低
4,耦合度低,只依赖了 imageLoader,可以自己切换
5,关键点皆给出了详细注释,方便二次开发
功能:
1,右滑无限循环(2^32,或更大),支持页面数>=2, 左滑循环直至原始第一张;
2,上述效果伴随着正确的页面小点显示,具体效果可自定义
使用:
// 第一个参数是 Activity;第二个是 ViewPager 对象;第三个是 imageLoader 实例,若使用自己的方法加载图片,请修改代码;第四个是图片链接字符串数组
new MyViewPager(this,viewpager,imageLoder,imageUrls)
.setUnClickLooper(true) // 设置开启第一种效果的无限循环
.setClickLooper(true) // 设置开启第二种效果的无限循环
.init(); // 实例化全部
效果图:
第一类效果,布局嵌套时,缩略图形式显示滑动
第二类效果,点击单张图片进入 dialog 风格,大图显示形式
类简介:
上述两种效果都能自己选择是否开启无限滑动。
LoopViewpagerAdapter 类,继承于 PagerAdapter,主要实现功能是无限循环,逻辑处理比较集中于此,通过接口方式加载页 View。
1 package cn.share.bananacloud.coustomViews.coustomEdittextInput.MyViewPager;
2
3 import android.app.Activity;
4 import android.support.v4.view.PagerAdapter;
5 import android.util.Log;
6 import android.view.LayoutInflater;
7 import android.view.View;
8 import android.view.ViewGroup;
9
10 /**
11 * Created by 林冠宏 on 2016/4/9.
12 *
13 * viewPager 无限循环
14 *
15 */
16
17 public class LoopViewpagerAdapter extends PagerAdapter{
18
19 private int images;
20 private String[] imageUrls;
21 private View[] views;
22 private boolean isLooper;
23 private LayoutInflater layoutInflater;
24 private getItemViewListener getItemViewListener;
25
26 public LoopViewpagerAdapter(
27 Activity activity,
28 String[] imageUrls,
29 boolean isLooper, /** 是否进行无限循环 */
30 getItemViewListener getItemViewListener)
31 {
32 this.isLooper = isLooper;
33 this.images = imageUrls.length;
34 this.layoutInflater = activity.getLayoutInflater();
35 this.imageUrls = imageUrls;
36 views = new View[images];
37 this.getItemViewListener = getItemViewListener;
38 }
39
40 @Override
41 public int getCount() {
42 if(isLooper){
43 if(images<3){ /** 1~2 张图片的情况 强制不开启循环 */
44 return images;
45 }else {
46 return 65535; /** 设置足够大 2^32 */
47 }
48 }else{
49 return images;
50 }
51 }
52
53 /** 调用顺序 destroyItem -> instantiateItem */
54
55 @Override
56 public void destroyItem(ViewGroup container, int position, Object object) {
57 Log.d("zzzzz","destroy "+position);
58 container.removeView((View) object); /** 和 instantiateItem 相照应,这个是移除,不用担心内存会累加 */
59 }
60
61 @Override
62 public Object instantiateItem(ViewGroup container,int position) {
63 Log.d("zzzzz", "position " + position);
64 if(isLooper && images==3){
65 /** 3张的特殊处理,在先右滑了一定张数后,再左滑,此时初始化的 距离当前位置 的第前2张和后面一张会重复 (x-2) == (x+1) */
66 View view = getItemViewListener.getItemView(layoutInflater, container, imageUrls[position%images], position);
67 container.addView(view);
68 return view;
69 }else {
70 if (position < images) {
71 if (views[position] == null) {
72 views[position] = getItemViewListener.getItemView(layoutInflater, container, imageUrls[position], position);
73 }
74 } else if (position == images && isLooper) { /** 解决由 setCurrentItem 引发的问题 */
75 /** 时间复杂度不高,每经过一次,进入一次 */
76 /** 如果看大图vp 从临界最大值点击进来,此时没有之前的 view 赋值,直接 view[max-1] 会造成 空指针 exception,这是会初始化的有 max-2,max,max-1 */
77 /*if(images>2){ // 最小情况的判断,因为此时的 container 还没有移除下标 0 的图片,再添加的话会造成不能重复添加的异常
78 views[0] = getItemViewListener.getItemView(layoutInflater,container,imageUrls[0],0);
79 }else{ // 刚好是 2 张,手动移除下标 0
80
81 }*/
82 /** 如果 共4张图,此时 positon = 3 setCurrentItem() 就会造成加载了 4-2,4,4-1,4 在 view[] 是越界状态,故需要手动赋值 0,2和3 也初始化了,但是 1 没
83 * 若一直右滑,到 下标 1 便会抛 Cannot add a null child view to a ViewGroup,所以要 加上 views[images - 3] 也初始化
84 * */
85 views[0] = getItemViewListener.getItemView(layoutInflater, container, imageUrls[0], 0);
86 views[images - 3] = getItemViewListener.getItemView(layoutInflater, container, imageUrls[images - 3], 0);
87 container.addView(views[0]); /** add 0 不会有问题, */
88 return views[0];
89 }
90 }
91
92 container.addView(views[position%images]);
93 return views[position%images];
94 }
95
96 @Override
97 public boolean isViewFromObject(View view, Object o){
98 return view.equals(o);
99 }
100
101 public interface getItemViewListener{
102 View getItemView(LayoutInflater layoutInflater,ViewGroup container,final String url,final int position);
103 }
104
105 }
MyViewPager 类,无父类,内部使用了 LoopViewpagerAdapter ,在无限循环的基础上,附加实现了页码小点的显示。
1 package cn.share.bananacloud.coustomViews.coustomEdittextInput.MyViewPager;
2
3 import android.app.Activity;
4 import android.app.AlertDialog;
5 import android.graphics.Bitmap;
6 import android.support.v4.view.ViewPager;
7 import android.util.Log;
8 import android.view.LayoutInflater;
9 import android.view.MotionEvent;
10 import android.view.View;
11 import android.view.ViewGroup;
12 import android.view.Window;
13 import android.widget.ImageView;
14 import android.widget.LinearLayout;
15 import android.widget.ProgressBar;
16 import android.widget.TextView;
17
18 import com.nostra13.universalimageloader.core.ImageLoader;
19 import com.nostra13.universalimageloader.core.assist.FailReason;
20 import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
21
22 import cn.share.bananacloud.R;
23 import cn.share.bananacloud.common.commonDataHelper;
24 import cn.share.bananacloud.coustomViews.coustomEdittextInput.zoomImageView.PhotoView;
25 import cn.share.bananacloud.tools.imageLoderHelper;
26
27 /**
28 * Created by 林冠宏 on 2016/4/9.
29 *
30 * viewPager 无限滑动 + 点击看大图,仍可滑动 + 当前张号页码
31 *
32 */
33
34 public class MyViewPager {
35
36 private Activity activity;
37 private String[] imageUrls;
38 protected ImageLoader imageLoder = null;
39 private ViewPager viewPager;
40 private int limitTemp = 0; /** 临界中间值 */
41 private int picnum;
42 private boolean unClickLooper = false;
43 private boolean ClickLooper = false;
44
45 public MyViewPager(
46 Activity activity,
47 ViewPager viewPager,
48 ImageLoader imageLoder,
49 String[] imageUrls
50 ){
51 this.activity = activity;
52 this.imageUrls = imageUrls;
53 this.imageLoder = imageLoder;
54 this.viewPager = viewPager;
55 picnum = imageUrls.length;
56 }
57
58 public MyViewPager setUnClickLooper(boolean unClickLooper){
59 this.unClickLooper = unClickLooper;
60 return this;
61 }
62
63 public MyViewPager setClickLooper(boolean ClickLooper){
64 this.ClickLooper = ClickLooper;
65 return this;
66 }
67
68 public void init(){
69 viewPager.setAdapter
70 (
71 new LoopViewpagerAdapter
72 (
73 activity,
74 imageUrls,
75 unClickLooper,
76 new LoopViewpagerAdapter.getItemViewListener() {
77 @Override
78 public View getItemView(LayoutInflater layoutInflater, ViewGroup container, final String url, final int position) {
79 View view = layoutInflater.inflate(R.layout.cy_item_main_image, container, false);
80 ImageView imageView = (ImageView) view.findViewById(R.id.image);
81 imageView.setOnClickListener(new View.OnClickListener() {
82 @Override
83 public void onClick(View v) {
84 int temp = position%picnum; /** 记得取余数 position 在无限循环的模式不是 0 ~ picnum */
85 if(temp==0 && position!=0){
86 showVPimage(picnum-1);
87 }else{
88 showVPimage(temp);
89 }
90
91 }
92 });
93 final ProgressBar spinner = (ProgressBar) view.findViewById(R.id.loading);
94
95 imageLoder.displayImage(url, imageView,
96 imageLoderHelper.getLoderOption(commonDataHelper.phoneWidth, R.drawable.hot_live_default_image, 0), new SimpleImageLoadingListener() {
97 @Override
98 public void onLoadingStarted(String imageUri, View view) {
99 spinner.setVisibility(View.VISIBLE);
100 Log.d("zzzzz", "onLoadingStarted");
101 }
102
103 @Override
104 public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
105 spinner.setVisibility(View.GONE);
106 }
107
108 @Override
109 public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
110 Log.d("zzzzz", "onLoadingComplete "+imageUri);
111 spinner.setVisibility(View.GONE);
112 }
113 });
114 return view;
115 }
116 }
117 )
118 );
119
120 viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
121 @Override
122 public void onPageScrolled(int i, float v, int i1) {
123
124 }
125
126 @Override
127 public void onPageSelected(int i) { /** 先于 instantiateItem 执行 */
128 TextView nowCount = (TextView) activity.findViewById(R.id.nowCount);
129 if((i+1)%picnum==0){
130 nowCount.setText("" + picnum + " /");
131 }else {
132 nowCount.setText("" + (i + 1) % picnum + " /");
133 }
134 }
135
136 @Override
137 public void onPageScrollStateChanged(int i) {
138
139 }
140 });
141 }
142
143 private void showVPimage(int positon) {
144 final AlertDialog dlg = new AlertDialog.Builder(activity).create();
145
146 View localView = LayoutInflater.from(activity).inflate(R.layout.viewpager, null);
147
148 localView.setOnTouchListener(new View.OnTouchListener() {
149 @Override
150 public boolean onTouch(View v, MotionEvent event) {
151 dlg.dismiss();
152 return false;
153 }
154 });
155
156 ViewPager viewPager = (ViewPager) localView.findViewById(R.id.imageContainer);
157 final LinearLayout pointContainer = (LinearLayout) localView.findViewById(R.id.pointContainer);
158 for(int i=0;i<picnum;i++){
159 ImageView imageView = (ImageView) LayoutInflater.from(activity).inflate(R.layout.viewpager_point,pointContainer,false);
160 pointContainer.addView(imageView);
161 }
162 viewPager.setAdapter
163 (
164 new LoopViewpagerAdapter
165 (
166 activity,
167 imageUrls,
168 ClickLooper,
169 new LoopViewpagerAdapter.getItemViewListener() {
170 @Override
171 public View getItemView(LayoutInflater layoutInflater, ViewGroup container, String url, int position) {
172 View view = layoutInflater.inflate(R.layout.show_big_pic, container, false);
173 PhotoView imageView = (PhotoView) view.findViewById(R.id.bigImage);
174 imageView.setdimissDialog(new PhotoView.dimissDialog() {
175 @Override
176 public void doDimissDialog() {
177 dlg.dismiss();
178 }
179 });
180 try {
181 imageLoder.displayImage
182 (
183 url,
184 imageView,
185 imageLoderHelper.getLoderOption(commonDataHelper.phoneWidth+50, R.drawable.hot_live_default_image,0)
186 );
187 } catch (Exception ignored){}
188 return view;
189 }
190 }
191 )
192 );
193 viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
194 @Override
195 public void onPageScrolled(int i, float v, int i1) {
196
197 }
198
199 @Override
200 public void onPageSelected(int i) { /** 先于 instantiateItem 执行 */
201 Log.d("onPageSelected","onPageSelected ->"+i);
202 /** 为了减少 CPU 和 内存的 绘图消耗,这里不采用 for 等循环的方式改 点背景,改用条件语句 */
203 if(ClickLooper){
204 Log.d("onPageSelected","i is ->"+i +" limitTemp is "+limitTemp);
205 /** 循环情况临界点的颜色恢复 */
206 if((i % picnum)==(picnum-1) && limitTemp == 0){ /** 左滑 */
207 ((ImageView) pointContainer.getChildAt(0)).setImageResource(R.drawable.white_point_xml);
208 }else {
209 if (i >= picnum && i % picnum == 0) { /** 右滑 */
210 ((ImageView) pointContainer.getChildAt(picnum - 1)).setImageResource(R.drawable.white_point_xml);
211 }
212 }
213 }
214 i = i % picnum;
215 ((ImageView) pointContainer.getChildAt(i)).setImageResource(R.drawable.color_point);
216 if (i != 0 && i != picnum - 1) { /** 非临界值,两边都要修改 */
217 ((ImageView) pointContainer.getChildAt(i>limitTemp ? i-1:i+1)).setImageResource(R.drawable.white_point_xml);
218 }else {
219 ((ImageView) pointContainer.getChildAt(i==picnum-1 ? i-1:i+1)).setImageResource(R.drawable.white_point_xml);
220 }
221 limitTemp = i;
222 }
223
224 @Override
225 public void onPageScrollStateChanged(int i) {
226
227 }
228 });
229 viewPager.setCurrentItem(positon);
230 ((ImageView)pointContainer.getChildAt(positon)).setImageResource(R.drawable.color_point);
231
232
233 Window localWindow = dlg.getWindow();
234 localWindow.getAttributes();
235 dlg.show();
236 localWindow.setContentView(localView);
237 localWindow.setGravity(17);
238 localWindow.setLayout(-1, -1);
239 }
240
241 }
Demo项目 github 链接: