ViewPager實現輪播廣告圖

輪播廣告在如今的應用中比較常見,下面就來實現下該功能(文章參考了網上流傳的黑馬的視頻教程)
先來看下具體的實現效果:java

clipboard.png

實現思路:
1.爲ViewPager設置數據源,實現ViewPager的滾動
2.將圓點指示器與ViewPager的頁面對應起來
3.實現左右滑動均能無限循環
4.實現自動播放
5.實現當手指滑動的時候取消自動播發android

首先須要說明的就是在這篇文章裏並無同時實現左右循環滑動和手指觸碰自動中止的功能,這個問題尚未解決,留待之後再解決,也但願有知道的朋友在下面留言,先謝謝拉。數組

1.首先咱們定義佈局文件,用來顯示Banner

這裏咱們採用對的是相對佈局RelativeLayout,固然也能夠採用幀佈局FrameLayout。佈局文件activity_mian.xml代碼:app

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.qc.admin.mylunbotu.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="160dp">

        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v4.view.ViewPager>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_alignParentBottom="true"
            android:background="#66000000"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:padding="5dp">

            <TextView
                android:id="@+id/tv_desc"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxLines="1"
                android:text="這是圖片描述"
                android:textColor="@android:color/white" />

            <!--用來填充圓點指示器的容器-->
            <LinearLayout
                android:id="@+id/point_container"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:orientation="horizontal" />
        </LinearLayout>
    </RelativeLayout>
</RelativeLayout>

2.初始化圓點指示器

這裏咱們提供的圖片數據和顯示的文本數據都是靜態的,直接從數組資源中得到的,如:框架

//圖片資源id數組
        imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};
        // 文本描述
        contentDescs = new String[]{
                "鞏俐不低俗,我就不能低俗",
                "撲樹又回來啦!再唱經典老歌引萬人大合唱",
                "揭祕北京電影如何升級",
                "樂視網TV版大派送",
                "熱血屌絲的反殺"
        };

咱們能夠本身定義兩個shape,來顯示圓點指示器的兩種不一樣顯示狀態,而後再定義一個selector,將它設置爲控件的背景(ImageView),經過其setEnable(Boolean bool)方法能夠自動的決定使用哪一個圖片。代碼分別以下:ide

a.正常狀態下,灰色圓點,point_normal.xml:佈局

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="5dp"
        android:height="5dp" />
    <solid android:color="#44000000" />

</shape>

b.點擊狀態下,白色圓點 point_pressed.xml:post

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size android:width="5dp" android:height="5dp"/>
    <solid android:color="#FFFFFFFF"/>
</shape>

c.設置背景時的xml文件, point_bg.xml:this

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_enabled="true" android:drawable="@drawable/point_pressed"/>
    <item android:state_enabled="false" android:drawable="@drawable/point_normal"/>
</selector>

具體代碼:spa

private void initData() {

        //圖片資源id數組
        imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};
        // 文本描述
        contentDescs = new String[]{
                "鞏俐不低俗,我就不能低俗",
                "撲樹又回來啦!再唱經典老歌引萬人大合唱",
                "揭祕北京電影如何升級",
                "樂視網TV版大派送",
                "熱血屌絲的反殺"
        };
        imageViewList = new ArrayList<>();
        LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        mParams.leftMargin = 15;
        mParams.topMargin = 2;

        ImageView imageView;
        ImageView pointView;
        //初始化要展現的ImageView,並添加圓點指示器
        for (int i = 0; i < imageResIds.length; i++) {
            // 初始化圖片
            imageView = new ImageView(this);
            imageView.setBackgroundResource(imageResIds[i]);
            imageViewList.add(imageView);

            // 初始化指示器
            pointView = new ImageView(this);
            pointView.setBackgroundResource(R.drawable.point_bg);
            pointView.setLayoutParams(mParams);
            if (i == 0) {
                textView.setText(contentDescs[0]);
                pointView.setEnabled(true);
            } else {
                pointView.setEnabled(false);
            }
            mPointsLayout.addView(pointView);
        }
    }

注意:建立shape文件的時候,若是找不到,能夠切換到project目錄下,在drawable目錄下右鍵,new->Drawable resource file ,獲得以下界面:

clipboard.png

將selector直接改成shape便可

3.爲ViewPager設置適配器

class MyAdapter extends PagerAdapter {

        // 1.返回條目的總數
        @Override
        public int getCount() {

            //return imageViewList.size();
            return Integer.MAX_VALUE;
        }

        // 2.返回要顯示的條目,並建立條目
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //container:容器,其實也就是ViewPager
            //position:當前要顯示的條目的位置

            int newPosition = position % imageViewList.size();
            ImageView imageView = imageViewList.get(newPosition);
            //a.將View對象添加到container容器中
            container.addView(imageView);
            //b.把View對象返回給框架,適配器
            return imageView;
        }

        // 3.銷燬條目,其實就是將要銷燬的對象object從container中移除出去就行了
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        // 4.指定複用的判斷邏輯(通常爲固定寫法)
        @Override
        public boolean isViewFromObject(View view, Object object) {
            // 當滑動到新的條目以後,又返回回來,view是否能夠被複用
            return view == object;
        }
    }

在這裏咱們實現的是僞無限循環,其實就是將咱們的第一項設置爲一個很大的數的中間位置(Integer.MAX_VALUE),這樣當咱們向左向右滑動的時候,將返回的position對數據的大小進行取模運算%,根據相應的位置設置相應的圖片或者文字便可。這樣就實現了向右向左都是無限循環了。

4.實現自動播放

咱們固然能夠直接開啓一個線程,在裏面設置ViewPager的當前項,可是直接使用Handler更便於進行控機制,由於咱們能夠爲ViewPager設置滾動監聽器。
自動播放控制代碼:

private void startRun() {
        mHandler = new Handler();
        mHandler.postDelayed(mTaskRunnable, delayMillis);

    }

    //該線程一直運行着,知道activity被銷燬,此時將isActivityAlive設置爲false
    final Runnable mTaskRunnable = new Runnable() {
        @Override
        public void run() {
            // 若是activity未被銷燬,就一直執行該線程
            // 在ViewPager的OnPageChangeListener方法中決定是否將isAutoRun置反
            if (isActivityAlive) {
                if (isAutoRun) {
                    viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size());
                    mHandler.postDelayed(mTaskRunnable, delayMillis);
                } else {
                    mHandler.postDelayed(mTaskRunnable, delayMillis);
                }
            }
        }
    };

爲ViewPager設置滾動監聽器,這裏咱們直接讓當前類實現ViewPager.OnPageChangeListener接口,實現其中的抽象方法:

@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        int newPosition = position % imageViewList.size();
        textView.setText(contentDescs[newPosition]);

        // 先將上一個置位false,將當前位置置位true,這樣可使得初始化的時候就在第一個位置
        // (由於previousSelectedItem的未賦值時候的初始值默認爲0)
        mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false);
        mPointsLayout.getChildAt(newPosition).setEnabled(true);
        previousSelectedItem = newPosition;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        switch (state) {
            // 靜止狀態
            case SCROLL_STATE_IDLE:
                isAutoRun = true;
                break;
            // 拖拽中
            case SCROLL_STATE_DRAGGING:
                isAutoRun = false;
                break;
            // 拖拽後鬆手,自動回到最終位置的過程
            case SCROLL_STATE_SETTLING:
                isAutoRun = true;
                break;
        }
    }

這裏面咱們就實現了當手指滑動的時候中止自動播放,當手指擡起的時候又開始了自動播放。可是,注意,你覺得這些代碼組合起來就能實現循環滾動的同時還能手指控制是否自動播放?好吧,我當時也是這樣覺得的,運行後會報錯:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

clipboard.png
意思是咱們待加入的子視圖已經有了一個父視圖,須要調用它的父視圖的removeView()方法來將其移除,以後再添加,然而事實證實並無什麼卵用。留待之後解決吧,在只能犧牲掉無線循環了,這裏咱們須要將getCount方法中的返回值改爲咱們數據的大小就行了:

@Override
        public int getCount() {

            return imageViewList.size();
           // return Integer.MAX_VALUE;
        }

而後將以前設置的默認的第一項:

private void initAdapter() {
        int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size());
        //viewPager.setOffscreenPageLimit(imageViewList.size());
        viewPager.setAdapter(new MyAdapter());
        // 設置從中間的某個位置開始滑動,從而可以實現向左向右的循環滑動
        viewPager.setCurrentItem(firstPosition);
    }

改成:

private void initAdapter() {

        viewPager.setAdapter(new MyAdapter());
        // 設置從中間的某個位置開始滑動,從而可以實現向左向右的循環滑動
        viewPager.setCurrentItem(0);
    }

可能說了這麼多,看的都混了,在最後就貼一下完整的源碼吧(支持自動播放、手指控制,可是不支持左右無限循環)。

package com.qc.admin.mylunbotu;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

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

import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING;
import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;


public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {


    private ViewPager viewPager;
    private int[] imageResIds;
    private List<ImageView> imageViewList;
    private LinearLayout mPointsLayout;
    private String[] contentDescs;
    private int previousSelectedItem;
    private TextView textView;
    private Handler mHandler;
    boolean isAutoRun = true;
    boolean isActivityAlive = true;
    private int delayMillis = 2000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化視圖
        initViews();

        //初始化數據
        initData();

        //初始化適配器
        initAdapter();

        //開始自動播放
        startRun();

    }

    private void startRun() {
        mHandler = new Handler();
        mHandler.postDelayed(mTaskRunnable, delayMillis);

    }

    //該線程一直運行着,知道activity被銷燬,此時將isActivityAlive設置爲false
    final Runnable mTaskRunnable = new Runnable() {
        @Override
        public void run() {
            // 若是activity未被銷燬,就一直執行該線程
            // 在ViewPager的OnPageChangeListener方法中決定是否將isAutoRun置反
            if (isActivityAlive) {
                if (isAutoRun) {
                    viewPager.setCurrentItem((viewPager.getCurrentItem() + 1) % imageViewList.size());
                    mHandler.postDelayed(mTaskRunnable, delayMillis);
                } else {
                    mHandler.postDelayed(mTaskRunnable, delayMillis);
                }
            }
        }
    };

    private void initAdapter() {
        //int firstPosition = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size());
        //viewPager.setOffscreenPageLimit(imageViewList.size());
        viewPager.setAdapter(new MyAdapter());
        // 設置從中間的某個位置開始滑動,從而可以實現向左向右的循環滑動
        //viewPager.setCurrentItem(firstPosition);
        viewPager.setCurrentItem(0);
    }

    private void initData() {

        //圖片資源id數組
        imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};
        // 文本描述
        contentDescs = new String[]{
                "鞏俐不低俗,我就不能低俗",
                "撲樹又回來啦!再唱經典老歌引萬人大合唱",
                "揭祕北京電影如何升級",
                "樂視網TV版大派送",
                "熱血屌絲的反殺"
        };
        imageViewList = new ArrayList<>();
        LinearLayout.LayoutParams mParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        mParams.leftMargin = 15;
        mParams.topMargin = 2;

        ImageView imageView;
        ImageView pointView;
        //初始化要展現的ImageView,並添加圓點指示器
        for (int i = 0; i < imageResIds.length; i++) {
            // 初始化圖片
            imageView = new ImageView(this);
            imageView.setBackgroundResource(imageResIds[i]);
            imageViewList.add(imageView);

            // 初始化指示器
            pointView = new ImageView(this);
            pointView.setBackgroundResource(R.drawable.point_bg);
            pointView.setLayoutParams(mParams);
            if (i == 0) {
                textView.setText(contentDescs[0]);
                pointView.setEnabled(true);
            } else {
                pointView.setEnabled(false);
            }
            mPointsLayout.addView(pointView);
        }
    }

    private void initViews() {

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        viewPager.addOnPageChangeListener(this);

        textView = (TextView) findViewById(R.id.tv_desc);
        // 用來添加圓點指示器
        mPointsLayout = (LinearLayout) findViewById(R.id.point_container);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        int newPosition = position % imageViewList.size();
        textView.setText(contentDescs[newPosition]);

        // 先將上一個置位false,將當前位置置位true,這樣可使得初始化的時候就在第一個位置
        // (由於previousSelectedItem的未賦值時候的初始值默認爲0)
        mPointsLayout.getChildAt(previousSelectedItem).setEnabled(false);
        mPointsLayout.getChildAt(newPosition).setEnabled(true);
        previousSelectedItem = newPosition;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        switch (state) {
            // 靜止狀態
            case SCROLL_STATE_IDLE:
                isAutoRun = true;
                break;
            // 拖拽中
            case SCROLL_STATE_DRAGGING:
                isAutoRun = false;
                break;
            // 拖拽後鬆手,自動回到最終位置的過程
            case SCROLL_STATE_SETTLING:
                isAutoRun = true;
                break;
        }
    }

    // 建立一個MyAdapter類,繼承自PagerAdapter來爲ViewPager設置適配器
    class MyAdapter extends PagerAdapter {

        // 1.返回條目的總數
        @Override
        public int getCount() {

            return imageViewList.size();
//            return Integer.MAX_VALUE;
        }

        // 2.返回要顯示的條目,並建立條目
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //container:容器,其實也就是ViewPager
            //position:當前要顯示的條目的位置

            int newPosition = position % imageViewList.size();
            ImageView imageView = imageViewList.get(newPosition);
            //a.將View對象添加到container容器中
            container.addView(imageView);
            //b.把View對象返回給框架,適配器
            return imageView;
        }

        // 3.銷燬條目,其實就是將要銷燬的對象object從container中移除出去就行了
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        // 4.指定複用的判斷邏輯(通常爲固定寫法)
        @Override
        public boolean isViewFromObject(View view, Object object) {
            // 當滑動到新的條目以後,又返回回來,view是否能夠被複用
            return view == object;
        }
    }

    @Override
    protected void onDestroy() {
        isActivityAlive = false;
        super.onDestroy();
    }
}
相關文章
相關標籤/搜索