手记

UI效果の循环轮播ViewPager

一、简介

      项目中曾几次用到了轮播图,即用ViewPager实现自动播放、以同一方向的动画效果循环播放的效果。曾经在网络搜集了很多资料,但是都有大大小小的问题,这里对网络的思路进行总结改进并封装到相关类中。

    1、自动播放 - 相比于Thread-Handler、CountDownTimer以及TimerTask的方式,这里采用纯Handler发送消息的方式实现循环

    2、循环播放 - 保证以同一方向的动画进行滑动,这里还是采用了网络思路,即设置最大值的办法。但是网络的设置最 大值时,对于低于4张的图片都会伴随着崩溃问题的产生,这里采用Double方式进行处理

        该效果实现总共有三个类:LooperViewPager、LooperLayout、LooperViewPagerHandle。 

        LooperViewPager:主要对ViewPager以及ViewPager所使用的Adapter进行整合以及处理

      LooperLayout:主要对自动播放功能、底部指示点功能进行整合与处理

      LooperViewPagerHandle:主要对以上两个类所需要的方法进行

         备注: 加载图片采用的是Facebook新出的Fresco框架,保证图片的加载不会内存溢出,所以需要提前引入Fresco框架的库。

二、 代码如下:

1、LooperViewPager类

public class LooperViewPager extends ViewPager {
public LooperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LooperViewPager(Context context) {
super(context);
}
/**
 * 设置适配器
 * 
 * @param views
 */
public void setPagerAdapter(Context context, List<String> urls,
DisplayImageOptions opts, OnPagerClickListener listener) {
List<View> mViews = LooperViewPagerHandle.getHandledViews(context,
urls, opts, listener);
if (mViews != null && mViews.size() > 0) {
LoopedAdapter mAdapter = new LoopedAdapter(mViews);
setAdapter(mAdapter);
}
}
/**
 * 适配器
 * 
 * @author Administrator
 * 
 */
class LoopedAdapter extends PagerAdapter {
private List<View> mViews;
public LoopedAdapter(List<View> data) {
mViews = data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
if (mViews.size() == 1) {
return 1;
}
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == (View) arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// TODO Auto-generated method stub
container.removeView(mViews.get(position % mViews.size()));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// TODO Auto-generated method stub
View view = mViews.get(position % mViews.size());
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {// 存在父类布局,表示界面已经加载过
parent.removeAllViews();
}
container.addView(view);
return mViews.get(position % mViews.size());
}
}
public interface OnPagerClickListener {
/**
 * ViewPager页面点击事件
 * 
 * @param v
 * @param position
 * @param url
 */
public void onPagerClick(View v, int position, String url);
}
}


2、LooperLayout类


public class LooperLayout extends LinearLayout implements OnPageChangeListener {
private Context mContext;
private LayoutInflater mInflater;
// 内容页面View
private View mContentView;
// 滑动页面
private LooperViewPager mLoopViewPager;
// 滑动指示器
private LinearLayout mLoopIndicator;
// 指示器长度
private int mIndicatorLength;
// 指示器数组
private List<View> mIndicators = new ArrayList<View>();
// 默认指示器大小
private static final int DEFAULT_INDICATOR_WIDTH = 20;
// 默认指示器间隔
private static final int DEFAULT_INDICATOR_MARGIN = 15;
// 默认播放周期
private static final long DEFAULT_PLAY_INTERVAL = 5000;
// 发送What
private int MESSAGE_WHAT = 0;
// 指示器默认图标
private static final int DEFAULT_INDICATOR_CHECK = R.drawable.indicator_checked;
private static final int DEFAULT_INDICATOR_UNCHECK = R.drawable.indicator_unchecked;
// 指示点大小
private int mIndicatorWidth = DEFAULT_INDICATOR_WIDTH;
// 指示点间隔
private int mIndicatorMargin = DEFAULT_INDICATOR_MARGIN;
// 默认播放时差
private long mPlayInterval = DEFAULT_PLAY_INTERVAL;
// 指示器资源
private int mIndicatorChecked = DEFAULT_INDICATOR_CHECK;
private int mIndicatorUnChecked = DEFAULT_INDICATOR_UNCHECK;
// 当前显示的指示器
private int mCurrentIndicatorPosition = 0;
// 是否开始播放
private boolean hasStartPlaying;
// 计时
private Handler mTimerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MESSAGE_WHAT) {
if (hasStartPlaying) {
int index = mLoopViewPager.getCurrentItem() + 1;
mLoopViewPager.setCurrentItem(index);
}
}
}
};
public LooperLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LooperLayout(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.mContext = context;
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
onCreateView();
}
private void onCreateView() {
mContentView = mInflater.inflate(R.layout.view_loop_pager, null);
mLoopViewPager = (LooperViewPager) mContentView
.findViewById(R.id.mLooperViewPager);
mLoopIndicator = (LinearLayout) mContentView
.findViewById(R.id.mLooperIndicator);
mLoopViewPager.setOnPageChangeListener(this);
addView(mContentView, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
}
/**
 * 设置适配显示
 * 
 * @param context
 * @param urls
 * @param opts
 * @param listener
 */
public void setPagerAdapter(Context context, List<String> urls,
DisplayImageOptions opts, OnPagerClickListener listener) {
mLoopViewPager.setPagerAdapter(context, urls, opts, listener);
// 生成指示器
if (!hasDoubled(urls)) {
mIndicatorLength = urls.size();
} else {
// 执行double处理后,指示器数减半
mIndicatorLength = urls.size() / 2;
}
initIndicators();
// 默认指示器显示
mLoopViewPager.setCurrentItem(0);
setCurrentIndicator(0);
}
/**
 * 根据数据长度,生成指示器
 */
private void initIndicators() {
for (int i = 0; i < mIndicatorLength; i++) {
View v = getIndicator();
mLoopIndicator.addView(v);
mIndicators.add(v);
}
}
/**
 * 生成一个指示器
 */
private View getIndicator() {
View v = new View(mContext);
LayoutParams params = new LayoutParams(mIndicatorWidth, mIndicatorWidth);
params.leftMargin = mIndicatorMargin;
v.setLayoutParams(params);
v.setBackgroundResource(mIndicatorUnChecked);
return v;
}
/**
 * 设置当前指示器显示
 * 
 * @param index
 */
private void setCurrentIndicator(int index) {
clearIndicators();
View v = mIndicators.get(index);
v.setBackgroundResource(mIndicatorChecked);
mCurrentIndicatorPosition = index;
}
/**
 * 重置所有指示器显示
 */
private void clearIndicators() {
for (View v : mIndicators) {
v.setBackgroundResource(mIndicatorUnChecked);
}
}
/**
 * 重置指示器样式
 */
private void resetIndicators() {
for (View v : mIndicators) {
LayoutParams params = (LayoutParams) v.getLayoutParams();
params.width = mIndicatorWidth;
params.height = mIndicatorWidth;
params.leftMargin = mIndicatorMargin;
v.setLayoutParams(params);
}
}
/**
 * 重置指示器资源
 */
private void resetIndicatorsBg() {
int len = mIndicators.size();
for (int i = 0; i < len; i++) {
if (i == mCurrentIndicatorPosition) {
mIndicators.get(i).setBackgroundResource(mIndicatorChecked);
} else {
mIndicators.get(i).setBackgroundResource(mIndicatorUnChecked);
}
}
}
/**
 * 设置指示器位置
 * 
 * @param mode
 */
public void setIndicatorGravity(Activity activity, int mode) {
DisplayMetrics outMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
float density = outMetrics.density;
switch (mode) {
case 0:// 右下角
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
params1.alignWithParent = true;
params1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
params1.bottomMargin = (int) (10 * density);
params1.rightMargin = (int) (20 * density);
mLoopIndicator.setLayoutParams(params1);
break;
case 1:// 底部居中
RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
params2.alignWithParent = true;
params2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params2.addRule(RelativeLayout.CENTER_HORIZONTAL);
params2.bottomMargin = (int) (10 * density);
mLoopIndicator.setLayoutParams(params2);
break;
}
}
/**
 * 判断是否执行过Double处理
 * 
 * @param data
 * @return
 */
private boolean hasDoubled(List<String> data) {
// 只有2或3条数据,即double后4或6条数据,才会有double的可能
if (data != null && (data.size() == 4 || data.size() == 6)) {
int len = data.size() / 2;
for (int i = 0; i < len; i++) {
// 前半数据与后半数据完全相同,则为double处理过
if (!data.get(i).equals(data.get(i + len))) {
return false;
}
}
return true;
}
return false;
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
int currentIndex = arg0 % mIndicatorLength;
setCurrentIndicator(currentIndex);
// 定时跳转
if (mIndicatorLength > 1) {
mTimerHandler.removeMessages(MESSAGE_WHAT);
mTimerHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, mPlayInterval);
}
}
// /////////PUBLIC METHOD
/**
 * 设置播放间隔
 * 
 * @param time
 */
public void setAutoPlayInterval(long time) {
if (time > 0) {
this.mPlayInterval = time;
}
}
/**
 * 设置指示器样式
 * 
 * @param size
 * @param margin
 */
public void setIndicatorStyle(int size, int margin) {
if (size > 0) {
this.mIndicatorWidth = size;
}
if (margin > 0) {
this.mIndicatorMargin = margin;
}
resetIndicators();
}
/**
 * 设置指示器资源
 * 
 * @param check
 * @param uncheck
 */
public void setIndicatorResource(int check, int uncheck) {
this.mIndicatorChecked = check;
this.mIndicatorUnChecked = uncheck;
resetIndicatorsBg();
}
/**
 * 开始播放
 */
public void startPlay() {
mTimerHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, mPlayInterval);
hasStartPlaying = true;
}
/**
 * 停止播放
 */
public void stopPlay() {
mTimerHandler.removeMessages(MESSAGE_WHAT);
hasStartPlaying = false;
}
}

3、LooperViewPagerHandle类


public class LooperViewPagerHandle {
/**
 * 根据URL生产对应的View
 * 
 * @param url
 * @param listener
 * @return
 */
private static ImageView getView(Context context, final String url,
final int position, DisplayImageOptions opts,
final OnPagerClickListener listener) {
if (TextUtils.isEmpty(url.trim())) {
return null;
}
final FrescoImageView view = new FrescoImageView(context);
LayoutParams params = new LayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
view.setAdjustViewBounds(true);
FrescoDisplayUtils.setFrescoParam(view, R.drawable.money_default_img,
com.facebook.drawee.drawable.ScalingUtils.ScaleType.FIT_XY);
FrescoDisplayUtils.displayImage(url, view);
if (listener != null) {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
listener.onPagerClick(view, position, url);
}
});
}
return view;
}
/**
 * 将URL数组转化为View适配数组
 * 
 * @param urls
 * @param listener
 * @return
 */
private static List<View> getViews(Context context, List<String> urls,
DisplayImageOptions opts, OnPagerClickListener listener) {
if (urls == null) {
return null;
}
boolean hasDoubled = hasDoubled(urls);
int len = urls.size();
if (hasDoubled) {
len = len / 2;
}
List<View> mViews = new ArrayList<View>();
for (String url : urls) {
ImageView view = getView(context, url, mViews.size() % len, opts,
listener);
if (view != null) {
mViews.add(view);
}
}
return mViews;
}
/**
 * 针对String数组特殊情况进行Double处理
 * 
 * @param views
 * @return
 */
private static List<String> handleUrl(List<String> urls) {
if (urls == null || urls.size() == 0 || urls.size() == 1
|| urls.size() > 3) {
return urls;
}
List<String> mTempUrls = urls;
urls.addAll(mTempUrls);
return urls;
}
/**
 * <外部调用>获取处理后的View数组
 * 
 * @param context
 * @param urls
 * @param listener
 * @return
 */
public static List<View> getHandledViews(Context context,
List<String> urls, DisplayImageOptions opts,
OnPagerClickListener listener) {
List<String> mHandledUrls = handleUrl(urls);
return getViews(context, mHandledUrls, opts, listener);
}
/**
 * 判断是否执行过Double处理
 * 
 * @param data
 * @return
 */
private static boolean hasDoubled(List<String> data) {
// 只有2或3条数据,即double后4或6条数据,才会有double的可能
if (data != null && (data.size() == 4 || data.size() == 6)) {
int len = data.size() / 2;
for (int i = 0; i < len; i++) {
// 前半数据与后半数据完全相同,则为double处理过
if (!data.get(i).equals(data.get(i + len))) {
return false;
}
}
return true;
}
return false;
}
}

三、调用方式(setPagerAdaper方法中的Options参数为预留参数,直接传null即可,或者删掉)

1、添加数据显示

     mLooperLayout.setPagerAdapter(MainActivity.this, data,  null, listener).

     参数1:context 参数2:List<String> 要显示的图片url 参数3:null即可 参数4:每张图片的点击监听

2、开始播放

     mLooperLayout.startPlay().

 

3、停止播放

mLooperLayout.stopPlay().

4、设置指示点的类型

     mLooperLayout.setIndicatorGravity(context, 0). 

     mode为0,则指示点显示在右下角;mode为1,则指示点显示在底部中央

5、设置播放间隔

      mLooperLayout.setAutoPlayInterval(5000).


本demo仅在于网上思路修改完成,不喜勿喷。如果发现问题,欢迎回复!

原文链接:http://www.apkbus.com/blog-192385-59639.html

0人推荐
随时随地看视频
慕课网APP