Android自定义的轮播图控件,基于ViewPager

这个东西可能没什么好讲的,但是这个和正常的有点不一样。正常的是你到了这个view,底部的小点就会切换,颜色就会改变。这里是有一个伴随ViewPager滑动的点,根据ViewPager滑动的百分比也对应滑到这些若干点中响应的位置。

Android自定义的轮播图控件,基于ViewPager


其实这个看似简单,并没有那么好实现。下面讲一下实现方案:

ViewPager开放了一个滑动时的监听方法

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
第一个position就是当前在哪个页面,第二个positionOffset就是当前偏移的百分比,在0-1之间变化,所以position+positionOffset就是0-5之间变化了,这一点很重要,这一点没理解,这个东西做不出来。


所以你接下来你需要计算每两个点的距离x,x乘以position+positionOffset+第一个点的左边距就是你这个点当前的横坐标了。所以你就可以用layout方法修改他的位置了


但是修改了还不行,由于重绘机制的存在,你必须修改布局参数才行。不然会发现,过一会就会复位。


github地址:https://github.com/xubinhong/WeChat2(在emojiFragment里)

package com.example.wechat.chat;

import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.example.wechat.R;
import com.example.wechat.base.BaseFragment;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;

/**
 * 第三次更新2017/12/22 完成了emoji相关,那个底部黑色dot,本来想属性动画,但是必须是跟随手指滑动的,所以还必须自定义控件(其实有点类似于view pager和tab host的绑定)
 **/
public class EmojiFragment extends BaseFragment {
    @BindView(R.id.vp_emoji)
    ViewPager mVpEmoji;

    // TODO: 2017/12/21 把emoji的滑动和那个黑点的滑动关联起来就好了,简单明白了!只需要在vp的监听事件里算出来移动的距离就可以了,在获取屏幕的长度,取得比例给底部的黑点。黑点是自定义控件。

    List<View> viewList = new ArrayList<>();

    @Override
    protected void initData() {
        if (viewList.size() == 0) {//之所以这样判断是因为我发现在闪电运行的时候,重了
            for (int i = 0; i < 6; i++) {
                View v = LayoutInflater.from(getActivity()).inflate(R.layout.all_emoji, null, false);
                viewList.add(v);
            }
        }

        // TODO: 2017/12/22 onCreate的时候控件还没有渲染完,所以不管怎么取坐标,都是0
    }

    private float dotDistance;

    private float realPos;//0-5

    private float dotStartLeftPos;
    private float dotStartRightPos;

    private View dotView;

    @Override
    protected void initWidget(final View mRoot) {
        mVpEmoji.setAdapter(new MyAdapter(viewList));
        mVpEmoji.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //看到这个positionOffset的输出值我就知道已经搞定了,它代表了滑动的比例
                //接下来需要取得两个按钮之间的距离,以第一个按钮和第二个按钮为参照。取得孩子,再取得左边距离就可以了
                //还要在一开始明确一个方向,判断滑动方向也是一个问题,主要分界的时候太难判断了,(哎,观察的太不仔细了),分界的时候,如果往左,会突然变到0.9几的,我只需要把positionOffset通过数学的方式处理成0-5就可以了,如果减了1(粗略点就是大了0.8)就代表是右移,需要+1,如果加了1,就代表是左移
                if (dotDistance == 0.0f) {
                    ViewGroup vg = (ViewGroup) mRoot;
                    ViewGroup vg2 = (ViewGroup) vg.getChildAt(1);
                    View v1 = vg2.getChildAt(0);
                    View v2 = vg2.getChildAt(1);
                    dotDistance = v2.getLeft() - v1.getLeft();

                    dotStartLeftPos = v1.getLeft();
                    dotStartRightPos = v1.getRight();

                    dotView = vg.getChildAt(2);
                }

                //为什么会有一个缩到0的操作,可能是因为

                realPos = positionOffset + position;//一行代码就搞定了!爽。和我的+1,-1效果一样

                Log.i("xbh", realPos + "");

                if (dotDistance != 0.0f) {
                    float dotLeftPos = dotDistance * realPos + dotStartLeftPos;
                    float dotRightPos = dotDistance * realPos + dotStartRightPos;
                    //很严重的问题就是移动了位置没改变,因为布局参数没改变,然后应该去改变布局参数
                    dotView.layout((int)dotLeftPos, dotView.getTop(), (int)dotRightPos , dotView.getBottom());
                    RelativeLayout.LayoutParams lpFeedback = new RelativeLayout.LayoutParams(
                            13, 13);
                    lpFeedback.setMargins(dotView.getLeft(), dotView.getTop(), 0, 0);
                    dotView.setLayoutParams(lpFeedback);
                }
            }

            @Override
            public void onPageSelected(int position) {//页面变动,才会执行
                //给那个dot一个属性动画,平移到对应第几个
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    @Override
    protected int getLayoutId() {
        return R.layout.fragment_emoji;
    }

    private class MyAdapter extends PagerAdapter {

        private List<View> list;

        MyAdapter(List<View> list) {
            this.list = list;
        }

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(list.get(position));
            return list.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(list.get(position));
        }
    }
}