Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義View實現廣告位輪播圖barner組件

自定義View實現廣告位輪播圖barner組件

編輯:關於Android編程

閒談

最近公司事情不算太多,閒來無事,看到項目中用到的廣告輪播圖,之前都是使用第三方的,趁事情不算多,所以自己實現一個廣告位輪播圖barner組件,這樣的話,在以後的開發中就可以使用自己的了。

知識點

好了,切入正題!我們要想實現barner組件,首先要求我們需要哪些知識點呢?
1、自定義View的流程(測量、布局、繪制)
2、廣告位輪播圖滑動的時候,我們需要彈性滑動Scroller
3、自定義View的事件傳遞機制
4、在我們自定義View事件傳遞給我們自定義的View的時候,我們在OnTouch方法中轉移給手勢探測器GestureDetector
5、廣告位輪播圖的自動輪播需要用到的定時器,我使用的是Timer+TimerTask+Handler
6、自定義View移動過程需要的ScrollTo和ScrollBy
首先使用我在定義的barner組件只需要幾行代碼如下:

final LGYBarnerFrameLayout frameLayout = new LGYBarnerFrameLayout(this,ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,mBarnerModels);
frameLayout.setmOutBarnerClickLisenter(this);
frameLayout.setmDotPostionDirection(DotPostion.center.getmPostion());
frameLayout.setmDuration(1500);
frameLayout.setIsShowDotLayout(true);

其中mBarnerModels 代表的是集合ArrayList,從而我們需要知道LGYBarnerModel只是一個自定義model類。

package com.lgy.lgyutils.banner;
import android.graphics.Bitmap;
import android.widget.ImageView;
import java.io.Serializable;
/**
 * 自定義手機輪播圖model類
 */
public class LGYBarnerModel implements Serializable{
    private String mHttpURL;//鏈接http
    private Bitmap mBitmap;
    public LGYBarnerModel(String mHttpURL, Bitmap mBitmap) {
        this.mHttpURL = mHttpURL;
        this.mBitmap = mBitmap;
    }
    public String getmHttpURL() {
        return mHttpURL;
    }
    public void setmHttpURL(String mHttpURL) {
        this.mHttpURL = mHttpURL;
    }
    public Bitmap getmBitmap() {
        return mBitmap;
    }
    public void setmBitmap(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }
}

這個類存在的價值就是:我們在點擊其中的一個圖片的時候,具體的行為在此model中封裝,目前我只是封裝了http,如果需要跳轉Activity,使用者可以繼承此類繼續拓展。

實現功能

在我自定義的barner組件實現的功能如下:
1、輪播圖手動滑動
2、輪播圖自動滑動
3、輪播圖點擊事件
至於輪播圖圖片的優化,我的想法就是放在輪播圖之外處理,在LGYBarnerModel 中mBitmap 此時已經是優化好的對象。所以沒有加入圖片的三級緩存(內存、文件、網絡).

思路

下面我就寫寫我的思路:從輪播圖中,我很自然的想到了,輪播圖的實現是由2部分實現:圖片的切換和底部圓點切換。圖片的切換來通知底部圓點的切換。那麼這2部分我們可以用FrameLayout布局來包括。這樣子大致構架就出來了。
好了,我們介紹圖片切換。對於圖片切換布局是由多張圖片橫向連接在一起,並且每張圖片的寬度就是我們切換布局的寬度。
我們自定義圖片切換布局ViewGroup如下:我們核心類

package com.lgy.lgyutils.banner;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import java.util.Timer;
import java.util.TimerTask;
/**
 * 自定義手機輪播圖ViewGroup類
 */
public class LGYBarnerViewGroup extends ViewGroup {
    public static final String TAG = LGYBarnerViewGroup.class.getName();
    private Scroller mScroller;//彈性對象
    private GestureDetector mGestureDetector;//手勢探測對象
    private int duration = 1500;//彈性滑動默認時間
    private int mIndex = 0;//索引第幾張圖片
    private int mChildrenWidth = 0;//每張圖片寬度
    private int mChildrenSize = 0;//總共有多少張圖片
    private LGYBarnerLisener mLGYBarnerLisener;//監聽
    //自動旋轉
    private boolean mIsAuto = true;
    private Timer mTimer = new Timer();
    private TimerTask mTask ;
    private Handler mBitmapHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    int i = (int) msg.obj;
                    if (i >= mChildrenSize - 1 || i < 0){//自動滑動第一張或者最後一張
                        mIndex = 0;
                    }else {
                        mIndex++;
                    }
                    int dx = mIndex * mChildrenWidth ;
                    mScroller.startScroll(dx, 0, 0, 0, duration);//滑動
                    invalidate();
                    mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);//通知底部圓點切換
                    break;
            }
        }
    };   
    private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }
        @Override
        public boolean onSingleTapUp(MotionEvent e) {//單擊
            mLGYBarnerLisener.onBarnerPagerClick(mIndex);
            return true;
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {//雙擊
            mLGYBarnerLisener.onBarnerPagerClick(mIndex);
            return true;
        }
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            Log.i(TAG,"onScroll");
            scrollBy((int) distanceX,0);//view移動
            boolean leftToRight = distanceX > 0 ? true : false;
            if (leftToRight && mIndex == 0){
                return false;
            }else if (!leftToRight && (mIndex == mChildrenSize - 1)){
                return false;
            }else {
                return true;
            }
        }
        @Override
        public boolean onFling(MotionEvent lastEvent, MotionEvent nowEvent, float velocityX, float velocityY) {
            Log.i(TAG,"onFling");
            int scrollX = getScrollX();
            boolean leftToRight = velocityX > 0 ? true : false;//true從左到右滑動
            if (leftToRight){//true從左到右滑動
                mIndex = (mIndex <= 0)?0:mIndex - 1;
            }else {
                mIndex = (mIndex >= mChildrenSize - 1)?mChildrenSize - 1 : mIndex + 1;
            }
            int dx = mIndex * mChildrenWidth - scrollX;
            smoothScroller(dx);
            mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);
            return true;
        }
    };
    public LGYBarnerViewGroup(Context context) {
        super(context, null);
        init();
    }
    private void init() {
        if (null == mScroller) {
            mGestureDetector = new GestureDetector(getContext(),mGestureListener);
            mScroller = new Scroller(getContext());
        }
        if (null == mTask) {
            mTask = new TimerTask() {
                @Override
                public void run() {
                    if (mIsAuto) {
                        Message msg = Message.obtain(mBitmapHandler, 0);
                        msg.obj = mIndex;
                        mBitmapHandler.sendMessage(msg);
                    }
                }
            };
        }
        mTimer.schedule(mTask, duration * 2, duration * 2);
    }
    public void setDuration(int duration) {
        mIsAuto = false;
        this.duration = duration;
        mIsAuto = true;
    }
    private void smoothScroller(int dx){
        if (null != mScroller){
            mScroller.startScroll(getScrollX(), 0, dx, 0, duration);
            invalidate();
        }
    }
    @Override
    protected void onDetachedFromWindow() {
        if (null != mScroller){
            mScroller.abortAnimation();
        }
        mIsAuto = false;
        mTimer.cancel();
        mTimer = null;
        mTask.cancel();
        mTask = null;
        Log.i(TAG,"onDetachedFromWindow");
        super.onDetachedFromWindow();
    }
    @Override
    public void computeScroll() {
        if (null != mScroller){
            if (mScroller.computeScrollOffset()){
                scrollTo(mScroller.getCurrX(),0);
                postInvalidate();
            }
        }
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN){
            mIsAuto = false;
            if (!mScroller.isFinished()){
                mScroller.abortAnimation();
            }
        }else if (action == MotionEvent.ACTION_UP){
            mIsAuto = true;
        }
        boolean customTouch = mGestureDetector.onTouchEvent(event);

        if (customTouch == false && action == MotionEvent.ACTION_UP){
            mIndex = (getScrollX() + mChildrenWidth / 2) / mChildrenWidth;
            if (mIndex <= 0){
                mIndex = 0;
            }else if (mIndex >= mChildrenSize - 1){
                mIndex = mChildrenSize - 1;
            }
            int dx = mIndex * mChildrenWidth - getScrollX();
            smoothScroller(dx);
            mLGYBarnerLisener.onBarnerScrollToIndex(mIndex);
        }
        return customTouch;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int childrenCount = getChildCount();
        if (childrenCount == 0) {
            setMeasuredDimension(0, 0);
        } else {
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            View firstView = getChildAt(0);
            int measureWidth = firstView.getMeasuredWidth() * childrenCount;
            setMeasuredDimension(measureWidth, heightSize);
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childrenHeight = getMeasuredHeight();
            int childrenLeft = 0;
            final int childrenCount = getChildCount();
            mChildrenSize = childrenCount;
            for (int i = 0; i < childrenCount; i++) {
                View temptView = getChildAt(i);
                mChildrenWidth = temptView.getMeasuredWidth();
                temptView.layout(childrenLeft, 0, mChildrenWidth + childrenLeft, childrenHeight);
                childrenLeft += mChildrenWidth;
            }
        }
    }
    public LGYBarnerLisener getmLGYBarnerLisener() {
        return mLGYBarnerLisener;
    }
    public void setmLGYBarnerLisener(LGYBarnerLisener mLGYBarnerLisener) {
        this.mLGYBarnerLisener = mLGYBarnerLisener;
    }
    public interface LGYBarnerLisener {
        /**
         * 輪播圖移動小點索引
         * @param index
         */
        void onBarnerScrollToIndex(int index);
        /**
         * 輪播圖點擊事件
         * @param index
         */
        void onBarnerPagerClick(int index);
    }
}

這是我們的核心類,要想明白以上代碼,前面提到的知識點要掌握,如果掌握的話,我想閱讀以上代碼不成問題。
接下來我們介紹底部圓點切換布局類,此類是和FrameLayout一起實現的如下:

package com.lgy.lgyutils.banner;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.lgy.lgyutils.C;
import com.lgy.lgyutils.R;
import java.util.ArrayList;
public class LGYBarnerFrameLayout extends FrameLayout implements LGYBarnerViewGroup.LGYBarnerLisener{
    public static final String TAG = "LGYBarnerFrameLayout";
    private static final int MDEFAULTHEIGHT = 200;
    private static final int MDOTHEIGHT = 40;
    private LGYBarnerViewGroup mLGYBarnerViewGroup;
    private LinearLayout mDotLineayLayout;
    private int mIndex = 0;//滑動索引
    private int mWidth;////布局寬度
    private int mHeight;//布局高度
    private int mDotPostionDirection = DotPostion.center.getmPostion();//遠點的位置
    private OutBarnerClickInterface mOutBarnerClickLisenter;
    private ArrayList mBarnerModels = new ArrayList<>();
    public LGYBarnerFrameLayout(Context context,int width,int height,ArrayList barnerModels){
        super(context);
        this.mBarnerModels = barnerModels;
        dealWidthHeight(width,height);
        initChildren();
    }
    public void setmDuration(int mDuration) {
        mLGYBarnerViewGroup.setDuration(mDuration);
    }
    public void setIsShowDotLayout(boolean isShow){
        if (isShow){
            mDotLineayLayout.setVisibility(VISIBLE);
        }else {
            mDotLineayLayout.setVisibility(GONE);
        }
    }
    public void setmDotPostionDirection(int mDotPostionDirection) {
        if (mDotPostionDirection == DotPostion.left.getmPostion()
                || mDotPostionDirection == DotPostion.center.getmPostion()
                || mDotPostionDirection == DotPostion.right.getmPostion()){
            this.mDotPostionDirection = mDotPostionDirection;
        }else {
            this.mDotPostionDirection = DotPostion.center.getmPostion();
        }
        mDotLineayLayout.setHorizontalGravity(this.mDotPostionDirection);
    }
    /**
     * ViewGroup.LayoutParams.MATCH_PARENT -1
     * ViewGroup.LayoutParams.WRAP_CONTENT -2
     * @param width
     * @param height
     */
    private void dealWidthHeight(int width,int height){
        if (width == ViewGroup.LayoutParams.WRAP_CONTENT || width == ViewGroup.LayoutParams.MATCH_PARENT){
            width = C.mWidth;//自己規定的不設置具體值,那麼我會按照當前屏幕寬度處理
        }
        if (height == ViewGroup.LayoutParams.WRAP_CONTENT || height == ViewGroup.LayoutParams.MATCH_PARENT){
            height = MDEFAULTHEIGHT;//自己規定的不設置具體值,那麼我會按照默認值處理
        }
        mWidth = width;
        mHeight = height;
        setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));//重設布局
    }
    private void initChildren(){
        mLGYBarnerViewGroup = new LGYBarnerViewGroup(getContext());
        mLGYBarnerViewGroup.setmLGYBarnerLisener(this);//設置底部圓點監聽
        mLGYBarnerViewGroup.setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));
        addViewGroupChildrenBitmap();
        this.addView(mLGYBarnerViewGroup);
        mDotLineayLayout = new LinearLayout(getContext());
        mDotLineayLayout.setBackgroundColor(Color.GRAY);
        mDotLineayLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, MDOTHEIGHT));
        mDotLineayLayout.setOrientation(LinearLayout.HORIZONTAL);
        mDotLineayLayout.setGravity(mDotPostionDirection);
        addDotViewChildren();
        this.addView(mDotLineayLayout);
        LayoutParams layoutParams = (LayoutParams) mDotLineayLayout.getLayoutParams();
        layoutParams.gravity = Gravity.BOTTOM;
        mDotLineayLayout.setLayoutParams(layoutParams);
        //設置透明度
        settingAlpha();
    }
    /**
     * 設置透明度
     */
    private void settingAlpha(){
        int sdkVersion = 0;
        try {
            sdkVersion = Integer.valueOf(Build.VERSION.SDK_INT);
            if (sdkVersion > Build.VERSION_CODES.HONEYCOMB){//3.0之後
                mDotLineayLayout.setAlpha(0.5f);
            }else {
                mDotLineayLayout.getBackground().setAlpha(100);
            }
        }catch (Exception e){
            Log.i(TAG,e.getLocalizedMessage());
        }
    }
    @Override
    public void onBarnerScrollToIndex(int index) {
        if (null != mDotLineayLayout) {
            mIndex = index;
            for (int i = 0; i < mBarnerModels.size(); i++) {
                LinearLayout dotImageLayout = (LinearLayout) mDotLineayLayout.getChildAt(i);
                if (i == index) {
                    dotImageLayout.setBackgroundResource(R.drawable.dot_forcus);
                } else {
                    dotImageLayout.setBackgroundResource(R.drawable.dot_normal);
                }
            }
        }
    }
    /**
     * 接受通知底部圓點,在這裡我們再通知外部使用者
     * 只是我們會封裝一下我們的LGYBarnerModel,供外部使用
     */
    @Override
    public void onBarnerPagerClick(int index) {
        LGYBarnerModel model = mBarnerModels.get(index);
        mOutBarnerClickLisenter.onBarnerPagerClick(model);
    }
    /**
     * 添加對應的點
     */
    private void addDotViewChildren(){
        for (int i = 0; i < mBarnerModels.size(); i++) {
            LinearLayout dotImageLayout = new LinearLayout(getContext());
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(MDOTHEIGHT / 2,MDOTHEIGHT / 2);
            layoutParams.setMargins(MDOTHEIGHT / 10, 0, MDOTHEIGHT / 10, 0);
            dotImageLayout.setLayoutParams(layoutParams);
            if (i == mIndex){
                dotImageLayout.setBackgroundResource(R.drawable.dot_forcus);
            }else {
                dotImageLayout.setBackgroundResource(R.drawable.dot_normal);
            }
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) dotImageLayout.getLayoutParams();
            lp.gravity = Gravity.CENTER_VERTICAL;
            dotImageLayout.setLayoutParams(lp);
            mDotLineayLayout.addView(dotImageLayout);
        }
    }
    /**
     * 添加ImageView到自定義的ViewGroup
     */
    private void addViewGroupChildrenBitmap(){
        for (LGYBarnerModel model : mBarnerModels){
            Bitmap bitmap = model.getmBitmap();
            ImageView tempView = new ImageView(getContext());
            tempView.setImageBitmap(bitmap);
            tempView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            tempView.setLayoutParams(new ViewGroup.LayoutParams(mWidth, mHeight));
            mLGYBarnerViewGroup.addView(tempView);
        }
    }
    @Override
    public void setLayoutParams(ViewGroup.LayoutParams params) {
        params.width = mWidth;
        params.height = mHeight;
        super.setLayoutParams(params);
    }
    public OutBarnerClickInterface getmOutBarnerClickLisenter() {
        return mOutBarnerClickLisenter;
    }
    public void setmOutBarnerClickLisenter(OutBarnerClickInterface mOutBarnerClickLisenter) {
        this.mOutBarnerClickLisenter = mOutBarnerClickLisenter;
    }
    /**
     * 對外部的接口
     */
    public interface OutBarnerClickInterface{
        void onBarnerPagerClick(LGYBarnerModel model);
    }
}

靜態圖(可以自動滑動)如下:
這裡寫圖片描述

自己封裝的不夠完美!但是整體功能已經實現了。如果有什麼問題建議,大家可以一起探討!謝謝

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved