Android 焦點輪播圖的打造

作為一個剛工作的菜鳥释牺,連個焦點輪播圖都擼不出來没咙,真的是感覺沒臉見人,查閱多方資料涡驮,磕磕絆絆也算是做出來了捉捅,但由于不會寫寄月,大部分代碼也是拼湊起來的漾肮,代碼不美觀不說,復用起來也是極為的不易谭溉,尋思還是從頭整理一下,做個自定義View,日后復用旅挤,查看也更為順手粘茄。
說是自定義view,其實也只是組合view,將不同功能的view組合在一起磺平。

STEP.ONE 滑動展示圖片

通過ViewPager可以容易的實現(xiàn)可滑動的展示圖片的view擦酌。新建三個文件:

  • custom_banner.xml闸英,自定義View布局
  <?xml version="1.0" encoding="utf-8"?>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/vp_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
  </RelativeLayout>

布局文件遇伞,很簡單巍耗,隨著功能的完善,還會添加view

  • Banner.java,自定義view
  public class Banner extends RelativeLayout {
    private String TAG = "Banner";
    private View mView;
    private ViewPager mImage;

    private List<Integer> mImageList = new ArrayList<>();
    public Banner(Context context, AttributeSet attrs) {
        super(context, attrs);
        mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
        init();
    }
    private void init() {
        mImage = mView.findViewById(R.id.vp_image);
        mImageList = new ArrayList<>();
        mImageList.add(R.mipmap.img_1);
        mImageList.add(R.mipmap.img_2);
        mImageList.add(R.mipmap.img_3);
        ImageAdapter adapter = new ImageAdapter(mImage,mImageList);
        mImage.setAdapter(adapter);
    }
}

public Banner(Context context, AttributeSet attrs)引入布局帘靡,init()中簡單的添加了幾個數(shù)據(jù)涩赢,通過adapter加載赃阀。

  • ImageAdapter.java,adapter
public class ImageAdapter extends PagerAdapter {
    private String TAG = "ImageAdapter";
    private ViewPager mImage;
    private List<Integer> mImageList = new ArrayList<>();

    private int DEFAULT_BANNER_SIZE;
    private int FAKE_BANNER_SIZE;
    public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
        this.mImage = mImage;
        this.mImageList = mImageImageList;
        this.DEFAULT_BANNER_SIZE = mImageImageList.size();
        this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        position %= DEFAULT_BANNER_SIZE;
        ImageView img = new ImageView(container.getContext());
        img.setImageResource(mImageList.get(position));
        container.addView(img);
        return img;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //super.destroyItem(container, position, object);
        container.removeView((View) object);
    }
}

instantiateItem()加載子view,定義了兩個變量DEFAULT_BANNER_SIZE和FAKE_BANNER_SIZE搪柑,在循環(huán)滑動時會用到聋丝。

到這里最簡單的ViewPager的用法了,這里還有一點需要注意工碾,ViewPager需要重寫onMeasure()方法弱睦,測量child的高度作為自身高度,否則會match_parent渊额,在banner.java中重寫onMeasure()况木。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int maxHeight = 0;
        for (int i = 0; i < mImage.getChildCount(); i++) {
            View child = mImage.getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            maxHeight = Math.max(maxHeight,child.getMeasuredHeight());
        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

測量child的高度,最大值作為父容器高度旬迹。

STEP.TWO 實現(xiàn)循環(huán)與自動播放

實現(xiàn)循環(huán)重寫PagerAdapter中的finishUpdate(),自動播放使用輕量級的定時器Timer火惊。

  • 循環(huán)。在ImageAdapter.java中重寫finishUpdate()奔垦。
public class ImageAdapter extends PagerAdapter {
    private String TAG = "ImageAdapter";
    private ViewPager mImage;
    private List<Integer> mImageList = new ArrayList<>();

    private int DEFAULT_BANNER_SIZE;
    private int FAKE_BANNER_SIZE;
    public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
        this.mImage = mImage;
        this.mImageList = mImageImageList;
        this.DEFAULT_BANNER_SIZE = mImageImageList.size();
        this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
    }

    @Override
    public int getCount() {
        return FAKE_BANNER_SIZE;
                      .
                      .
                      .
    @Override
    public void finishUpdate(ViewGroup container) {
        super.finishUpdate(container);
        int curPosition = mImage.getCurrentItem();
        if (0 == curPosition){
            //滑動到最左邊
            mImage.setCurrentItem(DEFAULT_BANNER_SIZE, false);
        }
        if ((FAKE_BANNER_SIZE -1) == curPosition){
            //滑動到最右邊
            mImage.setCurrentItem(FAKE_BANNER_SIZE - DEFAULT_BANNER_SIZE - 1, false);
        }
    }
}

DEFAULT_BANNER_SIZE是圖片列表的大小屹耐,F(xiàn)AKE_BANNER_SIZE是三倍圖片列表的小。
以代碼為例椿猎,目前我們的ViewPager有3屏(DEFAULT_BANNER_SIZE)惶岭,但是如果在PagerAdapter的getCount方法中我們返回9(FAKE_BANNER_SIZE),即告訴ViewPager我們有9屏鸵贬。那么如果當前位于第3屏俗他,由于對于ViewPager來說它認為有9屏,所以我們?nèi)匀豢梢韵蚝蠡瑒永啤.敾瑒拥降?屏時(右邊界)兆衅,立即將第6屏的view返回給他,這樣ViewPager依然可以向右滑動嗜浮。同理處于第1屏時(左邊界)羡亩,講第4屏的view返回給他,此時仍然可以左滑危融。這樣便實現(xiàn)了循環(huán)播放畏铆。如圖..............hahah


示例圖
  • 自動播放
public class Banner extends RelativeLayout {
    private int DEFAULT_BANNER_SIZE;
    private int FAKE_BANNER_SIZE;
    //{start update 實現(xiàn)自動播放
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    int position = (mImage.getCurrentItem() + 1) % DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
                    mImage.setCurrentItem(position, true);
                    Log.d(TAG, "handleMessage: "+position);
                    break;
                default:
                    break;
            }
        }
    };
    // end }

    private List<Integer> mImageList = new ArrayList<>();
    public Banner(Context context, AttributeSet attrs) {
        super(context, attrs);
        mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
        init();
        //調(diào)用
        startTimer();
    }
    //{start update
    private void startTimer() {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = 1;
                handler.sendMessage(message);
            }
        };
        timer.schedule(task,5000,5000);
    }
    //end }
}

使用Timer定義一個5秒執(zhí)行的任務,通過Headler異步執(zhí)行吉殃。

STEP.THREE 底部圓點

根據(jù)mImageList動態(tài)添加底部圓點辞居。

  • 修改custom_banner.xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/vp_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:id="@+id/ll_point"
        android:layout_centerHorizontal="true"
        android:layout_alignBottom="@+id/vp_image"
        android:layout_marginBottom="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" />
</RelativeLayout>

新增了LinearLayout布局楷怒,添加圓點的任務就交給他了。

  • 修改Banner.java
private void initPoint() {
        for (int i = 0; i < mImageList.size(); i++) {
            View view = new View(getContext());
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
            params.leftMargin = 10;
            params.rightMargin = 10;
            view.setLayoutParams(params);
            if (i == 0) {
                view.setBackgroundResource(R.drawable.shape_point_select);
            } else {
                view.setBackgroundResource(R.drawable.shape_point_default);
            }
            final int finalI = i;
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int item = finalI+DEFAULT_BANNER_SIZE;
                    mImage.setCurrentItem(item, true);
                }
            });
            mPoint.addView(view);
            mPointList.add(view);
        }
    }

在mImageList初始化后執(zhí)行該函數(shù)瓦灶,添加圓點鸠删,背景,和點擊事件贼陶。

  • 發(fā)生點擊事件后刃泡,修改圓點背景
mImage.addOnPageChangeListener(this);

 @Override
    public void onPageSelected(int position) {
        position %= DEFAULT_BANNER_SIZE;
        for (View view : mPointList) {
            view.setBackgroundResource(R.drawable.shape_point_default);
        }
        mPointList.get(position).setBackgroundResource(R.drawable.shape_point_select);
    }

添加監(jiān)聽,當某頁面被選中時碉怔,先遍歷列表烘贴,將所有圓點都設置為未選中狀態(tài),再將對應頁面的背景設置為選中背景撮胧。

END

到這里這個功能就已經(jīng)全部實現(xiàn)了桨踪,這也是我的第一篇技術文章,參考了挺多大大的博客趴樱,最后倒騰出來馒闷。文章感覺寫的干巴巴酪捡,一來我本人對有些也不算特別熟悉叁征,二來文章寫的少。堅持寫博客逛薇,到自己的文章干貨越來越多捺疼,越寫越寫流暢,自己的成長了多少也就很明確了永罚。當然也希望這篇文章可以幫助到大家啤呼。

參考鏈接

循環(huán)廣告位組件的實現(xiàn)
動態(tài)圓點輪播圖
解決ViewPager高度無法wrap_content問題

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市呢袱,隨后出現(xiàn)的幾起案子官扣,更是在濱河造成了極大的恐慌,老刑警劉巖羞福,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惕蹄,死亡現(xiàn)場離奇詭異,居然都是意外死亡治专,警方通過查閱死者的電腦和手機卖陵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來张峰,“玉大人泪蔫,你說我怎么就攤上這事〈” “怎么了撩荣?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵铣揉,是天一觀的道長。 經(jīng)常有香客問我餐曹,道長老速,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任凸主,我火速辦了婚禮橘券,結果婚禮上,老公的妹妹穿的比我還像新娘卿吐。我一直安慰自己旁舰,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布嗡官。 她就那樣靜靜地躺著箭窜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衍腥。 梳的紋絲不亂的頭發(fā)上磺樱,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音婆咸,去河邊找鬼竹捉。 笑死,一個胖子當著我的面吹牛尚骄,可吹牛的內(nèi)容都是我干的块差。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼倔丈,長吁一口氣:“原來是場噩夢啊……” “哼憨闰!你這毒婦竟也來了?” 一聲冷哼從身側響起需五,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤鹉动,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宏邮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泽示,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年蜀铲,在試婚紗的時候發(fā)現(xiàn)自己被綠了边琉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡记劝,死狀恐怖变姨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厌丑,我是刑警寧澤定欧,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布渔呵,位于F島的核電站,受9級特大地震影響砍鸠,放射性物質(zhì)發(fā)生泄漏扩氢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一爷辱、第九天 我趴在偏房一處隱蔽的房頂上張望录豺。 院中可真熱鬧,春花似錦饭弓、人聲如沸双饥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咏花。三九已至,卻和暖如春阀趴,著一層夾襖步出監(jiān)牢的瞬間昏翰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工刘急, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棚菊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓排霉,卻偏偏與公主長得像窍株,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子攻柠,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容