转载请注明出处:http://blog.csdn.net/jakeyangchina/article/details/53691592
好多app上都会看到广告图片无限制的循环播放,今天带大家一步步实现自己定义的控件,项目中使用自己定义的控件那就是一个字帅
按照惯例,先看效果图:
功能介绍:
图片可以手动左右滑动,当手指抬起时图片间隔3秒时间自动播放,无缝隙循环播放
思路分析:
- 图片可以手动左右滑动
这里采用ViewPager实现左右滑动,又因为当手指按下时,手动滑动,自动切换功能停止,这里采用自定义ViewPager类,把down和up事件提供给调用者,自己写个回调方法,当手指按下时通知调用者停止定时器,当手指抬起时,开启定时器,实现自动定时切换页面 - 松开手后图片间隔3秒时间自动切换
这里采用定时器,当间隔3秒时执行任务,也可以使用handler(略),这里采用定时器 - 图片切换时对应下方圆点也对应改变
这里是通过获取ViwePager当前显示的条目(索引)设置给对应圆点显示
步骤:
- 自定义类MyViewPager继承ViewPager
- 自定义类MyContainer继承RelativeLayout,将所有的逻辑处理等在这个类中进行,封装到这个类中
- 创建一个容器,存储圆点,根据页面数来创建圆点ImageView控件,再根据当前页面显示的条目指定对应圆点显示,获取到当前页面索引值是通过给ViewPager设置页面改变监听器来获取到
- 这里的原点采用shape通过xml绘制
- 在MyContainer视图显示在主MainActivity中
原理图:
具体代码实现:
自定义类MyViewPager继承ViewPager
public class MyViewPager extends ViewPager {
public MyViewPager(Context context) {
this(context,null);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(listener != null) {
listener.down();
}
break;
case MotionEvent.ACTION_UP:
if(listener != null) {
listener.up();
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
private OnTouchChangeListener listener;
public void setTouchChangeListener(OnTouchChangeListener listener) {
this.listener = listener;
}
public interface OnTouchChangeListener {
public void down();
public void up();
}
}
上面代码很容易理解,这里主要是想办法获取到down和up事件
传递给外部调用,OnTouchChangeListener 是自己定义的接口提供回调方法通知调用者手指的动作
定义容器MyContainer类,继承RelativeLayout控件
/**
* 存储圆点图片,原点的父容器
*/
private LinearLayout linearLayout;
/**
* 自定义ViewPager类
*/
private MyViewPager viewPager;
/**
* 适配器
*/
private MyAdapter myAdapter;
/**
* 定时器
*/
private Timer timer;
/**
* 定时器任务
*/
private MyTimerTask task;
/**
* 原点父容器的高度
*/
private int height = 50;
/**
* 设置原点的宽
*/
private int circleWidth = 25;
/**
* 设置原点的高
*/
private int circleHeight = 25;
/**
* 设置原点父容器的背景色
*/
private int backgroundColor = Color.parseColor("#55aaaaaa");
/**
* 当前容器的高度
*/
private int containerHeight = 300;
/**
* 弱引用,存储Activity,防止内存泄漏
*/
private WeakReference<Activity> weak;
/**
* 集合存储外部传过来的数据
*/
private List<ImageView> lists = new ArrayList<>();
上面定义的字段,标注很详细
public void init() {
//设置当前容器布局大小
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT,containerHeight);
//给当前控件设置布局
setLayoutParams(params);
//创建自定义ViewPager
viewPager = new MyViewPager(getContext());
//设置自定义ViewPager布局大小
RelativeLayout.LayoutParams paramsPager = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
//给自定义ViewPager添加布局
viewPager.setLayoutParams(paramsPager);
//创建适配器
myAdapter = new MyAdapter();
//设置适配器
viewPager.setAdapter(myAdapter);
//设置页面改变监听器
viewPager.addOnPageChangeListener(this);
//设置自定义的触摸监听器
viewPager.setTouchChangeListener(this);
//创建控件下方的视图容器,存储圆点图片,原点的父容器
linearLayout = new LinearLayout(getContext());
RelativeLayout.LayoutParams linearLayoutParams = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT,height);
//设置背景颜色
linearLayout.setBackgroundColor(backgroundColor);
//设置子控件对齐方式
linearLayout.setGravity(Gravity.CENTER_VERTICAL|Gravity.RIGHT);
//设置本身在外部的对齐方式
linearLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
//设置布局
linearLayout.setLayoutParams(linearLayoutParams);
//将自定ViewPager控件添加到当前容器中
addView(viewPager);
//将圆点容器添加到当前容器中
addView(linearLayout);
}
这个方法里主要是进行初始化,创建ViewPager控件和LinearLayout容器存储圆点,需要注意的是,将这个两个容器创建完一定要添加到当前容器中,通addView方法添加当前布局
public void addImageData(List<ImageView> list, Activity activity) {
if(weak == null) {
//防止关闭时,引用外部对象造成内存泄漏
weak = new WeakReference<Activity>(activity);
}
//添加数据时,先清空
lists.clear();
this.lists = list;
//设置当前页面
viewPager.setCurrentItem(3000*lists.size());
myAdapter.notifyDataSetChanged();
//添加圆点到容器中
addImagePoint(0);
//开启定时器
startScrollPager();
}
上方代码主要是外部调用设置数据,设置ViewPager当前显示的页面setCurrentItem(3000*lists.size());这里的3000*lists.size()是取页面数的整数倍,3000是随便设置的数,这样可以实现ViewPager左右循环无缝隙滑动,相当于取个中间页,左右都有页数,可以滑动,这里需要注意WeakReference弱引用,将外部传进来activity存在弱引用中,防止外部页面开始关闭了,但是activity页面对象被引用无法正常关闭页面导致内存泄漏
/**
* 根据数据个数创建原点
* @param curentPosition
*/
private void addImagePoint(int curentPosition) {
for(int i = 0; i < lists.size(); i++) {
ImageView imageView = new ImageView(getContext());
//设置图片控件布局,circleWidth和circleWidth指定图片大小
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
circleWidth,circleHeight);
//设置原点图片右边间距大小
params.rightMargin = 15;
if(curentPosition == i) {
imageView.setImageResource(R.drawable.circle_a);
}else {
imageView.setImageResource(R.drawable.circle_b);
}
//设置布局
imageView.setLayoutParams(params);
//把原点添加到父容器中
linearLayout.addView(imageView);
}
}
通过ViewPager页面数,动态创建原点,通过当前页面索引选中当前原点
/**
* 页面改变时,同步显示的原点
* @param position
*/
private void changeImageState(int position) {
for(int i = 0; i < linearLayout.getChildCount(); i++) {
ImageView childAt = (ImageView) linearLayout.getChildAt(i);
if(position == i) {
childAt.setImageResource(R.drawable.circle_a);
}else {
childAt.setImageResource(R.drawable.circle_b);
}
}
}
这个方法根据不同页面显示对应圆点,可以认为是同步原点显示
private class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//网络请求图片在这里进行,注意防止图片错位
ImageView imageView = lists.get(position % lists.size());
//添加对应控件到ViewPager
((ViewPager)container).addView(imageView);
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//从ViewPager中移除对应控件
((ViewPager)container).removeView((ImageView) object);
}
}
上方类是给ViewPager设置适配器,这里需要注意,通过instantiateItem方法给当前页面添加ImageView控件,在destroyItem方法中一定要移除对应ImageView控件,这里需要注意getCount() 方法返回最大页数,为了实现无限制页面播放,其实页面是有限制的,页面数为Integer.MAX_VALUE
/**
*创建定时器,方法多种,可以使用handler
*/
private class MyTimerTask extends TimerTask {
@Override
public void run() {
if(weak != null) {
weak.get().runOnUiThread(new Runnable() {
@Override
public void run() {
//设置显示当前页面
viewPager.setCurrentItem(viewPager.getCurrentItem()+1);
}
});
}
}
}
这个类是创建定时器任务,当定时时间到了就会执行run方法,在这里设置显示下一个页面,当前页面加一
/**
* 开启定时器
*/
public void startScrollPager() {
timer = new Timer();
task = new MyTimerTask();
//执行定时器
timer.schedule(task,3000,3000);
}
/**
* 停止滚动
*/
public void stopScrollPager() {
if(timer != null) {
timer.cancel();
timer = null;
}
if(task != null) {
task.cancel();
task = null;
}
}
startScrollPager() 方法开启定时器,stopScrollPager() 关闭定时器
@Override
public void down() {
//当手指按下时,取消定时器
stopScrollPager();
}
@Override
public void up() {
//当手指抬起时,启动定时器
startScrollPager();
}
当手指按下时,关闭定时器,当手指抬起时,开启定时器
/**
* 当关闭页面时,取消定时器
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
//取消定时器
stopScrollPager();
}
这个方法是当页面从当前窗体解除关联时会执行这个方法,在这里关闭定会器
绘制原点xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="10dp" />
<solid android:color="@android:color/white" />
</shape>
这里是绘制原点
这里MainActivity方法就不给列出了,详细下载Demo,代码里标注很详细
如果你觉得此文章对你有收获,那么就顶下,你的无意间的动作就是我写出好文章的动力,希望能够帮助到大家,共同进步
如果大家还有什么疑问,请在下方留言。