使用 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();
            }
        }
    }
}
时间: 2024-08-01 06:32:45

使用 ViewPager 和 RadioGroup 封装的一个导航控件的相关文章

【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件

目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET.BMap.NET.WindowsForm以及BMap.NET.WinformDemo. BMap.NET 对百度地图Web Service API 的一些封装,每个接口返回的都是JObject类型(参见Json.NET): BMap.NET.WindowsForm(开始少写了一个s ,后来一直没改) 提供一系列可以在Winform中使用的控件,包括地图显示控件.导航控件.POI查找控件等等: BMap.NET.

设计一个 iOS 控件

代码的等级:可编译.可运行.可测试.可读.可维护.可复用 前言 一个控件从外在特征来说,主要是封装这几点: 交互方式 显示样式 数据使用 对外在特征的封装,能让我们在多种环境下达到 PM 对产品的要求,并且提到代码复用率,使维护工作保持在一个相对较小的范围内:而一个好的控件除了有对外一致的体验之外,还有其内在特征: 灵活性 低耦合 易拓展 易维护 通常特征之间需要做一些取舍,比如灵活性与耦合度,有时候接口越多越能适应各种环境,但是接口越少对外产生的依赖就越少,维护起来也更容易.通常一些前期看起来

xamarin-forms (一) 建一个独立控件库,使控件独立出来,让项目分离开来。

大家在写xamarin forms应用的时候,可能需要写一个独立的类库,去封装控件.然后提供给其他的项目中的xaml引用.类似于这种样子 这个myLabel就是我自己封装的一个Label.... 在说这个控件怎么封装之前,先介绍我的一个发现:这种方式不仅仅可以应用于控件的封装,,,,用这种方式可以封装一切 资源,提供给其他项目使用.. 先从封装一个最简单的计算类说起 首先:建一个独立的控件项目,并写一个静态类,类名要个命名空间额最后一个单词一样,里面要有一个静态方法Init 其次:我建一个类,类

南沙政府应急系统之GIS一张图(arcgis api for flex)讲解(四)地图导航控件模块

config.xml文件的配置如下: <widget left="10" top="50" config="widgets/Navigation/NavigationWidget.xml" url="widgets/Navigation/NavigationWidget.swf" /> 源代码目录如下: 地图导航控件模块的源代码原理解析,详细的代码在下载的开源flexviewer自带的: 1.地图缩小 2.地图放大

自定义百度地图导航控件

百度地图js版 的导航控件按钮,在webapp有几点问题 1 不好点击,也就是点不中 2 不能置灰,也就是说当缩小到最小的时候,按钮应该为灰色,反之放大到最大也该为灰色 基于以上2点,自定一下,其实很简单,目的在于可以自己来控制 代码如下 1 /** 2 *@ NavControl 3 */ 4 var NavControl = 5 /** 6 * NavControl 7 * @class 8 * @constructor 9 * @param {Map} map 地图的一个实例. 10 *

openlayers入门开发系列之地图导航控件篇

本篇的重点内容是利用openlayers来实现了地图导航控件功能,效果图如下: 实现思路如下: 创建一个地图控件基类,除了本篇的地图导航控件继承这个地图控件基类之外,后续的其他地图控件也是继承该基类 地图控件基类中创建地图导航控件函数 地图导航控件类 最后,地图导航控件初始化调用 详细的实现过程见:这里 原文地址:https://www.cnblogs.com/giserhome/p/9550661.html

MVC中如何将后台封装好的Web控件发到前台视图显示

最近在用MVC开发的时候,师姐说所有用到的控件都要写在后台,然后从后台发到前台,这样就改变了以前把页面做死了的缺点.但是在实现的时候,遇到了点儿问题,如图: 上面代码写了一个LinkButton,当调用这个静态方法后,会生成一个LinkButton的HTML代码,这个是比较简单的元件,如果,现在我要拼接一个功能齐全的DataGridView控件,要这个控件有分页栏,各种添加删除功能,CSS样式,为了复用我们的EasyUI元件库,估计这个HTML代码也得有半页多吧.如果我要在每次遇到参数的时候都要

一个WPF控件 诡异的MouseEvent 。

背景: private System.Windows.Controls.Border _borderTouch; private bool _mouseDown = false;  private System.Windows.Point _currentPoint = new System.Windows.Point(0, 0);    private System.Windows.Point _lastPoint = new System.Windows.Point(0, 0); 一个wpf

appium+python:自己写的一个滑动控件的方式

#调用方式roll_ele("ID","ele_id","7","up",3)#将控件分为7格,从底部倒数第二格向上滑动3格#滑动控件的方法def roll_ele(id,ele_id,size,direct,rollsize=1): """ 参数id为定位方式,eled_id为具体控件的id或xpath等,size为需要把该控件分成几格,direct为滑动方向,up或down(上或下),roll