几经百度.COM周遭,依旧没找着小编期望的效果(标题),最终还是自己动手弄了个,就差支持导航栏水平滚动了。如标题论述,TabHost完全自定义,并定义每个Tab的点击监听效果,以及底部导航线的滑动拉长缩小的效果。此过程注意的只是Tab底下导航线的长度和位置控制,因为此自定义Tab文本内容长度不一致需要调整。下面将效果丢出来,再论述下代码的主要实现。
1.顶部Tab实现
顶部导航栏自定义Tab继承LinearLayout,其主要任务包括每个Tab标签的TextView以及底部导航线View的子视图添加。在init()方法中小编默认加上两个Tab标签,为的是在.xml引用的时候能够直接显示导航栏的呈现效果。而后还给每个Tab标签加上了点击监听,并自定义OnTabSelectListener回调监听将点击的事件分发给ViewPager,从而实现底下Fragment的切换。
public class TabHostNavView extends LinearLayout {
private Context mContext;
public View mLineV;
public LinearLayout mTabNavLly;
// 导航内容
public List<String> mNavNamesLs = new ArrayList<>();
// 引导线高度
private int mLineHeight = 5;
// 通用间隔
private int mDistance = 5;
public TabHostNavView(Context context) {
super(context);
this.mContext = context;
initView();
}
public TabHostNavView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initView();
}
public TabHostNavView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
}
public void setmNavNamesLs(List<String> mNavNamesLs) {
this.mNavNamesLs = mNavNamesLs;
initView();
}
public void initView() {
// 设置默认导航栏内容,以便xml引用直接查看效果
if (mNavNamesLs.isEmpty()) {
mNavNamesLs.add("TAB_ONE");
mNavNamesLs.add("TAB_TWO");
}
this.removeAllViews();
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
this.setLayoutParams(params);
this.setOrientation(VERTICAL);
mTabNavLly = new LinearLayout(mContext);
mTabNavLly.setLayoutParams(params);
mTabNavLly.setOrientation(HORIZONTAL);
for (int i = 0, k = mNavNamesLs.size(); i < k; i++) {
params = new LayoutParams(0, LayoutParams.WRAP_CONTENT, mNavNamesLs.get(i).length());
TextView mNavTv = new TextView(mContext);
mNavTv.setLayoutParams(params);
mNavTv.setText(mNavNamesLs.get(i));
mNavTv.setTextColor(getResources().getColor(i == 0 ?
R.color.blue : R.color.black));
mNavTv.setTextSize(14.0f);
mNavTv.setPadding(mDistance, mDistance, mDistance, mDistance);
mNavTv.setGravity(Gravity.CENTER);
mNavTv.setTag(i);
mNavTv.setOnClickListener(mOnclick);
mTabNavLly.addView(mNavTv);
}
this.addView(mTabNavLly);
if (mLineV == null) {
params = new LayoutParams(LayoutParams.MATCH_PARENT, mLineHeight);
mLineV = new View(mContext);
params.leftMargin = mDistance;
params.rightMargin = mDistance;
mLineV.setLayoutParams(params);
mLineV.setBackgroundColor(getResources().getColor(R.color.blue));
}
this.addView(mLineV);
}
/**
* @param position 刷新选中项
*/
public void refreshTab(int position) {
if (mTabNavLly == null || position < 0) return;
for (int i = 0, k = mTabNavLly.getChildCount(); i < k; i++) {
if (mTabNavLly.getChildAt(i) instanceof TextView) {
((TextView) mTabNavLly.getChildAt(i)).setTextColor(
getResources().getColor(i == position ? R.color.blue : R.color.black)
);
}
}
}
OnClickListener mOnclick = new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnTabSelectListener != null)
mOnTabSelectListener.onTabSelect((int) v.getTag());
}
};
private OnTabSelectListener mOnTabSelectListener;
public void setOnTabSelectListener(OnTabSelectListener listener) {
this.mOnTabSelectListener = listener;
}
public interface OnTabSelectListener {
void onTabSelect(int position);
}
}
2.ViewPager的切换监听
这里我们继承OnPageChangeListener实现Viewpager的滑动切换监听,并在内部onPageScrolled();函数当中实现对Tab底部导航线的位置控制。具体控制方案已经在代码中体现,表面上还是比较抽象的。另外,需要注意的有,引用该类的时候记得调用init();将上面的导航栏导进入(方便编辑Tab标签和导航线),且小编将ViewPager的适配器也一并写入其中,这样实现耦合度提高了,这个就根据个人需要检出吧。
public class TabHostPagerView extends ViewPager {
private Context mContext;
// 导航栏
private TabHostNavView mTabNav;
// 屏幕宽度
private float mScreenWidth;
public TabHostPagerView(Context context) {
super(context);
this.mContext = context;
}
public TabHostPagerView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
}
/**
* @param navView 关联导航栏视图
* @param fm Frame管理器
* @param list 展示Fragment集合
*/
public void init(TabHostNavView navView, FragmentManager fm, List<Fragment> list) {
this.mTabNav = navView;
this.setOnPageChangeListener(mPageChange);
this.mScreenWidth = DisplayUtil.getScreenWidth(mContext);
this.mTabNav.setOnTabSelectListener(onTabSelect);
if (list.isEmpty()) return;
this.setAdapter(new TabFragmentAdapter(fm, list));
}
/**
* 卡片内容切换监听
*/
OnPageChangeListener mPageChange = new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mTabNav == null) return;
int currentIndex = getCurrentItem();
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
mTabNav.mLineV.getLayoutParams();
int beforeWeight = 0;// 当前页左边内容(字体长度)所占水平位置上的权重
int selfWeight = 0;// 当前页本身(字体长度)所占水平位置上的权重
int afterWeight = 0;// 当前页右边本身(字体长度)所占水平位置上的权重
int lastWeight = 0;// 上一页本身(字体长度)所占水平位置上的权重
int nextWeight = 0;// 下一页本身(字体长度)所占水平位置上的权重
int sum = 0;// 水平位置上的总内容权重(总的字体长度)
for (int i = 0, k = mTabNav.mNavNamesLs.size(); i < k; i++) {
int length = mTabNav.mNavNamesLs.get(i).length();
sum += length;
if (i < currentIndex) {
beforeWeight += length;
if (i == currentIndex - 1) lastWeight = length;
} else if (i > currentIndex) {
afterWeight += length;
if (i == currentIndex + 1) nextWeight = length;
} else {
selfWeight = length;
}
}
if (currentIndex > position) {// 由左向右滑动
params.leftMargin = (int) ((positionOffset - 1) * mScreenWidth * lastWeight / sum
+ beforeWeight * (mScreenWidth / sum));
params.rightMargin = (int) ((1 - positionOffset) * mScreenWidth * selfWeight / sum
+ afterWeight * (mScreenWidth / sum));
} else {
params.leftMargin = (int) (positionOffset * mScreenWidth * selfWeight / sum
+ beforeWeight * (mScreenWidth / sum));
params.rightMargin = (int) (-positionOffset * mScreenWidth * nextWeight / sum
+ afterWeight * (mScreenWidth / sum));
}
mTabNav.mLineV.setLayoutParams(params);
}
@Override
public void onPageSelected(int position) {
if (mTabNav == null) return;
mTabNav.refreshTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
/**
* 导航栏点击项监听(自定义监听接口)
*/
TabHostNavView.OnTabSelectListener onTabSelect = new TabHostNavView.OnTabSelectListener() {
@Override
public void onTabSelect(int position) {
setCurrentItem(position);
}
};
/**
* 卡片内容适配器
*/
public static class TabFragmentAdapter extends FragmentPagerAdapter {
List<Fragment> fragmentList = new ArrayList<>();
public TabFragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
this.fragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
}
3.引用实例
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/space_main"
android:paddingRight="@dimen/space_main"
android:paddingTop="@dimen/space_min"
android:paddingBottom="@dimen/space_min"
android:background="@color/blue">
<include layout="@layout/include_search_anything" />
</LinearLayout>
<View style="@style/LineHorizontal" />
<路径.TabHostNavView
android:id="@+id/them_tab_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"/>
<路径.TabHostPagerView
android:id="@+id/them_vp_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
public class ThemFragmentActivity extends BaseFragmentActivity {
// 顶部导航
@ViewInject(R.id.them_tab_nav)
private TabHostNavView mTabNav;
// 卡片视图
@ViewInject(R.id.them_vp_view)
private TabHostPagerView mTabView;
private ThemHYFL_Fragment mFrameHYFL = new ThemHYFL_Fragment();
private ThemTJ_Fragment mFrameTJ = new ThemTJ_Fragment();
private ThemZD_Fragment mFrameZD = new ThemZD_Fragment();
private ThemZRM_Fragment mFrameZRM = new ThemZRM_Fragment();
private ThemZX_Fragment mFrameZX = new ThemZX_Fragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_them);
ViewUtils.inject(this);
initContent();
}
private void initContent() {
mTabNav.setmNavNamesLs(Arrays.asList(getResources().getStringArray(R.array.them_nav_name)));
Fragment[] fragments = {mFrameHYFL, mFrameTJ, mFrameZD, mFrameZRM, mFrameZX};
mTabView.init(mTabNav, getSupportFragmentManager(), Arrays.asList(fragments));
}
}
以上即为实现了自定义Tab导航栏以及ViewPager切换Fragment的效果,实现手段比较粗糙。之后需要跟进优化的是导航栏的水平移动和自动居中效果,Fragment内容ScrollView或者ListView的滑动兼容以及引用5.X谷歌上下拉刷新加载更多的特效。期待后面能对接上。