自定义View实现广告位轮播图barner组件

闲谈

最近公司事情不算太多,闲来无事,看到项目中用到的广告轮播图,之前都是使用第三方的,趁事情不算多,所以自己实现一个广告位轮播图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<LGYBarnerModel> 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);
    }
}

静态图(可以自动滑动)如下:
自定义View实现广告位轮播图barner组件_第1张图片

自己封装的不够完美!但是整体功能已经实现了。如果有什么问题建议,大家可以一起探讨!谢谢

你可能感兴趣的:(Android知识点总结,自定义View)