简单图片轮播的控件的实现
本人菜鸟。
之前想找个轮播的控件,网上找了几篇文章,觉得搞的很复杂,思路也不是很清晰。所以干脆就自己写了。总的来说,是比较简单的。
思路:
图片轮播,其实就是滑动图片由手动改为自动而已。针对滑动图片,我立刻想到两个方案:
- horizontalScrollView
水平scrollView,本来就是滑动的,可以在它的childView中添加要显示的视图。 - viewPager
viewPager是一个很好用的视图容器,相信很多人已经用过了,添加视图、管理pager状态都很方便。
虽然两个都能用,但是使用horizontalScrollView有几个麻烦的地方:
- 要在horizontalScrollView里显示视图,需要
container.addView(childView)
;如果只add不remove,那么就会造成oom。所以需要我们自己去管理。
- 子视图的切换和响应需要自己捕获和定义。
使用viewPager就没有上面的问题。viewPager默认维护3个childView。后面会给出验证。
废话不多说了,直接上代码分析吧
package android.com.loopview.view;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import java.lang.ref.WeakReference;
/**
* Created by yueshaojun on 16/7/one.
*/
public class LoopViewPager extends ViewPager {
private static int LEFTTORIGHT = 0;
private static int RIGHTTOLEFT = 1;
private long mtime;
Run r =new Run(this);
public LoopViewPager(Context context) {
super(context);
}
public LoopViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 让子view动起来的操作
* 默认滚动间隔时间为1s
* 不设置默认不滚动
* @param time 滚动间隔时间
* */
public void loop(long time){
if(null == getAdapter()){
return;
}
mtime = time;
int item = getCurrentItem();
if(time <= 0){
time = 1000;
}
r.item = item;
r.time = time;
postDelayed(r, time);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
float oX = 0;
float nX = 0;
super.onTouchEvent(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE :
break;
case MotionEvent.ACTION_DOWN:
oX= ev.getX();//捕获down事件的X坐标
break;
case MotionEvent.ACTION_UP:
nX = ev.getX();//捕获up事件的X坐标
break;
}
//判断手势方向,以相差50为准
if(nX == oX){
return false;
}
if(nX-oX>50){
handleGesture(LEFTTORIGHT);
}else {
handleGesture(RIGHTTOLEFT);
}
return true;
}
private void handleGesture(int gesture){
if(gesture == LEFTTORIGHT){
//TODO 处理从左到右的手势
}
if(gesture == RIGHTTOLEFT){
//TODO 处理从右到左的手势
}
if (!looping){
return;
}
//移除callBack
removeCallbacks(r);
r.item = getCurrentItem();
r.time = mtime;
postDelayed(r, mtime);
}
static class Run implements Runnable{
WeakReference<LoopViewPager> wloopView;
int item;
long time;
Run(LoopViewPager lp){
wloopView = new WeakReference<LoopViewPager>(lp);
}
@Override
public void run() {
LoopViewPager loopView = wloopView.get();
if(item<loopView.getAdapter().getCount()-1) {
++item;
}else{
item = 0;
}
loopView.setCurrentItem(item);
loopView.postDelayed(this, time);
}
}
}
代码并不复杂(我之前网上看到的都是好大一坨),思路也很清晰:
调用loop方法开启轮播后,postDelay一下,在runnable中设置当前子视图。重写onTouchEvent捕获手势,如果有左右滑动,就移除当前runnable,重新postDelay。(其实左右滑动的手势操作,viewpager已经帮我们做好了,在ViewPager.OnPageChangeListener中响应处理)
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".activity.MainActivity">
<android.com.loopview.view.LoopViewPager
android:id="@+id/loop_view"
android:layout_width="match_parent"
android:layout_height="500dp">
</android.com.loopview.view.LoopViewPager>
<TextView
android:id="@+id/indicator"
android:layout_below="@id/loop_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#000000"
android:text="0" />
</RelativeLayout>
适配器
package android.com.loopview.view;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by yueshaojun on 16/six/29.
*/
public class MyAdapter extends PagerAdapter {
private Object[] objs;
public MyAdapter(Object[] inflateObjs){
objs = inflateObjs;
}
@Override
public int getCount() {
return objs.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Log.i("viewpager","instantiateItem:"+position);
container.addView((View) objs[position]);
return objs[position];
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.i("viewpager","destroyItem:"+position);
container.removeView((View) objs[position]);
}
}
activity
package android.com.loopview.activity;
import android.app.Activity;
import android.com.loopview.R;
import android.com.loopview.view.LoopViewPager;
import android.com.loopview.view.MyAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity implements ViewPager.OnPageChangeListener {
private LoopViewPager loopViewPager;
private TextView indicator;
private ImageView[] imageViews;
private MyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
addListener();
}
private void initData() {
int[] imgIds = new int []{
R.drawable.one,
R.drawable.two,
R.drawable.three,
R.drawable.four,
R.drawable.five,
R.drawable.six,
};
imageViews = new ImageView[imgIds.length];
for (int i = 0;i<imgIds.length;i++){
ImageView imageView = new ImageView(this);
imageView.setImageResource(imgIds[i]);
imageViews[i] = imageView;
}
myAdapter = new MyAdapter(imageViews);
}
private void initView() {
loopViewPager = (LoopViewPager) findViewById(R.id.loop_view);
indicator = (TextView) findViewById(R.id.indicator);
}
private void addListener() {
loopViewPager.setAdapter(myAdapter);
loopViewPager.addOnPageChangeListener(this);
// 开启轮播并设置轮播时间间隔
loopViewPager.loop(3000);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Log.i("viewpager", "onPageSelected:" + position);
indicator.setText(""+position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
其实和普通viewPager的使用几本没有区别,只是需要用loop()方法开启轮播,不开启就是普通viewPager。
另外在添加子视图instantiateItem、移除子视图destroyItem和切换当前视图onPageSelected里打了日志。日志结果如下:
07-10 17:35:09.777 18715-18715/android.com.loopview I/viewpager: instantiateItem:0
07-10 17:35:09.778 18715-18715/android.com.loopview I/viewpager: instantiateItem:1
07-10 17:35:22.820 18715-18715/android.com.loopview I/viewpager: onPageSelected:1
07-10 17:35:23.345 18715-18715/android.com.loopview I/viewpager: instantiateItem:2
07-10 17:35:23.627 18715-18715/android.com.loopview I/viewpager: onPageSelected:2
07-10 17:35:24.111 18715-18715/android.com.loopview I/viewpager: destroyItem:0
07-10 17:35:24.113 18715-18715/android.com.loopview I/viewpager: instantiateItem:3
07-10 17:35:24.343 18715-18715/android.com.loopview I/viewpager: onPageSelected:3
07-10 17:35:24.844 18715-18715/android.com.loopview I/viewpager: destroyItem:1
07-10 17:35:24.844 18715-18715/android.com.loopview I/viewpager: instantiateItem:4
开始的时候添加0,1页,换到1添加2页,换到2页销毁0添加3。以此类推。说明viewpager只保存当前页和它左右两页(如果有)的视图。
改进:
之前用的是postDelay,可以用timer计时器去控制轮播。虽然timertask其实也是实现了runnable接口,但是由timer去管理线程完成调度显然要好的多。
改进以后的代码:
package android.com.loopview.view;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import java.lang.ref.WeakReference;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by yueshaojun on 16/7/one.
*/
public class LoopViewPager extends ViewPager {
private Timer timer= new Timer();
private MyHandler myHandler = new MyHandler(this);
public LoopViewPager(Context context) {
super(context);
}
public LoopViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 让子view动起来的操作
* 默认滚动间隔时间为1s
* 不设置默认不滚动
* @param time 滚动间隔时间
* */
public void loop(long time){
if(null == getAdapter()){
return;
}
timer.schedule(new LooperTimerTask(this),1000,time);
}
class LooperTimerTask extends TimerTask{
LoopViewPager loopViewPager;
public LooperTimerTask(Object obj){
loopViewPager = new WeakReference<>((LoopViewPager)obj).get();
}
@Override
public void run() {
int item = loopViewPager.getCurrentItem();
if(item<loopViewPager.getAdapter().getCount()-1) {
++item;
}else{
item = 0;
}
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putInt("item", item);
message.setData(bundle);
myHandler.sendMessage(message);
}
}
class MyHandler extends Handler{
LoopViewPager loopViewPager;
public MyHandler(Object obj){
loopViewPager = new WeakReference<>((LoopViewPager)obj).get();
}
@Override
public void handleMessage(Message msg) {
int item = msg.getData().getInt("item");
loopViewPager.setCurrentItem(item);
}
}
}
在timerTask中获取当前viewpager的位置,然后通知Ui更新。