问答详情
源自:3-1 自定义View实现轮播图总结

最后一页了,原码呢?

最后一页了,原码呢?

提问者:慕粉3499826 2017-10-22 14:04

个回答

  • 画师
    2018-10-09 21:08:17

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Color;
    import android.os.Build;
    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;
    
    import com.example.administrator.asapplication.ImageVariable;
    import com.example.administrator.asapplication.R;
    
    import java.util.List;
    
    public class ImageBarnnerFramLayout extends FrameLayout implements ImageBarnnerViewGroup.ImageBarnnerViewGroupLisnner, ImageBarnnerViewGroup.ImageBarnnerLister{
        private ImageBarnnerViewGroup imageBarnnerViewGroup;
        private LinearLayout linearLayout;
        private FramLayoutLisenner lisenner;
    
    
        public FramLayoutLisenner getLisenner() {
            return lisenner;
        }
    
        public void setLisenner(FramLayoutLisenner lisenner) {
            this.lisenner = lisenner;
        }
    
        public ImageBarnnerFramLayout(@NonNull Context context) {
            super(context);
            initImageBarnnerViewGroup();
            initDotLinearLayout();
        }
    
        public ImageBarnnerFramLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initImageBarnnerViewGroup();
            initDotLinearLayout();
        }
    
        public ImageBarnnerFramLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initImageBarnnerViewGroup();
            initDotLinearLayout();
        }
    
    
        public void addBitmap( List<Bitmap> list){
            for(int i = 0; i<list.size(); i++){
                Bitmap bitmap = list.get(i);
                addBitmapToImageBarnnerViewGroup(bitmap);
                addDotToLinearlayout();
            }
        }
    
        private void addBitmapToImageBarnnerViewGroup(Bitmap bitmap){
            ImageView iv = new ImageView(getContext());
            iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
            iv.setLayoutParams(new ViewGroup.LayoutParams(ImageVariable.WIDTH, ImageVariable.HEIGHT/4));
            iv.setImageBitmap(bitmap);
            imageBarnnerViewGroup.addView(iv);
        }
    
        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 initImageBarnnerViewGroup(){
            imageBarnnerViewGroup = new ImageBarnnerViewGroup(getContext());
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
            imageBarnnerViewGroup.setLayoutParams(lp);
            imageBarnnerViewGroup.setBarnnerViewGroupLisnner(this);//这里就是将Lisnner,传递给Framlayout
            imageBarnnerViewGroup.setLister(this);
            addView(imageBarnnerViewGroup);
        }
    
    
        /**
         * 初始化我们的底部圆点布局
         */
        private void initDotLinearLayout(){
            linearLayout = new LinearLayout(getContext());
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.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);
        }
    }


  • 画师
    2018-10-09 21:07:39

    
    
    import android.content.Context;
    import android.os.Handler;
    import android.os.Message;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Scroller;
    import android.widget.Toast;
    
    import java.util.Timer;
    import java.util.TimerTask;
    
    /**
     * 该类是实现轮播图的核心类
     */
    public class ImageBarnnerViewGroup extends ViewGroup{
    
        private int children;//子视图的总个数
        private int childheight;//子视图的高度
        private int childwidth;//子视图的宽度
    
        private int x;//此时x的值,代表第一次按下位置的横坐标,每次轮播图移动都是横坐标移动
        private int index = 0;//代表每个图片的索引
        private Scroller scroller;
    
        /**
         * 要想实现图片单击事件的获取
         * 方法:  利用一个单击变量开关进行判断,在用户离开屏幕的一瞬间,
         * 我们去判断变量开关来判断用户的操纵是点击还是移动
         */
        private boolean isClick;//true的时候,代表点击事件;flase代表的不是点击事件
        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
         *    布局的特性(在同一位置放置不同view,最终显示最后一个view),我们就可以实现底部圆点的布局。
         * 2.我们需要准备素材,就是底部的素材---圆点。利用Drawable的功能,去实现一个圆点图片的展示。
         * 3.我们还需要继承FramLayout 来自定义一个类,在该类的实现过程中,我们去加载我们刚才自定义的
         *   ImageBarnnerViewGroup核心类 和 步骤2的底部圆点的布局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;
        }
    
        public ImageBarnnerViewGroup(Context context) {
            super(context);
            initObj();
        }
    
        public ImageBarnnerViewGroup(Context context, AttributeSet attrs) {
            super(context, attrs);
            initObj();
        }
    
        public ImageBarnnerViewGroup(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() {
            super.computeScroll();
            if(scroller.computeScrollOffset()){
                scrollTo(scroller.getCurrX(),0);
                invalidate();
            }
        }
        /**
         * 我们在自定义ViewGroup方法中,我们必须实现的有 测量=》布局=》绘制
         * 测量 就是onMeasure()方法
         * 布局 就是onLayout()方法
         * 绘制 就是()方法
         */
    
        /**
         * 对于绘制来说,因为我们是自定义ViewGroup容器,针对于容器的绘制
         * 其实就是容器内部子控件的绘制过程,调用系统自带的绘制即可。
         * 即,对于ViewGroup的绘制过程,我们不需要重写该方法,调用系统自带即可
         */
    
    
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            /**
             * 由于我们实现的ViewGroup容器,那么
             * 我们应该知道该容器中的所有子视图。
             * 我们想要测量ViewGroup的宽度和高度,那么我们必须先去测量子视图的
             * 宽度和高度之和,才能知道ViewGroup的宽度和高度为多少
             */
    
            //第1步。求出的子视图的个数
            children = getChildCount();//获取子视图的个数
            if( children == 0){ //如果子视图个数为0.就不需要求子视图的高度和宽度
                setMeasuredDimension(0,0);//测量宽度和高度为0
            }else{
    
                //第2步。求出子视图的测量宽度和测量高度
                measureChildren(widthMeasureSpec,heightMeasureSpec);
                //此时,我们第一个子视图的宽度,就是我们第一个ViewGroup的宽度 乘以 子视图的个数
                //子视图的高度,就是我们vGroup的高度.
                View view = getChildAt(0);//因为第一个视图存在的,所以编号为0
    
                //第3步。根据子视图的宽度和高度,来求出ViewGroup的宽度和高度
                childheight = view.getMeasuredHeight();
                childwidth = view.getMeasuredWidth();
                int width = view.getMeasuredWidth() * children;//所有子视图宽度总和
    
                //重新赋值,测量完毕
                setMeasuredDimension(width,childheight);
    
            }
        }
    
    
        /**
         * 继承ViewGroup必须实现布局onLayout方法
         *
         * @param changed 代表ViewGroup布局位置是否发生变化,是则为true,不是则为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 = leftMargin + childwidth;
                }
            }
        }
    
    
    
        /**
         * 事件传递过程中的调用方法,我们需要  调用  容器内部的拦截方法
         * 针对于该方法,我们可以理解为 该方法的返回值为true,我们自定义的ViewGroup容器就会处理此次拦截事件
         * 返回值为flase,我们自定义ViewGroup容器则不会接受此次事件的处理过程,将会向下传递该事件
         * 针对于我们自定义的ViewGroup,我们当然希望我们的ViewGroup容器处理接受事件,那么我们的返回值就为true
         * 如果返回值为true,那么真正处理该事件的方法为onTouch方法
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return true;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
        //另一种方法,手势探测类 GestureDetector
            
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN://表示  用户按下的一瞬间
                    stopAuto();
                    if(!scroller.isFinished()){
                        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.startScroll(scrollx,0,dx,0);
                        postInvalidate();
                        barnnerViewGroupLisnner.selectImage(index);
                    }
    
                      startAuto();
    //                scrollTo(index * childwidth, 0);
                    break;
    
                 default:
                     break;
            }
            return true;//返回true,是告诉ViewGroup容器的父容器,我们已经处理好该事件
        }
    
        public interface ImageBarnnerViewGroupLisnner{
            void selectImage(int index);
        }
    
    
    
    
    
    
    }


  • 慕粉3499826
    2017-12-03 18:34:02

    。。。

  • 浅淡哟
    2017-11-21 21:01:26

    我也想要源码,自己打的,有点问题,没找到错在哪了