自定义TabHost+Fragment+ViewPager

几经百度.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谷歌上下拉刷新加载更多的特效。期待后面能对接上。

更好的推荐:https://github.com/canyinghao/CanRefresh

你可能感兴趣的:(viewpager,Fragment,自定义Tab)