使用 ViewPager 和 RadioGroup 封装的一个导航控件

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 使用 ViewPager 和 RadioGroup 封装的一个导航控件<p/>
 * 有2种布局方式:<br/>
 * 1. ViewPager + 图标在上文本在下的tab 栏<br/>
 * 2. 仅有文本的tab + ViewPager<br/>
 * 只需要调用 viewPagerIndicator.bind(fragments, indicatorEntityList) 方法<br/>
 * 其中如果indicatorEntityList 中如果传入图标,就是布局方式1,否则就是布局方式2<br/>
 * <br/> Fragment需要用v4的 ,Activity需要继承自 FragmentActivity
 */
public class ViewPagerIndicator extends LinearLayout implements RadioGroup.OnCheckedChangeListener {

    /**
     * 需要的实体类<p/>
     * 如果只设置tab的名字, 则不会显示图标<br/>
     * 如果设置了图标,会显示对应的图标
     */
    public static class IndicatorEntity {
        /** tab的名字 */
        String indicator;
        /** tab的选中状态和非选中状态的图标 */
        int selectedRes, unSelectedRes;
        boolean isShowIconEnable;

        /**
         * @param indicator tab的名字
         */
        public IndicatorEntity(String indicator) {
            this.indicator = indicator;
            isShowIconEnable = false;
        }

        /**
         * @param indicator     tab的名字
         * @param selectedRes   tab的选中状态的图标
         * @param unSelectedRes tab的非选中状态的图标
         */
        public IndicatorEntity(String indicator, int selectedRes, int unSelectedRes) {
            this.indicator = indicator;
            this.selectedRes = selectedRes;
            this.unSelectedRes = unSelectedRes;
            isShowIconEnable = true;
        }
    }

    /** 选中和非选中tab的前景色, 也就是字体颜色 */
    public static final int selected_fg_Color = 0xff0000ff, unselected_fg_Color = 0xFF000000;
    /** 选中和非选中tab的背景色 */
    public static final int selected_bg_Color = 0xff00ff00, unselected_bg_Color = 0xcccccccc;
    /** 分隔线的颜色 */
    public static final int divide_line_color = 0xFF000000;
    /** 指示条的颜色 */
    public static final int indicator_color = 0xFF0000FF;


    // 是否允许ViewPager 滑动
    private boolean scrollable = true;
    // radioGroup 即tab 栏的高度,可以使用 DimenUtil 传入dp单位的高度
    private int radioGroupShowIconHeight = 54 * 3;
    private int radioGroupGoneIconHeight = 36 * 3;
    private int radioGroupHeight = radioGroupShowIconHeight;
    private Context context;
    // 屏幕宽度
    private int mScreenWidth;
    private RadioGroup mRadioGroup;
    private MyViewPager mViewPager;
    private List<IndicatorEntity> indicatorEntityList;
    // 是否显示图标
    private boolean isShowIconEnable = true;

    /** 上一次的指示条的位置 */
    private int lastSelectedIndex;
    /** 指示条控件 */
    private View indicatorView;

    public ViewPagerIndicator(Context context) {
        this(context, null);
    }

    public ViewPagerIndicator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        this.setOrientation(VERTICAL);
        // 获取屏幕宽度
        DisplayMetrics outMetrics = new DisplayMetrics();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;
    }

    /** 设置是否允许ViewPager 滑动 */
    public void setScrollable(boolean scrollable) {
        this.scrollable = scrollable;
    }

    /**
     * 绑定页面<br/>
     * fragments 和 indicatorEntityList 的数量必须一致
     * <p/>
     *
     * @param fragments           绑定的fragment
     * @param indicatorEntityList 绑定的tab的名字或者图标
     */
    public void bind(List<Fragment> fragments, List<IndicatorEntity> indicatorEntityList) {
        if (fragments == null || fragments.size() <= 0 || indicatorEntityList == null || indicatorEntityList.size() <= 0 || fragments.size() != indicatorEntityList.size())
            return;
        this.indicatorEntityList = indicatorEntityList;
        isShowIconEnable = indicatorEntityList.get(0).isShowIconEnable;
        initViews();
        mViewPager.setFragments(fragments);
        for (int i = 0; i < indicatorEntityList.size(); i++) {
            mRadioGroup.addView(generateRadioButton(i));
        }
        //设置第一个tab为选中状态
        ((RadioButton) mRadioGroup.getChildAt(0)).setChecked(true);
        mViewPager.setCurrentItem(0);
    }

    /** 分隔线 */
    private View generateDivideLine() {
        View view = new View(context);
        view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
        view.setBackgroundColor(divide_line_color);
        return view;
    }

    private void initViewPager() {
        mViewPager = new MyViewPager(context);
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
        layoutParams.weight = 1;
        mViewPager.setLayoutParams(layoutParams);
        mViewPager.setId((int) (System.currentTimeMillis() % 10000)); // Id 必须设置,不然会有 NotFoundException 异常
        mViewPager.addOnPageChangeListener(mOnPageChangeListener);
    }

    private void initRadioGroup() {
        radioGroupHeight = isShowIconEnable ? radioGroupShowIconHeight : radioGroupGoneIconHeight;
        mRadioGroup = new RadioGroup(context);
        mRadioGroup.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, radioGroupHeight));
        mRadioGroup.setOrientation(HORIZONTAL);
        mRadioGroup.setOnCheckedChangeListener(this);
    }

    /** 初始化需要添加的添加控件以及事件 */
    private void initViews() {
        initViewPager();
        initRadioGroup();
        if (isShowIconEnable) {
            this.addView(mViewPager);
            this.addView(generateDivideLine());
            this.addView(mRadioGroup);
        } else {
            this.addView(mRadioGroup);
            initIndicatorView();
            this.addView(indicatorView);
            this.addView(generateDivideLine());
            this.addView(mViewPager);
        }
    }

    private void initIndicatorView() {
        indicatorView = new View(context);
        LayoutParams layoutParams = new LayoutParams((int) (mScreenWidth / indicatorEntityList.size() * 0.8f), radioGroupHeight / 10);
        layoutParams.setMargins((int) (mScreenWidth / indicatorEntityList.size() * 0.1f), -layoutParams.height, 0, 0);
        indicatorView.setLayoutParams(layoutParams);
        indicatorView.setBackgroundColor(indicator_color);
    }

    private RadioButton generateRadioButton(int index) {
        RadioButton rb = new RadioButton(context);
        // 均分宽度,TODO 不知道这里用 weight 为什么无法显示出来
        rb.setLayoutParams(new LayoutParams(mScreenWidth / indicatorEntityList.size(), ViewGroup.LayoutParams.MATCH_PARENT));
        rb.setId(index);
        rb.setGravity(Gravity.CENTER);
        rb.setText(indicatorEntityList.get(index).indicator);
        rb.setPadding(0, radioGroupHeight / 10, 0, radioGroupHeight / 10); // 设置上下边距,不至于挨着边
//        rb.setCompoundDrawablePadding(2); // 文本和图片的间距
        rb.setButtonDrawable(android.R.color.transparent); // 去掉圆圈圈
        return rb;
    }

    /** 处理tab切换以后的ui状态 */
    private void setSelectedItem(int position) {
        for (int i = 0; i < indicatorEntityList.size(); i++) {
            ((RadioButton) mRadioGroup.getChildAt(i)).setTextColor(i == position ? selected_fg_Color : unselected_fg_Color); // 字体颜色
            if (isShowIconEnable) {
                Drawable drawable = ContextCompat.getDrawable(context, i == position ? indicatorEntityList.get(i).selectedRes : indicatorEntityList.get(i).unSelectedRes);
                drawable.setBounds(0, 0, radioGroupHeight / 2, radioGroupHeight / 2);
                ((RadioButton) mRadioGroup.getChildAt(i)).setCompoundDrawables(null, drawable, null, null);// 图标
            } else {
                mRadioGroup.getChildAt(i).setBackgroundColor(i == position ? selected_bg_Color : unselected_bg_Color); // 背景颜色
            }
        }
        // 设置 indicatorView 动画 TODO indicatorView 动画是在 ViewPager 滑动动画完成后执行的,不是和 ViewPager 同步的
        if (indicatorView != null && lastSelectedIndex != position) {
            ObjectAnimator.ofFloat(indicatorView, "TranslationX", lastSelectedIndex * mScreenWidth / indicatorEntityList.size(), position * mScreenWidth / indicatorEntityList.size())
                    .setDuration(300).start();
            lastSelectedIndex = position;
        }
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        if (mViewPager != null) mViewPager.setCurrentItem(checkedId);
        setSelectedItem(checkedId);
    }

    private ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            setSelectedItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }
    };

    /**
     * 自定义ViewPager<p/>
     * Override onTouchEvent() 和  onInterceptTouchEvent()  是为了屏蔽滑动事件<br/>
     * Override setCurrentItem()  是为了在点击非相邻的tab时,避免中间滚动太多<br/>
     */
    class MyViewPager extends ViewPager {

        private Context context;
        private List<Fragment> mFragments;
        private MyFragmentPagerAdapter mAdapter;

        public MyViewPager(Context context) {
            this(context, null);
        }

        public MyViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
        }

        /**
         * 如果Activity不是继承 FragmentActivity,会crash掉
         *
         * @param fragments
         */
        public void setFragments(List<Fragment> fragments) {
            if (!(context instanceof FragmentActivity)) {
                throw new ClassCastException("activity need to extents FragmentActivity");
            }
            mAdapter = new MyFragmentPagerAdapter(((FragmentActivity) context).getSupportFragmentManager());
            this.setAdapter(mAdapter);
            mFragments = fragments;
            mAdapter.notifyDataSetChanged();
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            return scrollable && super.onTouchEvent(ev);
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return scrollable && super.onInterceptTouchEvent(ev);
        }

        @Override
        public void setCurrentItem(int item) {
            try {
                Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
                mFirstLayout.setAccessible(true);
                mFirstLayout.set(this, true);
                getAdapter().notifyDataSetChanged();
                super.setCurrentItem(item);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

            public MyFragmentPagerAdapter(FragmentManager fm) {
                super(fm);
            }

            @Override
            public Fragment getItem(int position) {
                return mFragments == null ? null : mFragments.get(position);
            }

            @Override
            public int getCount() {
                return mFragments == null ? 0 : mFragments.size();
            }
        }
    }
}

 

posted @ 2016-05-05 22:54  MarcoReus  阅读(807)  评论(0编辑  收藏  举报