美文网首页
Android 焦点轮播图的打造

Android 焦点轮播图的打造

作者: 北林丶 | 来源:发表于2017-12-28 18:14 被阅读0次

    作为一个刚工作的菜鸟,连个焦点轮播图都撸不出来,真的是感觉没脸见人,查阅多方资料,磕磕绊绊也算是做出来了,但由于不会写,大部分代码也是拼凑起来的,代码不美观不说,复用起来也是极为的不易,寻思还是从头整理一下,做个自定义View,日后复用,查看也更为顺手。
    说是自定义view,其实也只是组合view,将不同功能的view组合在一起。

    STEP.ONE 滑动展示图片

    通过ViewPager可以容易的实现可滑动的展示图片的view。新建三个文件:

    • custom_banner.xml,自定义View布局
      <?xml version="1.0" encoding="utf-8"?>
      <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v4.view.ViewPager
            android:id="@+id/vp_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
      </RelativeLayout>
    

    布局文件,很简单,随着功能的完善,还会添加view

    • Banner.java,自定义view
      public class Banner extends RelativeLayout {
        private String TAG = "Banner";
        private View mView;
        private ViewPager mImage;
    
        private List<Integer> mImageList = new ArrayList<>();
        public Banner(Context context, AttributeSet attrs) {
            super(context, attrs);
            mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
            init();
        }
        private void init() {
            mImage = mView.findViewById(R.id.vp_image);
            mImageList = new ArrayList<>();
            mImageList.add(R.mipmap.img_1);
            mImageList.add(R.mipmap.img_2);
            mImageList.add(R.mipmap.img_3);
            ImageAdapter adapter = new ImageAdapter(mImage,mImageList);
            mImage.setAdapter(adapter);
        }
    }
    

    public Banner(Context context, AttributeSet attrs)引入布局,init()中简单的添加了几个数据,通过adapter加载。

    • ImageAdapter.java,adapter
    public class ImageAdapter extends PagerAdapter {
        private String TAG = "ImageAdapter";
        private ViewPager mImage;
        private List<Integer> mImageList = new ArrayList<>();
    
        private int DEFAULT_BANNER_SIZE;
        private int FAKE_BANNER_SIZE;
        public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
            this.mImage = mImage;
            this.mImageList = mImageImageList;
            this.DEFAULT_BANNER_SIZE = mImageImageList.size();
            this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
        }
    
        @Override
        public int getCount() {
            return FAKE_BANNER_SIZE;
        }
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            position %= DEFAULT_BANNER_SIZE;
            ImageView img = new ImageView(container.getContext());
            img.setImageResource(mImageList.get(position));
            container.addView(img);
            return img;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            //super.destroyItem(container, position, object);
            container.removeView((View) object);
        }
    }
    

    instantiateItem()加载子view,定义了两个变量DEFAULT_BANNER_SIZE和FAKE_BANNER_SIZE,在循环滑动时会用到。

    到这里最简单的ViewPager的用法了,这里还有一点需要注意,ViewPager需要重写onMeasure()方法,测量child的高度作为自身高度,否则会match_parent,在banner.java中重写onMeasure()。

    @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int maxHeight = 0;
            for (int i = 0; i < mImage.getChildCount(); i++) {
                View child = mImage.getChildAt(i);
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                maxHeight = Math.max(maxHeight,child.getMeasuredHeight());
            }
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    

    测量child的高度,最大值作为父容器高度。

    STEP.TWO 实现循环与自动播放

    实现循环重写PagerAdapter中的finishUpdate(),自动播放使用轻量级的定时器Timer。

    • 循环。在ImageAdapter.java中重写finishUpdate()。
    public class ImageAdapter extends PagerAdapter {
        private String TAG = "ImageAdapter";
        private ViewPager mImage;
        private List<Integer> mImageList = new ArrayList<>();
    
        private int DEFAULT_BANNER_SIZE;
        private int FAKE_BANNER_SIZE;
        public ImageAdapter( ViewPager mImage, List<Integer> mImageImageList) {
            this.mImage = mImage;
            this.mImageList = mImageImageList;
            this.DEFAULT_BANNER_SIZE = mImageImageList.size();
            this.FAKE_BANNER_SIZE = DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
        }
    
        @Override
        public int getCount() {
            return FAKE_BANNER_SIZE;
                          .
                          .
                          .
        @Override
        public void finishUpdate(ViewGroup container) {
            super.finishUpdate(container);
            int curPosition = mImage.getCurrentItem();
            if (0 == curPosition){
                //滑动到最左边
                mImage.setCurrentItem(DEFAULT_BANNER_SIZE, false);
            }
            if ((FAKE_BANNER_SIZE -1) == curPosition){
                //滑动到最右边
                mImage.setCurrentItem(FAKE_BANNER_SIZE - DEFAULT_BANNER_SIZE - 1, false);
            }
        }
    }
    

    DEFAULT_BANNER_SIZE是图片列表的大小,FAKE_BANNER_SIZE是三倍图片列表的小。
    以代码为例,目前我们的ViewPager有3屏(DEFAULT_BANNER_SIZE),但是如果在PagerAdapter的getCount方法中我们返回9(FAKE_BANNER_SIZE),即告诉ViewPager我们有9屏。那么如果当前位于第3屏,由于对于ViewPager来说它认为有9屏,所以我们仍然可以向后滑动。当滑动到第9屏时(右边界),立即将第6屏的view返回给他,这样ViewPager依然可以向右滑动。同理处于第1屏时(左边界),讲第4屏的view返回给他,此时仍然可以左滑。这样便实现了循环播放。如图..............hahah


    示例图
    • 自动播放
    public class Banner extends RelativeLayout {
        private int DEFAULT_BANNER_SIZE;
        private int FAKE_BANNER_SIZE;
        //{start update 实现自动播放
        @SuppressLint("HandlerLeak")
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 1:
                        int position = (mImage.getCurrentItem() + 1) % DEFAULT_BANNER_SIZE + DEFAULT_BANNER_SIZE;
                        mImage.setCurrentItem(position, true);
                        Log.d(TAG, "handleMessage: "+position);
                        break;
                    default:
                        break;
                }
            }
        };
        // end }
    
        private List<Integer> mImageList = new ArrayList<>();
        public Banner(Context context, AttributeSet attrs) {
            super(context, attrs);
            mView = LayoutInflater.from(getContext()).inflate(R.layout.custom_banner, this);
            init();
            //调用
            startTimer();
        }
        //{start update
        private void startTimer() {
            Timer timer = new Timer();
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.what = 1;
                    handler.sendMessage(message);
                }
            };
            timer.schedule(task,5000,5000);
        }
        //end }
    }
    

    使用Timer定义一个5秒执行的任务,通过Headler异步执行。

    STEP.THREE 底部圆点

    根据mImageList动态添加底部圆点。

    • 修改custom_banner.xml文件
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v4.view.ViewPager
            android:id="@+id/vp_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <LinearLayout
            android:id="@+id/ll_point"
            android:layout_centerHorizontal="true"
            android:layout_alignBottom="@+id/vp_image"
            android:layout_marginBottom="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" />
    </RelativeLayout>
    

    新增了LinearLayout布局,添加圆点的任务就交给他了。

    • 修改Banner.java
    private void initPoint() {
            for (int i = 0; i < mImageList.size(); i++) {
                View view = new View(getContext());
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
                params.leftMargin = 10;
                params.rightMargin = 10;
                view.setLayoutParams(params);
                if (i == 0) {
                    view.setBackgroundResource(R.drawable.shape_point_select);
                } else {
                    view.setBackgroundResource(R.drawable.shape_point_default);
                }
                final int finalI = i;
                view.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int item = finalI+DEFAULT_BANNER_SIZE;
                        mImage.setCurrentItem(item, true);
                    }
                });
                mPoint.addView(view);
                mPointList.add(view);
            }
        }
    

    在mImageList初始化后执行该函数,添加圆点,背景,和点击事件。

    • 发生点击事件后,修改圆点背景
    mImage.addOnPageChangeListener(this);
    
     @Override
        public void onPageSelected(int position) {
            position %= DEFAULT_BANNER_SIZE;
            for (View view : mPointList) {
                view.setBackgroundResource(R.drawable.shape_point_default);
            }
            mPointList.get(position).setBackgroundResource(R.drawable.shape_point_select);
        }
    

    添加监听,当某页面被选中时,先遍历列表,将所有圆点都设置为未选中状态,再将对应页面的背景设置为选中背景。

    END

    到这里这个功能就已经全部实现了,这也是我的第一篇技术文章,参考了挺多大大的博客,最后倒腾出来。文章感觉写的干巴巴,一来我本人对有些也不算特别熟悉,二来文章写的少。坚持写博客,到自己的文章干货越来越多,越写越写流畅,自己的成长了多少也就很明确了。当然也希望这篇文章可以帮助到大家。

    参考链接

    循环广告位组件的实现
    动态圆点轮播图
    解决ViewPager高度无法wrap_content问题

    相关文章

      网友评论

          本文标题:Android 焦点轮播图的打造

          本文链接:https://www.haomeiwen.com/subject/wiimgxtx.html