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();
}
}
}
}

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

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

    目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET.BMap.NET.WindowsForm以及BMap.NET.WinformDemo. BMap.NET 对百度地 ...

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

    [转][完全开源]百度地图Web service API C#.NET版,带地图显示控件.导航控件.POI查找控件 目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET ...

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

    config.xml文件的配置如下: <widget left="10" top="50" config="widgets/Navigation ...

  4. 设计一个 iOS 控件

    转载自:http://blog.csdn.net/zhangao0086/article/details/45622875 代码的等级:可编译.可运行.可测试.可读.可维护.可复用 前言 一个控件从外 ...

  5. openlayers4 入门开发系列之地图导航控件篇(附源码下载)

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

  6. Android 一个日历控件的实现代码

    转载  2017-05-19   作者:Othershe   我要评论 本篇文章主要介绍了Android 一个日历控件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看 ...

  7. 3ds Max 中的导航控件SteeringWheels入门介绍

    介绍 软件环境:3d Max2015 SteeringWheels 3D导航控件也可以说是“追踪菜单”,通过它们可以使用户从单一的工具访问不同的2D和3D导航工具.SteeringWheels可分成多 ...

  8. .net验证控件,导航控件

    一.客户端验证(用户体验,减少服务器端压力) 二.服务器端验证(防止恶意攻击,客户端js很容易被绕过) 验证控件:RequiredFieldValidator:字段必填:RangeValidator: ...

  9. Add an Item to the Navigation Control 将项目添加到导航控件

    In this lesson, you will learn how to add an item to the navigation control. For this purpose, the N ...

随机推荐

  1. Tomcat Context配置(转)

    <Context>元素的属性:path:指定访问该Web应用的URL入口.docBase:指定Web应用的文件路径,可以给定绝对路径,也可以给定相对于<Host>的appBas ...

  2. Review of Segmentation for Medical image analysis

    成像方法:X射线,CT,MRI,SPECT,PET等 分割的定义: Image segmentation is a procedure for extracting the region of int ...

  3. Nginx负载均衡和LVS负载均衡的比较分析

    LVS和Nginx都可以用作多机负载的方案,它们各有优缺,在生产环境中需要好好分析实际情况并加以利用. 首先提醒,做技术切不可人云亦云,我云即你云:同时也不可太趋向保守,过于相信旧有方式而等别人来帮你 ...

  4. Flume-ng+Kafka+storm的学习笔记

    Flume-ng Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统. Flume的文档可以看http://flume.apache.org/FlumeUserGuide.html ...

  5. [BS-08]注意Xcode自动提示好用但极易出错,务必看清方法的名称

    今日在写一个UIBarButtonItem的分类时,在Xcode自动提示时,因未仔细查看,错将需要用到setBackgroundImage的地方,选择成setImage,结果导致button的boun ...

  6. 入手Intel 750

    要装虚拟机,入了块intel750 测速: C盘闪迪至尊超级速ssd,保修10年 D盘 intel 750,保修5年 E盘 西数2T红盘 呵呵,还没有装intel驱动,就用的win10自带的nvme驱 ...

  7. JavaScript解决命名冲突的一种方法

    过程化编码 过程化编码, 表现为 定义若干函数,然后调用定义函数, 随着页面交互逻辑变化, 从简单到复杂, 定义的所有函数.和变量 都挂在 window对象上, window对象 编程者子自定义变量名 ...

  8. Fusioncharts使用说明

    背景 最近由于工作需要,再次接触到了Fusioncharts,但也有不足之处,现在官网上似乎是不支持flash的版本了,只能看到html5相关的javascript版本,无奈再次从网上搜索到了一些别人 ...

  9. 自动备份sqlexpress 数据库脚本

    Create PROCEDURE [dbo].[usp_BackupDatabase] @databaseName sysname,@backupPath nvarchar(255), @backup ...

  10. Java String.split()

    在java.lang包中有String.split()方法,返回是一个数组 我在应用中用到一些,给大家总结一下,仅供大家参考: 1.如果用“.”作为分隔的话,必须是如下写法,String.split( ...