1. 程式人生 > >Android 輪播圖(Viewpager+Handler定時器)

Android 輪播圖(Viewpager+Handler定時器)

效果圖
發現好多人提到banner,第一個想法就是擼個第三方依賴。然後出bug了,開啟三方程式碼,一堆檔案無從下手,改了又擔心出現新bug,然後又替換了第二個三方…
一個ViewPager能實現的功能,何必求助第三方。
Banner的實現技術點主要在於
1 無限迴圈,當banner滑到最後一張後繼續滑動,要滑回第一張
2 自動輪播
Adapter程式碼

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import
android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; import com.bumptech.glide.Glide; import java.util.List; /** * Created by KID on 2017/8/11 0011. */ public class PagerBannerAdapter
extends PagerAdapter {
private Context context; private List<String> imgUrls; private ViewPager mViewPager; private boolean isPlay=false; public PagerBannerAdapter(Context context, List<String> imgUrls,ViewPager mViewPager) { this.context = context; this
.imgUrls = imgUrls; this.mViewPager=mViewPager; } //是否自動播放第一張圖片到第二張 public void setPlayingFirstItem(boolean isPlay){ this.isPlay=isPlay; } @Override public Object instantiateItem(ViewGroup container, final int position) { final int pos = position%imgUrls.size(); View view = LayoutInflater.from(context).inflate(R.layout.item_vp, container, false); ImageView imageView = (ImageView) view.findViewById(R.id.img_item); Glide.with(context).load(imgUrls.get(pos)).into(imageView); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(context,"當前看到的要做點選事件的position="+pos+"-----實際position="+position,Toast.LENGTH_SHORT).show(); } }); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); Log.d("BannerView","destroyItem position======"+position); } @Override public int getCount() { //理論上當圖片不為1張時,getCount可以設定無窮大,這裡設定成圖片4倍只是想看會不會滑到頭,實際上3倍就夠了,設2倍的話,當圖片只有2張時可能會碰到頭 return imgUrls.size()==1?imgUrls.size():imgUrls.size()*4; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } //當顯示介面載入完時呼叫該方法 @Override public void finishUpdate(ViewGroup container) { Log.d("BannerView","finishUpdate position======"+mViewPager.getCurrentItem()); int position = mViewPager.getCurrentItem(); if(imgUrls.size()==1){ //TODO 但輪播圖片只有一張的時候,什麼都不做,或者隱藏小圓點,提示文字之類。當然,如果你只有一張圖片,你還想重複滑出這張圖片的話,在這裡開始你的騷操作 } else { //TODO 但輪播圖片超過3張時,每當圖片的position超過圖片數量時,切換viewpager當前選擇item(去除切換動畫效果) if (position == 0){ position = imgUrls.size(); //TODO 自動輪播的時候,需要這部判斷來讓第一張和第二張平滑過渡 if(!isPlay){ mViewPager.setCurrentItem(position,false); } } else if(position>imgUrls.size()+1){ mViewPager.setCurrentItem(position%imgUrls.size(),false); } } } }

Activity程式碼

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;

/**
 * Created by KID on 2017/8/16 0016.
 * Handler實現圖片自動輪播
 */
public class PlayBannerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {

    @Bind(R.id.view_pager)
    ViewPager viewPager;
    @Bind(R.id.tv_page)
    TextView pageTv;

    List<String> imgUrls=new ArrayList<>();
    PagerBannerAdapter adapter;
    private boolean isStill;//是否靜止
    int pos ;//看到的position
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);
        ButterKnife.bind(this);
        initData();
    }
    private void initData() {
        imgUrls.add("http://img2.91.com/uploads/allimg/140417/59-14041GQ2040-L.jpg");
        imgUrls.add("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1205/25/c2/11755122_1337938898578_800x600.jpg");
        imgUrls.add("http://img04.tooopen.com/images/20130114/tooopen_22372502.jpg");
        imgUrls.add("http://img3.iqilu.com/data/attachment/forum/201308/21/100932s9pwjxmm4h8jy704.jpg");
        imgUrls.add("http://images.ali213.net/picfile/pic/2013/02/25/927_48.jpg");
        adapter=new PagerBannerAdapter(PlayBannerActivity.this,imgUrls,viewPager);
        viewPager.setAdapter(adapter);
        viewPager.addOnPageChangeListener(this);

        //第一次進入頁面開始輪播圖的間隔要久一點,要等圖片,還有你其他的頁面資料加載出來以後才輪播,提高使用者體驗
        mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,4000);

    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if(positionOffset==0&&positionOffsetPixels==0){
            isStill=true;
        }else {
            isStill=false;
            mHandler.removeMessages(MESSAGE_PLAY_IMAGE);
        }
    }
    @Override
    public void onPageSelected(int position) {
        //列印position為真實的position
        pos=position%imgUrls.size();
        pageTv.setText(pos+1+"/"+imgUrls.size());
    }
    @Override
    public void onPageScrollStateChanged(int state) {
        if(state==0){//靜止
            isStill=true;
            mHandler.removeMessages(MESSAGE_PLAY_IMAGE);
            mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY);
        }
    }
    /**
     * 訊息處理
     */
    //輪播下一張圖片
    private static final int MESSAGE_PLAY_IMAGE=1001;
    //輪播間隔時間
    private static final long PLAY_DELAY=2000;
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                /**滑動中,同步播放進度*/
                case MESSAGE_PLAY_IMAGE:
                    if(isStill){
                        if(pos==0){
                            adapter.setPlayingFirstItem(true);
                            viewPager.setCurrentItem(0,false);
                            viewPager.setCurrentItem(pos+1);
                            adapter.setPlayingFirstItem(false);
                        }else {
                            viewPager.setCurrentItem(pos+1);
                            adapter.setPlayingFirstItem(false);
                        }
                    }
                    break;
            }
        }
    };
}

核心程式碼

    //當顯示介面載入完時呼叫該方法
    @Override
    public void finishUpdate(ViewGroup container) {
        Log.d("BannerView","finishUpdate position======"+mViewPager.getCurrentItem());
        int position = mViewPager.getCurrentItem();
        if(imgUrls.size()==1){
            //TODO 但輪播圖片只有一張的時候,什麼都不做,或者隱藏小圓點,提示文字之類。當然,如果你只有一張圖片,你還想重複滑出這張圖片的話,在這裡開始你的騷操作

        } else {
            //TODO 但輪播圖片超過3張時,每當圖片的position超過圖片數量時,切換viewpager當前選擇item(去除切換動畫效果)
            if (position == 0){
                position = imgUrls.size();
                //TODO 自動輪播的時候,需要這部判斷來讓第一張和第二張平滑過渡
                if(!isPlay){
                    mViewPager.setCurrentItem(position,false);
                }
            } else if(position>imgUrls.size()+1){
                mViewPager.setCurrentItem(position%imgUrls.size(),false);
            }
        }
    }

將getCount return 無窮大,當圖片顯示在第一張位置(position=0)的時候,我們要讓viewpager左邊也有圖片,所以將position的索引加上圖片數量。其實到這裡,我們就已經能實現圖片無限輪播了,再加上hanlder延遲執行,自動輪播效果就出來了。但position的數量無限增加,item的數量也會一直增加,哪怕viewpager預設只會儲存當前和相鄰兩頁,那些被劃過的pager佔用的記憶體也不會馬上釋放掉。所以我們不能讓position,也就是item無限增加。 ViewPager的切換中,可以設定去除切換效果setCurrentItem(position,false);
而我們需要找準一個時機,讓viewpager切回position最小時的狀態 mViewPager.setCurrentItem(position%imgUrls.size(),false);
handler自動輪播的程式碼就比較簡單了,通過 mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY);
和mHandler.removeMessages(MESSAGE_PLAY_IMAGE)來實現。當viewpager處於靜止狀態時,丟擲一個延遲 n秒執行的訊息去讓viewpager切換到下一頁,viewpager切換到下一頁後,又會處於靜止狀態,再次丟擲n秒執行的訊息。當延遲訊息執行前,viewpager狀態不是靜止了,就把延遲訊息取消掉。
至此,一個自動輪播的banner就出來了。
可能你看著,感覺程式碼一堆,沒第三方寫起來簡潔- -!
ok,讓我們稍微封裝一下

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.bumptech.glide.Glide;

import java.util.List;

/**
 * Created by KID on 2017/8/17 0017.
 */

public class BannerView extends FrameLayout implements ViewPager.OnPageChangeListener {

    //輪播下一張圖片
    private static final int MESSAGE_PLAY_IMAGE=1001;
    //輪播間隔時間
    private static final long PLAY_DELAY=2000L;
    //第一張圖片間隔多久開始輪播
    private static final long FIRST_DELAY=4000L;

    private ViewPager viewPager;
    private Context context;
    private List<String> imgUrls;
    private BannerPageAdapter bannerPageAdapter;

    private int cusPosition;//當前看到的選中位置
    private boolean isAutoPlay=false;

    private BannerListener bannerListener;
    public interface BannerListener{
        void OnBannerSelect(int position);
        void OnBannerClick(int position);
    }
    public void setBannerListener(BannerListener bannerListener){
        this.bannerListener=bannerListener;
    }

    private boolean isStill;//是否靜止
    int pos ;//看到的position

    public BannerView(Context context) {
        super(context);
        this.context=context;
    }
    public BannerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
        init(context);
    }
    public BannerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context=context;
    }

    /**
     * @param isAutoPlay 是否自動播放 ,實現比較暴力,直接不讓handler訊息執行
     */
    public void setAutoPlay(boolean isAutoPlay){
        this.isAutoPlay=isAutoPlay;
    }
    /**
     * 設定圖片
     * @param imageList
     */
    public void setImageList(List<String>imageList){
        imgUrls=imageList;
        bannerPageAdapter=new BannerPageAdapter();
        viewPager.setAdapter(bannerPageAdapter);
        viewPager.addOnPageChangeListener(this);

        //第一次進入頁面開始輪播圖的間隔要久一點,要等圖片,還有你其他的頁面資料加載出來以後才輪播,提高使用者體驗
        mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,FIRST_DELAY);
    }

    private void init(Context context) {
        LayoutInflater.from(context).inflate(R.layout.view_banner, this);
        viewPager= (ViewPager) findViewById(R.id.view_pager);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if(positionOffset==0&&positionOffsetPixels==0){
            isStill=true;
        }else {
            isStill=false;
            mHandler.removeMessages(MESSAGE_PLAY_IMAGE);
        }
    }

    @Override
    public void onPageSelected(int position) {
        //列印position為真實的position
        pos=position%imgUrls.size();
        if(cusPosition!=pos){//引入自動播放後,會在某一位置做一個無動畫效果的頁面切換,加入這步判斷才回調防止重複呼叫
            bannerListener.OnBannerSelect(pos);
        }
        cusPosition=pos;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if(state==0){//靜止
            isStill=true;
            mHandler.removeMessages(MESSAGE_PLAY_IMAGE);
            mHandler.sendEmptyMessageDelayed(MESSAGE_PLAY_IMAGE,PLAY_DELAY);
        }
    }


    class BannerPageAdapter extends PagerAdapter {
        private boolean isPlay=false;
        public BannerPageAdapter() {
        }
        //是否自動播放第一張圖片到第二張
        public void setPlayingFirstItem(boolean isPlay){
            this.isPlay=isPlay;
        }
        @Override
        public Object instantiateItem(ViewGroup container, final int position) {
            final int pos = position%imgUrls.size();
            View view = LayoutInflater.from(context).inflate(R.layout.item_vp, container, false);
            ImageView imageView = (ImageView) view.findViewById(R.id.img_item);
            Glide.with(context).load(imgUrls.get(pos)).into(imageView);
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bannerListener.OnBannerClick(pos);
                }
            });
            container.addView(view);

            return view;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
        @Override
        public int getCount() {
//          理論上當圖片不為1張時,getCount可以設定無窮大,這裡設定成圖片4倍只是想看會不會滑到頭,實際上3倍就夠了,設2倍的話,當圖片只有2張時可能會碰到頭
//          return Integer.MAX_VALUE;
            return imgUrls.size()==1?imgUrls.size():imgUrls.size()*4;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        //當顯示介面載入完時呼叫該方法
        @Override
        public void finishUpdate(ViewGroup container) {
            Log.d("BannerView","finishUpdate position======"+viewPager.getCurrentItem());
            int position = viewPager.getCurrentItem();
            if(imgUrls.size()==1){
                //TODO 但輪播圖片只有一張的時候,什麼都不做,或者隱藏小圓點,提示文字之類。當然,如果你只有一張圖片,你還想重複滑出這張圖片的話,在這裡開始你的騷操作

            } else {
                //TODO 但輪播圖片超過3張時,每當圖片的position超過圖片數量時,切換viewpager當前選擇item(去除切換動畫效果)
                if (position == 0){
                    position = imgUrls.size();
                    //TODO 自動輪播的時候,需要這部判斷來讓第一張和第二張平滑過渡
                    if(!isPlay){
                        viewPager.setCurrentItem(position,false);
                    }
                } else if(position>imgUrls.size()+1){
                    viewPager.setCurrentItem(position%imgUrls.size(),false);
                }
            }
        }
    }
    /**
     * 訊息處理
     */
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                /**滑動中,同步播放進度*/
                case MESSAGE_PLAY_IMAGE:
                    if(isStill&&isAutoPlay){
                        if(pos==0){
                            bannerPageAdapter.setPlayingFirstItem(true);
                            viewPager.setCurrentItem(0,false);
                            viewPager.setCurrentItem(pos+1);
                            bannerPageAdapter.setPlayingFirstItem(false);
                        }else {
                            viewPager.setCurrentItem(pos+1);
                            bannerPageAdapter.setPlayingFirstItem(false);
                        }
                    }
                    break;
            }
        }
    };

    /**
     * 退出頁面後防止handler還執行
     */
    public void stopPlay(){
        if(mHandler!=null)mHandler.removeMessages(MESSAGE_PLAY_IMAGE);
    }
}

使用

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;

public class CusBannerActivity extends AppCompatActivity {
    @Bind(R.id.bannerView)
    BannerView bannerView;
    @Bind(R.id.tv_page)
    TextView pageTv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cus_banner);
        ButterKnife.bind(this);
        final List<String>list=new ArrayList<>();
        list.add("http://img2.91.com/uploads/allimg/140417/59-14041GQ2040-L.jpg");
        list.add("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1205/25/c2/11755122_1337938898578_800x600.jpg");
        list.add("http://img04.tooopen.com/images/20130114/tooopen_22372502.jpg");
        list.add("http://img3.iqilu.com/data/attachment/forum/201308/21/100932s9pwjxmm4h8jy704.jpg");
        list.add("http://images.ali213.net/picfile/pic/2013/02/25/927_48.jpg");
        bannerView.setImageList(list);
        bannerView.setAutoPlay(true);
        bannerView.setBannerListener(new BannerView.BannerListener() {
            @Override
            public void OnBannerSelect(int position) {
                pageTv.setText(position+1+"/"+list.size());
                Toast.makeText(CusBannerActivity.this, "選中======="+position, Toast.LENGTH_SHORT).show();
            }
            @Override
            public void OnBannerClick(int position) {
                Toast.makeText(CusBannerActivity.this, "點選======="+position, Toast.LENGTH_SHORT).show();
            }
        });
        pageTv.setText(1+"/"+list.size());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        bannerView.stopPlay();
    }
}

在onDestory裡,我們需要讓hanlder不再執行。PS,有些手機退出介面後,onDestory遲遲不執行,這時候就要結合實際情況,在onPause或者監聽返回鍵,你自己專案的返回按鈕執行bannerView.stopPlay()這步操作了。很多三方庫封裝的banner,定時器沒有關閉操作,在頁面finish後,延遲執行的操作裡,找不到之前被關閉頁面的view,導致App崩潰。

相關推薦

Android Viewpager+Handler定時

發現好多人提到banner,第一個想法就是擼個第三方依賴。然後出bug了,開啟三方程式碼,一堆檔案無從下手,改了又擔心出現新bug,然後又替換了第二個三方… 一個ViewPager能實現的功能,何必求助第三方。 Banner的實現技術點主要在於 1

JQ多張同時顯示

HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <

js面向物件---無縫附面向過程程式碼

window.onload = function(){ var t1 = new Lb("box"); t1.l(); //設定包含圖片的ul寬度 t1.dot(); //點哪個圓,相對應的圖片顯示

自定義ViewGroup和FrameLayout實現包括底部小圓點

廣告輪播圖在現在的APP首頁比較常見,主要的實現方式有兩種,一種是通過ViewPager,一種是通過自定義ViewGroup。前者的實現方式比較簡便,本篇文章講的是第二種方法,有人說用ViewPager不是更方便嗎,的確,但是我們通過自己定義ViewGroup,

Android 實現 一 :三方框架 自定義viewPager CircleViewPager.實現無限

使用流程:1  。 gradle中新增依賴compile 'com.zhpan.library:viewpager:1.0.3'2.在xml檔案中新增如下程式碼:<com.zhpan.viewpager.view.CircleViewPager andr

android banner卡片式之recyclerView橫向定時滑動

                                    使用環境:       使用首頁banner輪播圖,卡片式輪播 使用效果:                                                    使用方法:

TabLayout + ViewPager 雙層巢狀,側拉展示條目頭像加listview,點選頭像可切換PullToRefreshListView重新整理載入

模組簡介: 1.底部:TabLayout + ViewPager輪播圖 2.主頁面可測拉 展示頭像,可選擇系統相簿,切換圖片(二級取樣) 3.TabLayout + ViewPager巢狀TabLayout + ViewPager以展示 正在上映 和 *

Android框架之路——Banner實現RecyclerView新增Header

一、簡介 Banner能實現迴圈播放多個廣告圖片和手動滑動迴圈等功能。因為原生ViewPager並不支援迴圈翻頁, 要實現迴圈還得需要自己去動手。Banner框架可以進行不同樣式、不同動畫設定, 以及完善的api方法能滿足大部分軟體首頁輪播圖效果的需求。

android簡單實現左右無限滑動,自動輪

直接上程式碼了,都有註釋,原理很簡單 public class MainActivity extends AppCompatActivity { private static final String Tag = MainActivity.class.getSimpleName();

ViewPager打造Banner\引導頁Guide

前言 今年7月時,在Github釋出了一個開源的Banner庫,雖然Star不多,但還是有少部分人使用。 Banner效果: 昨天,有使用此庫的同學提出需求,想在引導頁的時候用這個庫並且最後一頁有進入按鈕如何實現,為滿足他的需求,也方便更多開發者是

left margin font ges opacity onload rgb absolut pin <style>  *{ margin:0; padding:0; list-style:none; }  .box{ width:520px; height:

Android

網址 ott not 設置 amp 文件添加 .cn ive create package com.example.carson_ho.android_banner;import android.os.Bundle;import android.support.v7.app

vue慕課網音樂項目手記:5-手寫滾動

flow overflow box cor 慕課 efs PE osi 通過 在這一節,會封裝一些公用的函數來添加classname,改變image的寬度。 具體如下: 首先:封裝一個slider的組件 <template> <div cl

11.練習

current cor ext 技術 one 練習 func children script 效果圖: 源代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset

AndroidBanner

Android實現輪播圖 昨天早睡,今天早上精力充沛!所以寫一篇部落格記錄一下。 效果圖: 第一步新增依賴: compile 'com.youth.banner:banner:1.4.9' 第二步在佈局中新增: <com.youth.banner.Banner

關於1

不瞞大家說,輪播圖簡直是我的噩夢,就像是論文之於大學生[受虐滑稽][受虐滑稽] 簡單輪播圖 最簡單的輪播圖,用WebAPI做的,點選圖片底下的數字跳轉到相對應的圖片。 1、首先把結構寫出來 <div class="box" id="box"> <div

完整4

我們終於迎來了最完整的輪播圖 當然還是WebAPI做的 依舊和之前一樣,結構樣式只給程式碼,script裡面會有一些註釋 有的圖片路徑可能會報錯什麼的,因為我本來使用的是本地圖片,然後換成了臨時百度的圖片,圖片路徑出錯了你們要自己換圖片,反正是不要指望我了,嘻嘻 一、結構 <d

無縫滾動3

我吃了炫邁做出來的輪播圖,停不下來的那種 一、結構 <div class="box" id="screen"> <ul> <li><img src="http://img.mp.itc.cn/upload/20161107/033

左右焦點2

顧名思義, 這個輪播圖只有左右兩個焦點,只能左右滑動。 一、結構 <div id="box" class="all"> <div class="ad"> <ul id="imgs"> <li><img src="

Android Banner切換圖片的效果

Android XBanner使用詳解 2018年03月14日 08:19:59 AND_Devil 閱讀數:910   前言:現如今的很多APP都