自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)

来源:互联网 发布:centos 7 伪静态 编辑:程序博客网 时间:2024/06/05 20:18

广告轮播图在现在的APP首页比较常见,主要的实现方式有两种,一种是通过ViewPager,一种是通过自定义ViewGroup。前者的实现方式比较简便,本篇文章讲的是第二种方法,有人说用ViewPager不是更方便吗,的确,但是我们通过自己定义ViewGroup,可以更深入了解ViewGroup内部的原理。用别人造的轮子确实方便,但有的时候拆开轮子看看,我们也许会学到更多。

效果图
这里写图片描述

主要的思路如下:
首先,轮播图可以理解为n张图片横向相连,并通过一个单张图片大小的的相框,一次移动一张图片的距离,我们可以通过一个ViewGroup容器来存放这n张图片,然后用Scroller类配合TimeTask和Handler来实现图片的滑动,至于小圆点,也就是一个横向存放圆点图片的LinearLayout布局,可以再继承一个FrameLayout类,把存放图片的ViewGroup和存放圆点的LinearLayout都放进去,并根据图片滑动的位置来设置圆点的切换。

这就需要对ViewGroup的测量过程(onMeasure),布局过程 (onLayout)和绘制过程(onDraw)以及onTouch点击事件的处理有所了解。

直接看代码
ViewGroup类:

package com.imagebanner;import android.content.Context;import android.graphics.Canvas;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;import java.util.Timer;import java.util.TimerTask;/** * 该类是实现图片轮播核心类 */public class ImageBannerViewGroup extends ViewGroup {    //子视图个数    private int children ;    //子视图宽度和高度    private int childWidth ;    private int childHeight ;    private int x ;    private int index = 0 ;    private Scroller scroller ;    //图片点击事件的监听器    private ImageBannerListner listner ;    //底部圆点切换的监听器    private ImageBannerViewGroupListener bannerViewGroupListener ;    public ImageBannerViewGroupListener getBannerViewGroupListener() {        return bannerViewGroupListener;    }    public void setBannerViewGroupListener(ImageBannerViewGroupListener bannerViewGroupListener) {        this.bannerViewGroupListener = bannerViewGroupListener;    }    //判断是点击事件还是移动事件的标识    private boolean isClick ;    public ImageBannerListner getListner() {        return listner;    }    public void setListner(ImageBannerListner listner) {        this.listner = listner;    }    public interface ImageBannerListner{        void clickImageIndex( int pos );    }    //判断是否自动轮播的标识    private boolean isAuto = true ;    //自动轮播    private Timer timer = new Timer();    private TimerTask task ;    private Handler autoHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch ( msg.what ){                case 0:                    //最后一张的时候返回第一张                    if( ++index >= children ){                        index = 0 ;                    }                    scrollTo( childWidth * index , 0 );                    //图片切换完毕后通知FrameLayout切换底部圆点                    bannerViewGroupListener.selectImage(index);                    break;            }        }    };    private void startAuto(){        isAuto = true ;    }    private void stopAuto(){        isAuto = false ;    }    private void init(){        scroller = new Scroller(getContext());        task = new TimerTask() {            @Override            public void run() {                if( isAuto ){                    autoHandler.sendEmptyMessage(0);                }            }        };        timer.schedule(task , 100 , 3000 );    }    @Override    public void computeScroll() {        super.computeScroll();        if( scroller.computeScrollOffset()){            scrollTo(index * childWidth, 0);            invalidate();        }    }    public ImageBannerViewGroup(Context context) {        super(context);        init();    }    public ImageBannerViewGroup(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public ImageBannerViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //求出子视图的个数        children = getChildCount();        if( children == 0 ){            setMeasuredDimension(0,0);        }else{            //测量子视图的高度和宽度            measureChildren(widthMeasureSpec,heightMeasureSpec);            //根据子视图的宽度和高度 , 求出该ViewGroup的宽度和高度            View view = getChildAt(0);            childHeight = view.getMeasuredHeight() ;            childWidth = view.getMeasuredWidth() ;            //子视图的总宽度            int width = childWidth * children ;            setMeasuredDimension( width , childHeight );        }    }    /**     *     * @param change 布局位置发生改变时为true     * @param l  相对于父View的Left位置     * @param t  相对于父View的Top位置     * @param r  相对于父View的Right位置     * @param b  相对于父View的Bottom位置     */    @Override    protected void onLayout(boolean change, int l, int t, int r, int b) {        if( change ){            int leftMargin = 0 ;            for( int i = 0 ; i < children ; i ++ ){                View view = getChildAt(i);                view.layout(leftMargin, 0 , leftMargin + childWidth , childHeight );                leftMargin += childWidth ;            }        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);    }    /**     * 该方法返回true , ViewGroup会处理此次拦截事件     */    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return true ;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch ( event.getAction() ){            case MotionEvent.ACTION_DOWN:                isClick = true ;                 stopAuto();                if( !scroller.isFinished() ){                    scroller.abortAnimation();                }                x = (int) event.getX();                break ;            case MotionEvent.ACTION_MOVE:                int moveX = (int) event.getX();                int distance = moveX - x ;                scrollBy( -distance , 0 );                x = moveX ;                isClick = false ;                break ;            case MotionEvent.ACTION_UP:                int scrollX = getScrollX() ;                index = ( scrollX + childWidth/2 ) / childWidth ;                if( index < 0 ){                    index = 0 ;                }else if( index > children - 1 ){                    index = children - 1 ;                }                if( isClick ){                    //如果是点击事件                    listner.clickImageIndex(index);                }                else{                    int dx = index * children - scrollX ;                    scroller.startScroll(scrollX , 0 , dx , 0 );                    postInvalidate();                    bannerViewGroupListener.selectImage(index);                }                startAuto();            /*    scrollTo( index * childWidth , 0 );*/                break ;            default:                break ;        }        //返回true的目的是告诉该ViewGroup的父View已经处理该事件        return true ;    }    //    public interface ImageBannerViewGroupListener{        void selectImage( int index ) ;    }}

FrameLayout类

package com.imagebanner;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Color;import android.os.Build;import android.util.AttributeSet;import android.view.Gravity;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.LinearLayout;import java.util.List;/** * Created by Administrator on 2017/7/9. */public class ImageBannerFrameLayout extends FrameLayout implements ImageBannerViewGroup.ImageBannerViewGroupListener , ImageBannerViewGroup.ImageBannerListner{    private ImageBannerViewGroup imageBannerViewGroup ;    private LinearLayout linearLayout ;    //自定义轮播图的监听器    public FrameLayoutListener listener ;    public FrameLayoutListener getListener() {        return listener;    }    public void setListener(FrameLayoutListener listener) {        this.listener = listener;    }    public ImageBannerFrameLayout(Context context) {        super(context);        initViewGroup();        initDotLinearLayout();    }    public ImageBannerFrameLayout(Context context, AttributeSet attrs) {        super(context, attrs);        initViewGroup();        initDotLinearLayout();    }    public ImageBannerFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initViewGroup();        initDotLinearLayout();    }    //初始化图片轮播功能的核心类    private void initViewGroup(){        imageBannerViewGroup = new ImageBannerViewGroup(getContext());        //设置布局属性        FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);        imageBannerViewGroup.setLayoutParams(fp);        //为ViewGroup设置底部圆点切换的监听器        imageBannerViewGroup.setBannerViewGroupListener(this);        //为ViewGroup设置图片点击事件的监听器        imageBannerViewGroup.setListner(this);        addView(imageBannerViewGroup);    }    //初始化底部圆点布局    private void initDotLinearLayout(){        linearLayout = new LinearLayout(getContext());        //设置布局属性        FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 40);        linearLayout.setLayoutParams(fp);        linearLayout.setOrientation(LinearLayout.HORIZONTAL);        linearLayout.setGravity(Gravity.CENTER);        linearLayout.setBackgroundColor(Color.GRAY);        addView(linearLayout);        FrameLayout.LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams();        layoutParams.gravity = Gravity.BOTTOM ;        linearLayout.setLayoutParams(layoutParams);        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){            linearLayout.setAlpha( 0.5f );        }else{            linearLayout.getBackground().setAlpha(100);        }    }    //公共方法,向FrameLayout中添加图片    public void addBitmap( List<Bitmap> list ){        for( int i = 0 ; i < list.size() ; i ++ ){            Bitmap bitmap = list.get(i);            addBitmapToViewGroup(bitmap);            addDots();        }    }    //向ViewGroup中添加图片    private void addBitmapToViewGroup(Bitmap bitmap){        ImageView iv = new ImageView(getContext());        iv.setScaleType(ImageView.ScaleType.CENTER_CROP);        iv.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));        iv.setImageBitmap(bitmap);        imageBannerViewGroup.addView(iv);    }    //向底部linearlayout中添加圆点    private void addDots(){        ImageView iv = new ImageView(getContext());        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);        lp.setMargins(15,5,15,5);        iv.setLayoutParams(lp);        iv.setImageResource(R.drawable.dot_normal);        linearLayout.addView(iv);    }    //实现该接口,完成底部圆点的切换    @Override    public void selectImage(int index) {        int count = linearLayout.getChildCount();        for( int i = 0 ; i < count ; i ++ ){            ImageView iv = (ImageView) linearLayout.getChildAt(i);            if( i == index ){                iv.setImageResource(R.drawable.dot_select);            }else {                iv.setImageResource(R.drawable.dot_normal);            }        }    }    @Override    public void clickImageIndex(int pos) {        listener.clickImageIndex(pos);    }    //定义FrameLayout的监听器接口    public interface FrameLayoutListener{        void clickImageIndex( int pos ) ;    }}

MainActivity代码

package com.imagebanner;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.support.v7.app.ActionBar;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.DisplayMetrics;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements ImageBannerFrameLayout.FrameLayoutListener{    private ImageBannerFrameLayout mGroup ;    private int[] ids = new int[]{            R.drawable.banner1,            R.drawable.banner2,            R.drawable.banner3    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //计算出当前手机的宽度        DisplayMetrics dm = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(dm);        int width = dm.widthPixels;        mGroup = (ImageBannerFrameLayout) findViewById(R.id.image_banner);        mGroup.setListener(this);        List<Bitmap> list = new ArrayList<>();        for( int i = 0 ; i < ids.length ; i ++ ){            Bitmap bitmap = BitmapFactory.decodeResource(getResources(),ids[i]);            list.add(bitmap);        }        mGroup.addBitmap(list);    }    //此处填写点击事件相关的业务代码    @Override    public void clickImageIndex(int pos) {          Toast.makeText(this,"点击了第" + pos +  "张图片" , Toast.LENGTH_SHORT).show();    }}

圆点布局文件dot_normal.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="oval">    <solid android:color="@android:color/white"></solid>    <size android:height="10dp"        android:width="10dp"></size></shape>

dot_select.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="oval">    <solid android:color="@android:color/holo_red_light"></solid>    <size android:height="10dp"        android:width="10dp"></size></shape>

MainActivity布局文件

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.imagebanner.MainActivity">    <com.imagebanner.ImageBannerFrameLayout        android:id="@+id/image_banner"        android:layout_width="match_parent"        android:layout_height="200dp"></com.imagebanner.ImageBannerFrameLayout></RelativeLayout>
阅读全文
0 0
原创粉丝点击