Android uses viewpager achieve image carousel effect

Custom View achieve image carousel, have realized the automatic carousel, manual slide, carousel title, and click on the event.

There are a lot of comments

First, the file layout

 

Second, the code

ImageBannerViewGroup类

/**
 * Created by hp on 2018/7/31.
 * 这是实现图片轮播的核心类
 */

public class ImageBannerViewGroup extends ViewGroup
{

    private int children;//我们ViewGroup子视图的总个数
    private int childwidth;//子视图的宽度
    private int childheight;//子视图的高度


    private  int x;//此时的x的值 代表的是第一次按下的位置横坐标、每一次移动过程中一定之前的位置横坐标
    private int index = 0;//代表的是我们每张图片的索引

    private Scroller scroller;//.利用 Scroller 对象 完成轮播图的手动轮播
    /**
     * 要实现图片的单击事件的获取
     * 我们 利用一个单击变量开关进行判断,在用户离开屏幕的一瞬间
     * 我们用判断变量开关来判断用户的操作是点击还是移动
     */

    private boolean isClick;//true代表的是点击事件,false代表的是不是点击事件
    private ImageBarnnerLister lister;

    public ImageBarnnerLister getLister(){
        return lister;
    }

    public void setLister(ImageBarnnerLister lister) {
        this.lister = lister;
    }

    public interface ImageBarnnerLister{
        void clickImageIndex(int pos);//pos代表的是我们当前图片的具体索引值
    }

    private ImageBarnnerViewGroupLisnner barnnerViewGroupLisnner;

    public ImageBarnnerViewGroupLisnner getBarnnerViewGroupLisnner() {
        return barnnerViewGroupLisnner;
    }

    public void setBarnnerViewGroupLisnner(ImageBarnnerViewGroupLisnner barnnerViewGroupLisnner) {
        this.barnnerViewGroupLisnner = barnnerViewGroupLisnner;
    }
    /**
     * 实现图片轮播底部圆点以及底部圆点切换功能步骤思路:
     * 1.自定义一个集成自FrameLayout的布局,利用FrameLayout布局的特性(在同一位置放置不同的iew最终显示的是最后一个view)
     *   利用这个特性我们就可以实现底部圆点的布局
     * 2.我们需要准备素材,就是底部圆点的素材,我们可以利用Drawable的功能,去实现一个圆点的展示
     * 3.我们需要继承FrameLayout来定义一个类,在该类的实现过程中,我们去加载我们刚才自定义的ImageBannerViewGroup核心类
     *    和我们需要实现的底部圆点的布局LinearLayout来实现
     */
    //---自动轮播
    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);
                    barnnerViewGroupLisnner.selectImage(index);
                    break;
            }
        }
    };
    private void startAuto(){
        isAuto = true;
    }
    private void stopAuto(){
        isAuto = false;
    }

    /**
     * 采用Timer,TimerTask,Handler三者相结合的方法实现自动轮播
     * 抽取两个方法来控制,是否启动自动轮播,称之为 startAuto(),stopAuto();
     * 我们在2个方法的控制过程中,我们希望能有控制自动开启轮播图的开关
     * 我们需要一个变量参数来作为我们自动启动轮播图的开关,为 isAuto boolean  true代表开启,false代表关闭
     */



    public ImageBannerViewGroup(Context context) {
        super(context);
        initObj();//.利用 Scroller 对象 完成轮播图的手动轮播需要用到这个方法,利用 scrollTo、scrollBy 完成轮播图的手动轮播时可以删掉
    }

    public ImageBannerViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        initObj();
    }

    public ImageBannerViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initObj();
    }

    private void initObj() {
        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() {//利用 Scroller 对象 完成轮播图的手动轮播需要用到这个方法
        super.computeScroll();
        if(scroller.computeScrollOffset()){
            scrollTo(scroller.getCurrX(),0);
            invalidate();
        }
    }

    /**
     * 我们在自定义的ViewGroop中,我们必须要实现的方法有:测量-》布局-》绘制
     * 那么对于来说就是:onMeasure()
     * 我们对于绘制来说,因为我们是自定义的ViewGroup容器,针对于容器的绘制
     * 其实就是容器内的子控件的绘制过程,那么我们只需要调用系统自带的绘制即可,
     * 也就是对于ViewGroup绘制过程我们不需要再重写该方法
     * 调用系统的即可
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 由于我们要实现ViewGroup的容器,
         * 那么我们就需要知道该容器内的所有子视图
         * 我们要想测量我们的ViewGroup的宽度和高度,那么我们就必须先要去测量子视图
         * 的宽度和高度之和,才能知道我们的ViewGroup的宽度和高度是多少
         */

        //1.求子视图的个数
        children = getChildCount();//我们就可以知道子视图的个数
        if(0 == children){
            setMeasuredDimension(0,0);
        }
        else{
            //2.测量子视图的高度
            measureChildren(widthMeasureSpec,heightMeasureSpec);
            //此时我们以第一个子视图为基准,也就是说我们的viewGroup的高度就是我们第一个子视图的高度
            //宽度就是我们第一个姿势图的额宽度*子视图的个数
            View view = getChildAt(0);//因为此时第一个视图绝对是存在的
            childwidth = view.getMeasuredWidth();

            //3.根据子视图的宽度和高度,求出改ViewGroup的宽度和高度
            childheight = view.getMeasuredHeight();
            int width = view.getMeasuredWidth() * children;
            setMeasuredDimension(width,childheight);

        }

    }
    /**
     * 事件传递过程中的调用方法,我们需要调用容器的拦截方法 onInterceptTouchEvent
     * 针对于该方法我们可以理解为 如果说该方法的返回值为true的时候,那么我们自定义的ViewGroup容器就会处理此次拦截事件
     * 如果说返回值为false的时候,那么我们自定义的ViewGroup容器将不会接受此次事件的处理过程,将会继续向下传递事件
     * 针对于我们自定义的ViewGroup 我们希望我们的ViewGroup,容器处理接受事件 那么我们的返回值就是true
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return  true;
    }


    /**
     * 用2中方式来实现轮播图的手动轮播
     * 1.利用 scrollTo、scrollBy 完成轮播图的手动轮播
     * 2.利用 Scroller 对象 完成轮播图的手动轮播
     *
     * 第一:我们在滑动屏幕图片的过程中,其实就是 我们自定义ViewGroup的子视图的移动过程,那么我们只需要知道
     * 滑动之前的横坐标和滑动之后的横坐标,此时  我们就可以  求出次过程中移动的距离,我们在利用 scrollBy方法实现
     * 图片的滑动,所以 此时我们需要有2个值要我们求出:移动之前、移动之后的 横坐标值
     *
     * 第二:在我们第一次按下的那一瞬间,此时的移动之前和移动之后的值是相等的,也就是我们按下的那一瞬间的那一个点
     * 的横坐标的值。
     *
     * 第三:我们在不断滑动的过程中,会不断的调用我们的ACTION_MOVE方法,那么此时我们就应该将移动之前的值
     * 和移动之后的进行保存,以便我们能够算出滑动的距离
     *
     * 第四:在我们抬起的那一瞬间,我们需要计算出我们此时将要滑动到哪张图片的位置上。
     *
     * 我们此时就需要求得将要滑动到的哪张图片的索引值。
     * (我们当前ViewGroup的滑动距离 + 我们的每一张图片的宽度 / 2) / 我们的每一张图片的宽度值
     *
     * 此时我们就可以利用scrollTo方法。滑动到该图片的位置上
     *
     */

    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        return super.onTouchEvent(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN://表示的是用户按下的一瞬间
                stopAuto();
                if(scroller.isFinished()){//.利用 Scroller 对象 完成轮播图的手动轮播
                    scroller.abortAnimation();
                }
                isClick = true;
                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){//代表点击事件
                    lister.clickImageIndex(index);
                }
                else {
                    int dx = index * childwidth - scrollX;//.利用 Scroller 对象 完成轮播图的手动轮播
                    scroller.startScroll(scrollX,0,dx,0);//.利用 Scroller 对象 完成轮播图的手动轮播
                    postInvalidate();//.利用 Scroller 对象 完成轮播图的手动轮播
//                scrollTo(index * childwidth,0);//利用 scrollTo、scrollBy 完成轮播图的手动轮播
                    barnnerViewGroupLisnner.selectImage(index);
                }

                startAuto();
                break;
            default:
                break;
        }
        return true;//我们该容器的父View,我们已经处理好了该事件

    }

    /**
     * 继承ViewGroup必须要实现布局onLayout方法
     * @param changed 当我们的ViewGroup布局位置发生变化的为true,没有发生改变为false
     * @param l
     * @param t
     * @param r
     * @param b ,没有发生改变为false
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){

            int leftMargin = 0;
            for(int i = 0;i < children;i++){
                View view = getChildAt(i);

                view.layout(leftMargin, 0, leftMargin + childwidth, childheight);
                leftMargin += childwidth;

            }
        }
    }

    public interface ImageBarnnerViewGroupLisnner{
        void selectImage(int index);
    }
}


ImageBarnnerFrameLayout类

/**
 * Created by hp on 2018/8/2.
 */

public class ImageBarnnerFrameLayout extends FrameLayout implements ImageBannerViewGroup.ImageBarnnerViewGroupLisnner,ImageBannerViewGroup.ImageBarnnerLister{

    private ImageBannerViewGroup imageBannerViewGroup;
    private LinearLayout linearLayout;

    private FramLayoutLisenner lisenner;

    public FramLayoutLisenner getLisenner() {
        return lisenner;
    }

    public void setLisenner(FramLayoutLisenner lisenner) {
        this.lisenner = lisenner;
    }

    public ImageBarnnerFrameLayout(@NonNull Context context) {
        super(context);
        initImageBannerViewGroup();
        initDotLinearlayout();
    }

    public ImageBarnnerFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initImageBannerViewGroup();
        initDotLinearlayout();
    }

    public ImageBarnnerFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initImageBannerViewGroup();
        initDotLinearlayout();
    }

    public void addBitmaps(List<Bitmap> list){

        for(int i = 0;i < list.size();i++){
            Bitmap bitmap = list.get(i);
            addBitmapToImageBarnnerViewGroup(bitmap);
            addDotToLinearlayout();
        }

    }
    private void addDotToLinearlayout(){
        ImageView iv = new ImageView(getContext());
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        lp.setMargins(5,5,5,5);
        iv.setLayoutParams(lp);
        iv.setImageResource(R.drawable.dot_normal);
        linearLayout.addView(iv);

    }

    private void addBitmapToImageBarnnerViewGroup(Bitmap bitmap){

        ImageView iv = new ImageView(getContext());
        iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
        iv.setLayoutParams(new ViewGroup.LayoutParams(C.WITTH,ViewGroup.LayoutParams.WRAP_CONTENT));
        iv.setImageBitmap(bitmap);
        imageBannerViewGroup.addView(iv);
    }

    /**
     * 初始化我们自定义的图片轮播功能的核心
     */
    private void initImageBannerViewGroup(){
        imageBannerViewGroup = new ImageBannerViewGroup(getContext());
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
        imageBannerViewGroup.setLayoutParams(lp);
        imageBannerViewGroup.setBarnnerViewGroupLisnner(this);//这里就是将Lisnner传递给Framlayout
        imageBannerViewGroup.setLister(this);
        addView(imageBannerViewGroup);
    }

    /**
     * 初始化 底部圆点 布局
     */
    private void initDotLinearlayout(){
        linearLayout = new LinearLayout(getContext());
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,40);
        linearLayout.setLayoutParams(lp);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);
        linearLayout.setGravity(Gravity.CENTER);

        linearLayout.setBackgroundColor(Color.RED);

        addView(linearLayout);

        FrameLayout.LayoutParams layoutParams = (LayoutParams) linearLayout.getLayoutParams();
        layoutParams.gravity = Gravity.BOTTOM;

        linearLayout.setLayoutParams(layoutParams);

        /**
         * 设置透明度
         * 在3.0以后,我们使用的是setAlpha(),在3.0之前我们使用的也是setAlpha(),但是调用者不同
         */
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            linearLayout.setAlpha(0.5f);
        }
        else {
            linearLayout.getBackground().setAlpha(100);
        }

    }

    @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) {
        lisenner.clickImageIndex(pos);
    }

    public interface FramLayoutLisenner{
        void clickImageIndex(int pos);
    }
}
 

 MainActivity类

public class MainActivity extends AppCompatActivity implements ImageBarnnerFrameLayout.FramLayoutLisenner {

    private ImageBarnnerFrameLayout mGroup;
    private int[] ids = new int[]{
            R.drawable.d,
            R.drawable.a,
            R.drawable.b,
            R.drawable.c
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //我们需要计算出我们当前手机的宽度
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        C.WITTH = dm.widthPixels;
        mGroup = (ImageBarnnerFrameLayout)findViewById(R.id.image_group);
        mGroup.setLisenner(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.addBitmaps(list);
       /* for (int i = 0;i < ids.length;i++){
            ImageView iv = new ImageView(this);
            iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
            iv.setLayoutParams(new ViewGroup.LayoutParams(width,ViewGroup.LayoutParams.WRAP_CONTENT));
            iv.setImageResource(ids[i]);
            mGroup.addView(iv);
        }

        mGroup.setLister(this);
        */
    }
    public void clickImageIndex(int pos){
        Toast.makeText(this,"pos = "+pos,Toast.LENGTH_SHORT).show();
    }
}

Class C

public class C {
    public static int WITTH = 0;
}

Layout file

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hp.zzdlunbo7.MainActivity">

    <com.example.hp.zzdlunbo7.view.ImageBarnnerFrameLayout
        android:id="@+id/image_group"
        android:layout_width="match_parent"
        android:layout_height="200dp">
    </com.example.hp.zzdlunbo7.view.ImageBarnnerFrameLayout>

</RelativeLayout>

Guide to the attention of the package, the package java and some are Android, error probably imported java package

 

The code is written according to the teacher's curriculum on the Mu class, the teacher is very good, strongly suggest that you listen to, to explain in great detail, a great harvest

Teacher curriculum links https://www.imooc.com/learn/793

Guess you like

Origin blog.csdn.net/qq_38971487/article/details/81482433