使用 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();
}
}
}
}
使用 ViewPager 和 RadioGroup 封装的一个导航控件的更多相关文章
- 【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件
目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET.BMap.NET.WindowsForm以及BMap.NET.WinformDemo. BMap.NET 对百度地 ...
- 【转】【完全开源】百度地图Web service API C#.NET版,带地图显示控件、导航控件、POI查找控件
[转][完全开源]百度地图Web service API C#.NET版,带地图显示控件.导航控件.POI查找控件 目录 概述 功能 如何使用 参考帮助 概述 源代码主要包含三个项目,BMap.NET ...
- 天津政府应急系统之GIS一张图(arcgis api for flex)讲解(四)地图导航控件模块
config.xml文件的配置如下: <widget left="10" top="50" config="widgets/Navigation ...
- 设计一个 iOS 控件
转载自:http://blog.csdn.net/zhangao0086/article/details/45622875 代码的等级:可编译.可运行.可测试.可读.可维护.可复用 前言 一个控件从外 ...
- openlayers4 入门开发系列之地图导航控件篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...
- Android 一个日历控件的实现代码
转载 2017-05-19 作者:Othershe 我要评论 本篇文章主要介绍了Android 一个日历控件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看 ...
- 3ds Max 中的导航控件SteeringWheels入门介绍
介绍 软件环境:3d Max2015 SteeringWheels 3D导航控件也可以说是“追踪菜单”,通过它们可以使用户从单一的工具访问不同的2D和3D导航工具.SteeringWheels可分成多 ...
- .net验证控件,导航控件
一.客户端验证(用户体验,减少服务器端压力) 二.服务器端验证(防止恶意攻击,客户端js很容易被绕过) 验证控件:RequiredFieldValidator:字段必填:RangeValidator: ...
- 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 ...
随机推荐
- php数组遍历
<?php $arr = array('a','b','c','d','e','f'); //for语句只能遍历索引数组 for($i = 0; $i < 6; $i++){ echo $ ...
- Volume rendering
Volume rendering Reconstruction filter UCDAVIS
- Flume-ng+Kafka+storm的学习笔记
Flume-ng Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统. Flume的文档可以看http://flume.apache.org/FlumeUserGuide.html ...
- SQL Server翻译目录
从SQLServerCentral翻译部分Stairways文章,设置目录方便阅读(2015-12更新)SQL Server代理系列第一篇 SQL Server代理概述第二篇 SQL Server代理 ...
- Silverlight打印注意事项
1.Silverlight的打印功能从版本5开始才支持矢量打印,这不但要求打印机支持矢量打印,而且还要安装相应的打印驱动程序. 测试你的打印机是否支持矢量打印,可以参考:如何用C#代码检测打印机和驱动 ...
- iOS如何统计渠道
http://bbs.umeng.com/thread-10-1-1.html https://www.zhihu.com/question/20697933
- Ajax.ActionLink()方法的使用
第一句话都会这么去写:程序猿就是苦逼,除了开发还要会写博文!哎,今天就和大家一起讨论下Ajax的辅助方法ActionLink的使用,如果有讲的不好的地方或错的地方,请大家务必扔板砖,要投准哦,砸死我算 ...
- java 笔记(5) —— 线程,yield,join
一.线程各个状态与转换: 新建状态:用new语句创建的线程对象处于新建状态,此时它和其它的java对象一样,仅仅在堆中被分配了内存 .就绪状态:当一个线程创建了以后,其他的线程调用了它的start() ...
- Tomcat系列之Java技术详解
一.概述 1.前言 在前面几篇博客中,我们和大家说了负载均衡器服务器.Web服务器.反向代理服务器.缓存服务器,从这篇博客开始我们和大家说说应用程序服务器,对于上述内容不了解的博友可以去参考一下我们前 ...
- java Servlet中的过滤器Filter
web.xml中元素执行的顺序listener->filter->struts拦截器->servlet. 1.过滤器的概念 Java中的Filter 并不是一个标准的Servlet ...