簡單圖片輪播的控件的實現
本人菜鳥。
之前想找個輪播的控件,網上找了幾篇文章,覺得搞的很複雜,思路也不是很清晰。所以乾脆就自己寫了。總的來說,是比較簡單的。
思路:
圖片輪播,其實就是滑動圖片由手動改爲自動而已。針對滑動圖片,我立刻想到兩個方案:
- 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更新。