1. 程式人生 > >自定義Banner輪播廣告(真*無限迴圈無卡頓&設定切換速度)

自定義Banner輪播廣告(真*無限迴圈無卡頓&設定切換速度)

前言

    Github上有很多輪播廣告的原始碼,比如帶著很酷炫動畫的flashView框架。
    不過就學習而已,我建議每個人都應該自己多嘗試著寫一些控制元件。
    以下,是我為小白們分享的簡單經驗。

自定義控制元件

    先展示下效果圖。!由於不會做gif圖,只能粗略的展示效果,看官見諒
    ![這裡寫圖片描述](https://img-blog.csdn.net/20170225165425535?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFiZW5kYW4wNzE0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
    **簡單說下思路,就是由一個展示圖片的viewPager和右下角的三個radioButton組成的自定義組合控制元件。**

送上第一道菜——佈局程式碼

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height
="match_parent">
<android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> <RadioGroup android:id="@+id/radio_group" android:layout_alignParentBottom
="true" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginRight="15dp" android:layout_marginBottom="15dp" >
<RadioButton android:id="@+id/rb1" android:layout_width="7dp" android:layout_height="8dp" android:button="@null" android:background="@drawable/circle_advertisement" android:clickable="false" /> <RadioButton android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:id="@+id/rb2" android:layout_width="7dp" android:layout_height="8dp" android:button="@null" android:background="@drawable/circle_advertisement" android:clickable="false" /> <RadioButton android:id="@+id/rb3" android:layout_width="7dp" android:layout_height="8dp" android:button="@null" android:background="@drawable/circle_advertisement" android:clickable="false" /> </RadioGroup> </RelativeLayout>
簡單說一說自定義組合控制元件的使用,其實很簡單。
1.讓你的控制元件繼承一個佈局(LinerLayout or RelativeLayout)
public class YAdvertisementLayout extends LinearLayout
2.重寫構造方法,新增你要進行的操作(構造方法有4個,分別對應不同構造方式),
儘量在每個構造方法下都要寫你初始化的操作。
 public YAdvertisementLayout(Context context) {
        super(context);
        initView(context);
    }

    public YAdvertisementLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public YAdvertisementLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }
3.初始化佈局,組合控制元件就加上如下程式碼,然後就可以對你的控制元件為所欲為了
View v = inflate(context, R.layout.widget_advertisement, this);
    接下來的步驟,就是正常findViewById。初始化資料來源,對pager進行設定廣告圖片。

進入今天的重頭戲,優化Banner圖

1.如何使你的輪播廣告無限迴圈
2. 手動設定viewPager切換速度

第一部分:viewPager無限迴圈

    這個部分得先從viewPager的介面卡PagerAdapter講起
    簡單介紹下PagerAdapter的各個方法
public int getCount();//獲取viewPager資料來源總個數

 public boolean isViewFromObject(View view, Object object);//這個方法是判斷物件是否相同,相同直接複用,所以一般都是return view == object;

//還有兩個方法是要自己重寫的
 public void destroyItem(ViewGroup container, int position, Object object);//顧名思義,移除物件的操作container.removeView(list.get(position));

 public Object instantiateItem(ViewGroup container, int position);//重點方法,在此方法進行初始化操作,設定圖片,設定點選時間等等。
舉個栗子
//list是資料來源
public class MyAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
//            container.removeView(list.get(position%list.size()));
            container.removeView(list.get(position));
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(list.get(position));
              list.get(position).setImageResource(imgs.get(position));
            list.get(position).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, "我被點選了", Toast.LENGTH_SHORT).show();
                }
            });
            return list.get(position);
        }
    }
這個就是一般流程。之前有一種無限迴圈的方式,其實個人認為又low又搞笑。
 在getCount()方法中return Integer.MAX_VALUE;
 相當於建立無數物件,在初始化物件時container.addView(list.get(position%list.size()));
如此一來,反覆建立物件,缺點顯而易見。



那麼問題來了,如何能優雅的裝逼...
**上程式碼!**
/**新增首末圖,末圖與首圖相同,首圖為末圖*/
        imgs.add(R.drawable.match_banner);
        imgs.add(R.drawable.advertisement_2);
        imgs.add(R.drawable.advertisement_3);

        imgs.add(0,R.drawable.advertisement_3);
        imgs.add(list.size()-1.drawable.match_banner);
相信看到這裡,你應該有了一些想法。
假設三張動圖,我們要支援無限滑動,那就設定3+2資料來源,編號0| 123 |4(123為展示圖,4圖與1圖相同,
這樣做是為了讓viewPager支援左右滑動,並且欺騙使用者的眼睛)在滑動到4時,
我們進行一個無動畫切換mPager.setCurrentItem(1,false);同理滑動到0時無動畫切換3.mPager.setCurrentItem(3,false)

註釋:在滑動到資料來源末端時,viewPager是不支援繼續滑動的


原理就是這麼簡單,但是,僅僅這樣會有一個bug,是在首末頁切換時,會出現,動畫並不夠平滑,有一種卡頓的感覺。
解決方法:
mPager.addOnPageChangeListener(this);
//在這個方法上做文章,positionOffset這是偏移比例,positionOffsetPixels這是偏移畫素
@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        //原理就是在視覺上滑動完全結束了,進行無動畫跳轉,如此一來切換過程就沒有卡頓效果 
        if (position == 0 && positionOffset == 0)
            mPager .setCurrentItem(list.size() - 2, false);
        else if (position == list.size() - 1 && positionOffset == 0)
            mPager .setCurrentItem(1, false);
    }
這也是為什麼不在
public void onPageSelected(int position) ;方法上進行跳轉的原因

第二部分:手動設定viewPager切換速度

    這個部分設計到反射,修改原始碼設定時間。本人就不丟人現眼了,直接上原始碼
//新增工具類 
import java.lang.reflect.Field;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.view.animation.Interpolator;
import android.widget.Scroller;

/**
 * ViewPager 滾動速度設定 
 *
 * @author lyy
 *
 */
public class ViewPagerScroller extends Scroller {
    private int mScrollDuration = 2000; // 滑動速度  

    /**
     * 設定速度速度 
     *
     * @param duration
     */
    public void setScrollDuration(int duration) {
        this.mScrollDuration = duration;
    }

    public ViewPagerScroller(Context context) {
        super(context);
    }

    public ViewPagerScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    public ViewPagerScroller(Context context, Interpolator interpolator,
                             boolean flywheel) {
        super(context, interpolator, flywheel);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        super.startScroll(startX, startY, dx, dy, mScrollDuration);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        super.startScroll(startX, startY, dx, dy, mScrollDuration);
    }

    public void initViewPagerScroll(ViewPager viewPager) {
        try {
            Field mScroller = ViewPager.class.getDeclaredField("mScroller");
            mScroller.setAccessible(true);
            mScroller.set(viewPager, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}  


//使用方式
private void fixSpeed() {
        ViewPagerScroller pagerScroller = new ViewPagerScroller(mPager.getContext());
        pagerScroller.setScrollDuration(1000*1);//設定時間,時間越長,速度越慢
        pagerScroller.initViewPagerScroll(mPager);
    }

原始碼

佈局檔案,文章開始已經給出了主要佈局,下面附上radioButton圖片
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/circle_advertisement_normal" android:state_checked="false"/>
    <item android:drawable="@drawable/circle_advertisement_checked" android:state_checked="true"/>
</selector>

<?xml version="1.0" encoding="utf-8"?>

<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    android:useLevel="false">
    <solid android:color="#ffffff"/>
    <size android:width="10dp"
    android:height="10dp"/>
</shape>

<?xml version="1.0" encoding="utf-8"?>

<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    android:useLevel="false">
    <solid android:color="#80ffffff"/>
    <size android:width="10dp"
        android:height="10dp"/>
</shape>
控制元件原始碼
package app.ui.widget;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import com.wy.sport.R;

import java.util.ArrayList;
import java.util.List;
import android.os.Handler;
/**
 * Created by Yangmu on 2017/2/21.
 */

public class YAdvertisementLayout extends LinearLayout implements ViewPager.OnPageChangeListener {

    private List<ImageView> list = new ArrayList<>();
    private List<Integer> imgs = new ArrayList<>();
    private Context context;
    private ViewPager mPager;
    private RadioButton mRb1;
    private RadioButton mRb2;
    private RadioButton mRb3;
    private RadioGroup mRadioGroup;
    private static final String TAG = "ym";
    private Handler handler;
    private Runnable r;
    private MyAdapter adpter;

    public YAdvertisementLayout(Context context) {
        super(context);
        initView(context);
    }

    public YAdvertisementLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public YAdvertisementLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        this.context = context;

        View v = inflate(context, R.layout.widget_advertisement, this);
        mPager = (ViewPager) v.findViewById(R.id.pager);
        mRadioGroup = (RadioGroup) v.findViewById(R.id.radio_group);
        mRb1 = (RadioButton) v.findViewById(R.id.rb1);
        mRb2 = (RadioButton) v.findViewById(R.id.rb2);
        mRb3 = (RadioButton) v.findViewById(R.id.rb3);

        initPager();

        mPager.addOnPageChangeListener(this);

        mPager.setCurrentItem(1);

//        start();
        fixSpeed();
    }
    /**修改動畫播放速度*/
    private void fixSpeed() {
        ViewPagerScroller pagerScroller = new ViewPagerScroller(mPager.getContext());
        pagerScroller.setScrollDuration(1000*1);//設定時間,時間越長,速度越慢
        pagerScroller.initViewPagerScroll(mPager);
    }

//開啟自動跳轉廣告功能
    public void start() {
        handler = new Handler();
        r = new Runnable() {
            @Override
            public void run() {
                mPager.setCurrentItem(mPager.getCurrentItem()+1);
//                handler.postDelayed(r,1000*3);
            }
        };
        handler.postDelayed(r,1000*4);
    }

    private void initPager() {
        for (int i = 0; i < 5; i++) {
            ImageView iv1 = new ImageView(context);
            iv1.setScaleType(ImageView.ScaleType.FIT_XY);
            list.add(iv1);
        }
        Log.e(TAG, "initPager: "+list.size() );
        //
        /**新增首末圖,末圖與首圖相同,首圖為末圖*/
        imgs.add(R.drawable.advertisement_3);
        imgs.add(R.drawable.match_banner);
        imgs.add(R.drawable.advertisement_2);
        imgs.add(R.drawable.advertisement_3);
        imgs.add(R.drawable.match_banner);

        adpter = new MyAdapter();
        mPager.setAdapter(adpter);
        mRadioGroup.check(mRb1.getId());
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        Log.e(TAG, "onPageScrolled: position"+position+"  positionOffset"+positionOffset+"   positionOffsetPixels"+positionOffsetPixels );
        if (position == 0 && positionOffset == 0)
            mPager .setCurrentItem(list.size() - 2, false);
        else if (position == list.size() - 1 && positionOffset == 0)
            mPager .setCurrentItem(1, false);
    }


    @Override
    public void onPageSelected(int position) {
        if (list.size() > 3) { //多於1,才會迴圈跳轉
            switch (position) {
                case 0:
                    mRadioGroup.check(mRb3.getId());

                case 1:
                    mRadioGroup.check(mRb1.getId());
                    break;

                case 2:
                    mRadioGroup.check(mRb2.getId());
                    break;

                case 3:
                    mRadioGroup.check(mRb3.getId());
                    break;
                case 4:
                    mRadioGroup.check(mRb1.getId());
            }
        }else {
            mPager.setCurrentItem(1, false);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        switch(state){
            case 1:  //滑動狀態
                handler.removeCallbacks(r);
              break;

            case 2://滑動結束狀態
                handler.postDelayed(r,1000*4);
                break;

            default:

              break;
        }
    }


    public class MyAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
//            container.removeView(list.get(position%list.size()));
            container.removeView(list.get(position));
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(list.get(position));
            Log.e(TAG, "instantiateItem: position"+position );
            list.get(position).setImageResource(imgs.get(position));
            list.get(position).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
//                    Toast.makeText(context, "呵呵", Toast.LENGTH_SHORT).show();
                }
            });
            return list.get(position);
        }
    }
    /**對外借口,通過此方法修改圖片*/
    public void changerImages(int imgs[]){
        this.imgs.clear();
        for (int i = 0; i < imgs.length; i++) {
            this.imgs.add(imgs[i]);
        }
        this.imgs.add(0,imgs[imgs.length-1]);
        this.imgs.add(this.imgs.size()-1,imgs[0]);
        Log.e(TAG, "changerImages: 更新完畢" );
        adpter.notifyDataSetChanged();
    }

}