自定义ViewGroup实现轮播图

作为一名Android开发者对于自定义View的掌握体现利润他对控件的深入理解,我下面是自定义ViewGroup实现轮播图;

效果图

轮播图效果图

效果图就是这样,自动轮播,可对每张图片进行点击监听,接下来我们先看一下自定义ViewGroup实现实现的代码:

public class ImageBannerViewGroup extends ViewGroup {
    private int lastX; //滑动的上一次位置
    private int childerCount; //子控件的个数
    private int width; //屏幕的宽度
    private int height; //Banner图的高度
    private int index = 0; //Banner图索引
    private int startX; //滑动的起始位置
    private Timer timer;
    private List<Integer> banners;
    private Boolean isClick = true; //是否点击
    private Boolean isAuto = true; //是否自动轮播
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                if (++index >= childerCount) {
                    index = 0;
                }
                if (bannerDotListener != null) {
                    bannerDotListener.bannerDotListener(index);
                }
                scrollTo(index * width, 0);
            }
        }
    };

    public void setAuto(Boolean isAuto) {
        this.isAuto = isAuto;
    }

    public ImageBannerViewGroup(Context context) {
        this(context, null);
    }

    public ImageBannerViewGroup(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageBannerViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isAuto) {
                    mHandler.sendEmptyMessage(0);

                }
            }
        }, 1000, 3000);
    }

    //测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        childerCount = getChildCount();
        if (childerCount == 0) {
            setMeasuredDimension(0, 0);
        } else {
            measureChild(getChildAt(0), widthMeasureSpec, heightMeasureSpec);
            DisplayMetrics dm = new DisplayMetrics();
            ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
            width = dm.widthPixels;
            height = dm.heightPixels / 3;//轮播图为手机全屏高度的三分之一
            setMeasuredDimension(width * childerCount, height);

        }

    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int leftMargin = 0;
            for (int i = 0; i < childerCount; i++) {
                View view = getChildAt(i);
                view.layout(leftMargin, 0, leftMargin + width, height);
                leftMargin += width;
            }

        }


    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                isClick = true;
                isAuto = false;
                startX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                isClick = false;
                lastX = (int) event.getX();
                int offsetX = startX - lastX;
                if (index == 0 && offsetX < 0) {
                    return true;
                } else if (index == childerCount - 1 && offsetX > 0) {
                    return true;
                }

                scrollBy(offsetX, 0);
                startX = lastX;
                break;
            case MotionEvent.ACTION_UP:
                index = (getScrollX() + width / 2) / width;
                Log.e("点击", "onTouchEvent:click " + isClick);

                if (isClick && bannerImagClickListener != null) {
                    bannerImagClickListener.bannerClick(index);
                    Log.e("点击", "onTouchEvent:click " + index);
                } else {

                    if (index < 0) {
                        index = 0;

                    } else if (index == childerCount - 1) {

                        index = childerCount - 1;
                    }

                    if (bannerDotListener != null) {
                        bannerDotListener.bannerDotListener(index);
                    }
                    scrollTo(index * width, 0);
                    isAuto = true;
                }
                break;

        }
        return true;


    }

    /**
     * banner图点击事件回调接口
     */
    public interface BannerImagClickListener {
        void bannerClick(int pos);
    }

    private BannerImagClickListener bannerImagClickListener;

    public void setBannerImagClickListener(BannerImagClickListener bannerImagClickListener) {
        this.bannerImagClickListener = bannerImagClickListener;
    }

    /**
     * banner图的dot点的滑动监听
     */
    public interface BannerDotListener {
        void bannerDotListener(int pos);
    }

    private BannerDotListener bannerDotListener;

    public void setBannerDotListener(BannerDotListener bannerDotListener) {
        this.bannerDotListener = bannerDotListener;
    }

}

虽然代码不多,但是逻辑要理清,对于自定义ViewGroup,分三步走:

  1. onMeasure()测量
  2. onLayout()布局
  3. onDraw()绘制
    这里我们只用了前两个对父布局和子布局进行了测量和,布置,具体看上面的代码,打了很多注释,多看应该很清楚,这里有个知识点就是ScrollerTo和ScrollerBy的使用规则,其实就是绝对滑动和相对滑动,在ViewGroup中滑动的是子布局,在其他子View中,比如TextView,实现滑动的话,将会是内部内容也就是TextView中的字体进行滑动,还有就是在OnTouchEvent中对触摸事件的判断逻辑,以及在ViewGroup中如何进行事件传递,就是责任链模式,这里我大概说一下ViewGroup中事件的传递,首先在Activity中接收到触摸事件,在ViewGroup中首先对调用自身的onInterceptTouchEvent事件,根据返回的Boolean值判断该事件是否阻断然后自身使用,如果返回False,将调用自身的dispatchTouchEvent来将事件分发给子View,如果是True,将会调用自身的OnTouchEvent来进行事件分类和处理。上面的代码实现了的轮播和点击,没有轮播图下面的索引点的切换,所以我继图片续自定义了一个ViewGroup继承FrameLayout来添加上面的ViewGroup和一个Linearalayout,在LinearLayout中添加索引的指示布局:
package com.gsww.www.scrollertest;

import android.content.Context;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

/**
 * Author : luweicheng on 2017/3/24 0024 16:46
 * E-mail :1769005961@qq.com
 * GitHub : https://github.com/luweicheng24
 */

public class BannerFrameLayout extends FrameLayout implements ImageBannerViewGroup.BannerDotListener {
    private ImageBannerViewGroup mImageBannerViewGroup;
    private LinearLayout mLinearLayout;

    public ImageBannerViewGroup getmImageBannerViewGroup() {
        return mImageBannerViewGroup;
    }

    public void setmImageBannerViewGroup(ImageBannerViewGroup mImageBannerViewGroup) {
        this.mImageBannerViewGroup = mImageBannerViewGroup;
    }

    public LinearLayout getmLinearLayout() {
        return mLinearLayout;
    }

    public void setmLinearLayout(LinearLayout mLinearLayout) {
        this.mLinearLayout = mLinearLayout;
    }

    private Integer[] banners;

    public BannerFrameLayout(@NonNull Context context) {
        this(context, null);
    }

    public BannerFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BannerFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDragView();
        initLinearLayout();
    }

    /**
     * 设置Banner图的集合
     *
     * @param banners
     * @param screenWidth  屏幕宽度
     * @param screenHeight 屏幕高度
     */
    public void addBanners(final Integer[] banners, int screenWidth, int screenHeight) {
        this.banners = banners;
        for (int i = 0; i < banners.length; i++) {
            ImageView img = new ImageView(getContext());
            img.setBackgroundResource(banners[i]);
            img.setId(i);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(screenWidth * banners.length, screenHeight / 4);
            img.setScaleType(ImageView.ScaleType.CENTER_CROP);
            img.setLayoutParams(lp);
            mImageBannerViewGroup.addView(img);
        }
        addDotToLinearLayout(banners.length);

    }

    private void addDotToLinearLayout(int dotCount) {
        for (int i = 0; i < dotCount; i++) {
            ImageView dot = new ImageView(getContext());
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            lp.setMargins(5, 0, 0, 0);
            dot.setBackgroundResource(R.drawable.dot_drawable);
            dot.setLayoutParams(lp);
            mLinearLayout.addView(dot);
        }

    }


    private void initLinearLayout() {
        mLinearLayout = new LinearLayout(getContext());
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
        lp.gravity = Gravity.BOTTOM;
        mLinearLayout.setLayoutParams(lp);
        mLinearLayout.setGravity(Gravity.CENTER);

        addView(mLinearLayout);

    }

    private void initDragView() {
        mImageBannerViewGroup = new ImageBannerViewGroup(getContext());
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mImageBannerViewGroup.setLayoutParams(lp);
        addView(mImageBannerViewGroup);
        mImageBannerViewGroup.setBannerDotListener(this);
    }

    @Override
    public void bannerDotListener(int pos) {
        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
            if (pos == i) {
                ImageView img = (ImageView) mLinearLayout.getChildAt(i);
                img.setBackgroundResource(R.drawable.dot_select);
            } else {
                ImageView img = (ImageView) mLinearLayout.getChildAt(i);
                img.setBackgroundResource(R.drawable.dot_no_select);

            }

        }
    }
}

在自定义FrameLayout中我初始化了图片轮播的ViewGroup和索引布局LinearLayout,在使用的时候如下布局和使用:

<?xml version="1.0" encoding="utf-8"?>
<com.gsww.www.scrollertest.BannerFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mBannerFrameLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</com.gsww.www.scrollertest.BannerFrameLayout>


package com.gsww.www.scrollertest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements ImageBannerViewGroup.BannerImagClickListener {
    private BannerFrameLayout banner;
    private Integer[] imgs = {R.drawable.girl_3, R.drawable.girl_5, R.drawable.girl_6,
            R.drawable.girl_4, R.drawable.girl_7, R.drawable.girl_9};
    private int screenWidth;
    private int screenHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.main);
        banner = (BannerFrameLayout) findViewById(R.id.mBannerFrameLayout);
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
        banner.addBanners(imgs, screenWidth, screenHeight);
        banner.getmImageBannerViewGroup().setBannerImagClickListener(this);

    }


    @Override
    public void bannerClick(int pos) {
        Toast.makeText(this, ""+pos, Toast.LENGTH_SHORT).show();
    }
}

好了,自定义轮播图就这样完成了,源码点击下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值