zoukankan      html  css  js  c++  java
  • Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航)

    我记得在前面,我写了一篇Android 微信6.1 tab栏图标和字体颜色渐变的实现,如果大家仅仅认为这篇文章的功能只是模仿微信颜色渐变效果,那就大错特错了!认真阅读了这篇文章的朋友,应该知道,这里面代码可用作 app 通用的底部栏导航,通过它能快速的实现类似微信6.0版本以底部导航的 app 整体架构,并且在 MainActivity 中需要编写的代码非常简洁。如果有兴趣的朋友可以去看看。

    效果:

    今天这篇 blog的内容同样可以拿来做 app 的整体架构,但与前面那篇 blog 不同,不同之处是前面那篇文章所讲的内容可用作底部导航,而这篇 blog 的内容,是用作顶部导航,老版本的微信就是此效果,ok,来看看效果图 
    这里写图片描述

    实现原理

    根据效果图,不难分析,可以通过自定义 ViewGroup 来实现,但这样代码量偏多,看了我的前面 blog 的朋友应该清楚,这里最好的实现方式是通过重写 LinearLayout,相比通过 ViewGroup 来实现,省略了测量onMeasure()和布局onLayout()方法的实现,因为这些LinearLayout已经帮我们实现好了,而我们真正要做的就是为 LinearLayout 填充内容,填充内容可大致可分为以下四个步骤:

    1、填充每个 item 的内容

    2、绘制每个 item 之间的分割线

    3、绘制底部线条

    4、绘制指示器的内容

    代码实现

    1、先把需要用的属性定义出来 
    需要的属性 
    1、页卡指示器的颜色 
    2、分割线的颜色 
    3、底部线条的颜色 
    4、页卡指示器的高度 
    5、分割线距离上下边距的距离 
    6、分割线的宽度

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <attr name="indicatorColor" format="color"/>
        <attr name="dividerColor" format="color"/>
        <attr name="bottomLineColor" format="color"/>
        <attr name="dividerMargin" format="dimension"/>
        <attr name="indicatorHeight" format="dimension"/>
        <attr name="bottomLineHeight" format="dimension"/>
        <attr name="dividerWidth" format="dimension"/>
        <declare-styleable name="SlidingTabLayout">
            <attr name="indicatorColor"/>
            <attr name="dividerColor"/>
            <attr name="bottomLineColor"/>
            <attr name="dividerMargin"/>
            <attr name="indicatorHeight"/>
            <attr name="bottomLineHeight"/>
            <attr name="dividerWidth"/>
        </declare-styleable>
    </resources>

    2、代码中获取属性,并附上相应的默认值

        /*默认的页卡颜色*/
        private final int DEFAULT_INDICATOR_COLOR = 0xffff00ff;
        /*默认分割线的颜色*/
        private final int DEFAULT_DIVIDER_COLOR = 0xff000000;
        /*默认title字体的大小*/
        private final int DEFAULT_TEXT_SIZE = 16;
        /*默认padding*/
        private final int DEFAULT_TEXT_PADDING = 16;
        /*divider默认的宽度*/
        private final int DEFAULT_DIVIDER_WIDTH = 1;
        /*indicator 的高度*/
        private final int DEFAULT_INDICATOR_HEIGHT = 5;
        /*底部线条的高度默认值*/
        private final int DEFAULT_BOTTOM_LINE_HEIGHT = 2;
        /*分割线距离上下边缘的距离默认为8*/
        private final int DEFAULT_DIVIDER_MARGIN = 8;
        /*底部线条的颜色默认值*/
        private final int DEFAULT_BOTTOM_LINE_COLOR = 0xff000000;
            /*获取TypedArray*/
            TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.SlidingTabLayout);
            /*获取自定义属性的个数*/
            int N = typedArray.getIndexCount();
            Log.v("zgy","=========getIndexCount========="+N) ;
            for (int i = 0; i < N; i++) {
                int attr = typedArray.getIndex(i);
                switch (attr) {
                    case R.styleable.SlidingTabLayout_indicatorColor:
                        /*获取页卡颜色值*/
                        mIndicatorColor = typedArray.getColor(attr, DEFAULT_INDICATOR_COLOR);
                        break;
                    case R.styleable.SlidingTabLayout_dividerColor:
                        /*获取分割线颜色的值*/
                        mDividerColor = typedArray.getColor(attr, DEFAULT_DIVIDER_COLOR);
                        break;
                    case R.styleable.SlidingTabLayout_bottomLineColor:
                        /*获取底部线条颜色的值*/
                        mBottomLineColor = typedArray.getColor(attr, DEFAULT_BOTTOM_LINE_COLOR);
                        break;
                    case R.styleable.SlidingTabLayout_dividerMargin:
                        /*获取分割线的距离上线边距的距离*/
                        mDividerMargin = (int) typedArray.getDimension(attr, DEFAULT_DIVIDER_MARGIN * getResources().getDisplayMetrics().density);
                        Log.v("zgy","=========mDividerMargin========="+mDividerMargin) ;
                        break;
                    case R.styleable.SlidingTabLayout_indicatorHeight:
                        /*获取页卡的高度*/
                        mIndicatorHeight = (int) typedArray.getDimension(attr, DEFAULT_INDICATOR_HEIGHT * getResources().getDisplayMetrics().density);
                        break;
                    case R.styleable.SlidingTabLayout_bottomLineHeight:
                        /*获取底部线条的高度*/
                        mBottomLineHeight = (int) typedArray.getDimension(attr, DEFAULT_BOTTOM_LINE_HEIGHT * getResources().getDisplayMetrics().density);
                        break;
                    case R.styleable.SlidingTabLayout_dividerWidth:
                        /*获取分割线的宽度*/
                        mDividerWidth = (int) typedArray.getDimension(attr, DEFAULT_DIVIDER_WIDTH * getResources().getDisplayMetrics().density);
                        break;
                }
            }
            /*释放TypedArray*/
            typedArray.recycle();

    这里说一个细节,我经常在这里获取属性的时候习惯性的把

    case R.styleable.SlidingTabLayout_indicatorColor:

    写成

    case R.attr.indicatorColor:

    而且这里不会报错,不过结果可想而知,无法获取我们设置的属性内容,这里如果有跟我犯同样错误的朋友记得注意一下。

    3、为 LinearLayout 填充内容

        /**
         * 设置viewPager,初始化SlidingTab,
         * 在这个方法中为SlidingLayout设置
         * 内容,
         *
         * @param viewPager
         */
        public void setViewPager(ViewPager viewPager) {
            /*先移除所以已经填充的内容*/
            removeAllViews();
            /* viewPager 不能为空*/
            if (viewPager == null) {
                throw new RuntimeException("ViewPager不能为空");
            }
            mViewPager = viewPager;
            mViewPager.setOnPageChangeListener(new InternalViewPagerChange());
            //填充内容
            populateTabLayout();
        }
        /**
         * 填充layout,设置其内容
         */
        private void populateTabLayout() {
            final PagerAdapter adapter = mViewPager.getAdapter();
            final OnClickListener tabOnClickListener = new TabOnClickListener();
            mItemName = (TabItemName) adapter;
            for (int i = 0; i < adapter.getCount(); i++) {
                TextView textView = createDefaultTabView(getContext());
                textView.setOnClickListener(tabOnClickListener);
                textView.setText(mItemName.getTabName(i));
                addView(textView);
            }
        }

    根据ViewPager中页面的个数,填充相应的 tab。

    4、绘制相应的内容 
    绘制内容,肯定在 onDraw()方法中 
    绘制底部线条

    canvas.drawRect(0,height - mBottomLineHeight,getWidth(),height,mBottomPaint);

    绘制分割线

     for (int i = 0; i < getChildCount() - 1; i++) {
          View child = getChildAt(i);
          canvas.drawLine(child.getRight(), mDividerMargin,child.getRight(), height - mDividerMargin,mDividerPaint);
            }

    重点:绘制滑动页卡

            /*当前页面的View tab*/
            View selectView = getChildAt(mSelectedPosition);
            /*计算开始绘制的位置*/
            int left = selectView.getLeft();
            /*计算结束绘制的位置*/
            int right = selectView.getRight();
            if (mSelectionOffset > 0) {
                View nextView = getChildAt(mSelectedPosition + 1);
                /*如果有偏移量,重新计算开始绘制的位置*/
                left = (int) (mSelectionOffset * nextView.getLeft() + (1.0f - mSelectionOffset) * left);
                /*如果有偏移量,重新计算结束绘制的位置*/
                right = (int) (mSelectionOffset * nextView.getRight() + (1.0f - mSelectionOffset) * right);
            }
            /*绘制滑动的页卡*/
            canvas.drawRect(left, height - mIndicatorHeight, right, height, mIndicatorPaint);

    运用案例

    为了体现他的简洁之处,这里把 xml 中的代码也贴出来

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"               xmlns:tools="http://schemas.android.com/tools"               xmlns:zgy="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    tools:context=".MainActivity">
        <demo.slidingtablayout.view.SlidingTabLayout
            android:id="@+id/id_tab"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            zgy:bottomLineColor="#AEAEAE"
            zgy:dividerMargin="15dp"
            zgy:indicatorColor="#77e69c"
            zgy:indicatorHeight="5dp"
            zgy:bottomLineHeight="2dp"
            android:background="#eeeeee">
        </demo.slidingtablayout.view.SlidingTabLayout>
    
        <android.support.v4.view.ViewPager
            android:layout_below="@+id/id_tab"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/id_view_pager"/>
    </RelativeLayout>

    MainActivity.java

    public class MainActivity extends ActionBarActivity {
    
        /*viewPager*/
        private ViewPager mViewPager ;
        /*自定义的 tabLayout*/
        private SlidingTabLayout mTabLayout ;
        /*每个 tab 的 item*/
        private List<PagerItem> mTab = new ArrayList<>() ;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mViewPager = (ViewPager) findViewById(R.id.id_view_pager) ;
            mTabLayout = (SlidingTabLayout) findViewById(R.id.id_tab) ;
            mTab.add(new PagerItem("tab1","FirstPager")) ;
            mTab.add(new PagerItem("tab2","SecondPager")) ;
            mTab.add(new PagerItem("tab3","ThirdPager")) ;
            mTab.add(new PagerItem("tab4","FourthPager")) ;
            mViewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
            /*需要先为 viewpager 设置 adapter*/
            mTabLayout.setViewPager(mViewPager);
        }
    
     private class ViewPagerAdapter extends FragmentPagerAdapter implements SlidingTabLayout.TabItemName{
    
            public ViewPagerAdapter(FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public Fragment getItem(int position) {
                return mTab.get(position).createFragment();
            }
    
            @Override
            public int getCount() {
                return mTab.size();
            }
    
            @Override
            public String getTabName(int position) {
                return mTab.get(position).getTitle();
            }
        }
    }

    点击下载源码

     
  • 相关阅读:
    IS-IS协议的内容
    OSPF协议---进阶篇
    OSPF协议-summary-LSA(LSA-3)
    OSPF协议-外部路由(LSA-4和LSA-5)
    OSPF的特殊区域和其他特性
    BGP的反射器和联盟
    neo4j 初级使用笔记
    Flink窗口介绍及应用
    HDFS之append数据到已存在文件中
    Random Projection在k-means的应用
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654237.html
Copyright © 2011-2022 走看看