zoukankan      html  css  js  c++  java
  • Android开发技巧——实现在图标文本底部导航栏(更新)

    本文参考了导航栏的代码viewpagerindicator实现。

    本文介绍了之前版本号qq或者微信,添加文本,实现图标,导航栏的底部。

    2014-09-14 13:59:42更新:library的代码已经从Demo中分离出来。见文末。

    本样例依赖viewpagerindicator的两个接口:IconPagerAdapter及PageIndicator。这两个接口的方法例如以下:

    package com.viewpagerindicator;
    
    public interface IconPagerAdapter {
        int getIconResId(int index);
        int getCount();
    }
    

    package com.viewpagerindicator;
    
    import android.support.v4.view.ViewPager;
    
    public interface PageIndicator extends ViewPager.OnPageChangeListener {
        void setViewPager(ViewPager view);
        void setViewPager(ViewPager view, int initialPosition);
        void <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(int item);
        void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
        void notifyDataSetChanged();
    }
    

    在本样例中,我把这两个类单独拿出来了。假设你的项目已经有依赖该库,则就不须要再去复制它们。

    以下先上两张效果图。

       

    在图中,上面的内容区域是viewpager,以下的是导航栏indicator。点击导航栏能够切换上面的页面。当然。滑动上面的页面以下的导航栏也能够切换。


    接着说一下它的实现。类的代码不复杂,大部分參照了viewpagerindicator中的TabPageIndicator类来实现,只是在这里我继承的是LinearLayout,代码例如以下:

    package com.githang.navigatordemo;
    
    import android.content.Context;
    import android.support.v4.view.PagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import com.viewpagerindicator.IconPagerAdapter;
    import com.viewpagerindicator.PageIndicator;
    
    
    import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
    
    /**
     * User: Geek_Soledad(msdx.android@qq.com)
     * Date: 2014-08-27
     * Time: 09:20
     * FIXME
     */
    public class IconTabPageIndicator extends LinearLayout implements PageIndicator {
        /**
         * Title text used when no title is provided by the adapter.
         */
        private static final CharSequence EMPTY_TITLE = "";
    
        /**
         * Interface for a callback when the selected tab has been reselected.
         */
        public interface OnTabReselectedListener {
            /**
             * Callback when the selected tab has been reselected.
             *
             * @param position Position of the current center item.
             */
            void onTabReselected(int position);
        }
    
        private Runnable mTabSelector;
    
        private final View.OnClickListener mTabClickListener = new View.OnClickListener() {
            public void onClick(View view) {
                TabView tabView = (TabView) view;
                final int oldSelected = mViewPager.getCurrentItem();
                final int newSelected = tabView.getIndex();
                mViewPager.<span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(newSelected, false);
                if (oldSelected == newSelected && mTabReselectedListener != null) {
                    mTabReselectedListener.onTabReselected(newSelected);
                }
            }
        };
    
        private final LinearLayout mTabLayout;
    
        private ViewPager mViewPager;
        private ViewPager.OnPageChangeListener mListener;
    
        private int mSelectedTabIndex;
    
        private OnTabReselectedListener mTabReselectedListener;
    
        private int mTabWidth;
    
        public IconTabPageIndicator(Context context) {
            this(context, null);
        }
    
        public IconTabPageIndicator(Context context, AttributeSet attrs) {
            super(context, attrs);
            setHorizontalScrollBarEnabled(false);
    
            mTabLayout = new LinearLayout(context, null, R.attr.tabPageIndicator);
            addView(mTabLayout, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    
        public void setOnTabReselectedListener(OnTabReselectedListener listener) {
            mTabReselectedListener = listener;
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
            final boolean lockedExpanded = widthMode == View.MeasureSpec.EXACTLY;
    
            final int childCount = mTabLayout.getChildCount();
    
            if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
                mTabWidth = MeasureSpec.getSize(widthMeasureSpec) / childCount;
            } else {
                mTabWidth = -1;
            }
    
            final int oldWidth = getMeasuredWidth();
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            final int newWidth = getMeasuredWidth();
    
            if (lockedExpanded && oldWidth != newWidth) {
                // Recenter the tab display if we're at a new (scrollable) size.
                <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(mSelectedTabIndex);
            }
        }
    
        private void animateToTab(final int position) {
            final View tabView = mTabLayout.getChildAt(position);
            if (mTabSelector != null) {
                removeCallbacks(mTabSelector);
            }
            mTabSelector = new Runnable() {
                public void run() {
                    final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
                    mTabSelector = null;
                }
            };
            post(mTabSelector);
        }
    
        @Override
        public void onAttachedToWindow() {
            super.onAttachedToWindow();
            if (mTabSelector != null) {
                // Re-post the selector we saved
                post(mTabSelector);
            }
        }
    
        @Override
        public void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (mTabSelector != null) {
                removeCallbacks(mTabSelector);
            }
        }
    
        private void addTab(int index, CharSequence text, int iconResId) {
            final TabView tabView = new TabView(getContext());
            tabView.mIndex = index;
            tabView.setOnClickListener(mTabClickListener);
            tabView.setText(text);
    
            if (iconResId > 0) {
                tabView.setIcon(iconResId);
            }
    
            mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1));
        }
    
        @Override
        public void onPageScrollStateChanged(int arg0) {
            if (mListener != null) {
                mListener.onPageScrollStateChanged(arg0);
            }
        }
    
        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
            if (mListener != null) {
                mListener.onPageScrolled(arg0, arg1, arg2);
            }
        }
    
        @Override
        public void onPageSelected(int arg0) {
            <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(arg0);
            if (mListener != null) {
                mListener.onPageSelected(arg0);
            }
        }
    
        @Override
        public void setViewPager(ViewPager view) {
            if (mViewPager == view) {
                return;
            }
            if (mViewPager != null) {
                mViewPager.setOnPageChangeListener(null);
            }
            final PagerAdapter adapter = view.getAdapter();
            if (adapter == null) {
                throw new IllegalStateException("ViewPager does not have adapter instance.");
            }
            mViewPager = view;
            view.setOnPageChangeListener(this);
            notifyDataSetChanged();
        }
    
        public void notifyDataSetChanged() {
            mTabLayout.removeAllViews();
            PagerAdapter adapter = mViewPager.getAdapter();
            IconPagerAdapter iconAdapter = null;
            if (adapter instanceof IconPagerAdapter) {
                iconAdapter = (IconPagerAdapter) adapter;
            }
            final int count = adapter.getCount();
            for (int i = 0; i < count; i++) {
                CharSequence title = adapter.getPageTitle(i);
                if (title == null) {
                    title = EMPTY_TITLE;
                }
                int iconResId = 0;
                if (iconAdapter != null) {
                    iconResId = iconAdapter.getIconResId(i);
                }
                addTab(i, title, iconResId);
            }
            if (mSelectedTabIndex > count) {
                mSelectedTabIndex = count - 1;
            }
            <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(mSelectedTabIndex);
            requestLayout();
        }
    
        @Override
        public void setViewPager(ViewPager view, int initialPosition) {
            setViewPager(view);
            <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(initialPosition);
        }
    
        @Override
        public void <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(int item) {
            if (mViewPager == null) {
                throw new IllegalStateException("ViewPager has not been bound.");
            }
            mSelectedTabIndex = item;
            mViewPager.<span style="BACKGROUND-COLOR: #ff9632">setCurrent</span>Item(item, false);
    
            final int tabCount = mTabLayout.getChildCount();
            for (int i = 0; i < tabCount; i++) {
                final View child = mTabLayout.getChildAt(i);
                final boolean isSelected = (i == item);
                child.setSelected(isSelected);
                if (isSelected) {
                    animateToTab(item);
                }
            }
        }
    
        @Override
        public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
            mListener = listener;
        }
    
        private class TabView extends LinearLayout {
            private int mIndex;
            private ImageView mImageView;
            private TextView mTextView;
    
            public TabView(Context context) {
                super(context, null, R.attr.tabView);
                View view = View.inflate(context, R.layout.tab_view, null);
                mImageView = (ImageView) view.findViewById(R.id.tab_image);
                mTextView = (TextView) view.findViewById(R.id.tab_text);
                this.addView(view);
            }
    
            @Override
            public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
                // Re-measure if we went beyond our maximum size.
                if (mTabWidth > 0) {
                    super.onMeasure(MeasureSpec.makeMeasureSpec(mTabWidth, MeasureSpec.EXACTLY),
                            heightMeasureSpec);
                }
            }
    
            public void setText(CharSequence text) {
                mTextView.setText(text);
            }
    
            public void setIcon(int resId) {
                if (resId > 0) {
                    mImageView.setImageResource(resId);
                }
            }
    
            public int getIndex() {
                return mIndex;
            }
        }
    }
    

    修改的地方主要是添加一个表示导航栏button宽度的变量,以及导航栏的view的实现。及两个onMeasure方法。因为在这里我继承的是LinearLayout,也就是当导航栏栏目较多时,不会通过左右滑动来显示或隐藏其它button。而是直接平分,该部分的代码例如以下:

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
            final boolean lockedExpanded = widthMode == View.MeasureSpec.EXACTLY;
    
            final int childCount = mTabLayout.getChildCount();
    
            if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
                mTabWidth = MeasureSpec.getSize(widthMeasureSpec) / childCount;
            } else {
                mTabWidth = -1;
            }
    
            final int oldWidth = getMeasuredWidth();
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            final int newWidth = getMeasuredWidth();
    
            if (lockedExpanded && oldWidth != newWidth) {
                // Recenter the tab display if we're at a new (scrollable) size.
                <span style="BACKGROUND-COLOR: #ffd700">setCurrent</span>Item(mSelectedTabIndex);
            }
        }
    

    当导航button大于1个时,直接平分。每一个导航button的宽度即为mTabWidth。

    然后重写TabView的onMeasure方法,当mTabWidth大于0时,设置它的宽度为mTabWidth,例如以下:

            @Override
            public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
                // Re-measure if we went beyond our maximum size.
                if (mTabWidth > 0) {
                    super.onMeasure(MeasureSpec.makeMeasureSpec(mTabWidth, MeasureSpec.EXACTLY),
                            heightMeasureSpec);
                }
            }

    在这里的TabView中。我则直接使用布局文件来写,上面是一个ImageView,以下是一个TextView,代码例如以下(tab_view.xml):

    <?

    xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:background="@android:color/white" android:gravity="center_horizontal" android:addStatesFromChildren="true" android:layout_height="wrap_content"> <ImageView android:id="@+id/tab_image" android:layout_width="27dp" android:layout_marginTop="2dp" android:adjustViewBounds="true" android:contentDescription="@null" android:layout_height="27dp" /> <TextView android:id="@+id/tab_text" android:layout_marginTop="2dp" android:gravity="center_horizontal|bottom" android:padding="2dp" android:layout_width="wrap_content" android:textColor="@color/tab_text_selector" android:textSize="12sp" android:layout_height="match_parent" /> </LinearLayout>


    再看TabView内部类的构造方法代码:

     private class TabView extends LinearLayout {
    
            public TabView(Context context) {
                super(context, null, R.attr.tabView);
                View view = View.inflate(context, R.layout.tab_view, null);
                mImageView = (ImageView) view.findViewById(R.id.tab_image);
                mTextView = (TextView) view.findViewById(R.id.tab_text);
                this.addView(view);
            }
    }

    TabView是继承自LinearLayout。然后通过布局文件tab_view获取一个view,并将它加到TabView其中。

    可是我们并未定义TabView本身的布局參数,所以加到它里面的view并非居中的,而是靠左。

    所以我们还须要设置这个TabView的參数,通过我们定义的属性R.attr.tabView。然后调用它父类的构造方法super(context, null, R.attr.tabView)。

    在IconTabPageIndicator的构造方法其中,你相同能够看到导航栏的容器——mTabLayout,相同是通过属性来创建的。例如以下代码:

        public IconTabPageIndicator(Context context, AttributeSet attrs) {
            super(context, attrs);
            setHorizontalScrollBarEnabled(false);
    
            mTabLayout = new LinearLayout(context, null, R.attr.tabPageIndicator);
            addView(mTabLayout, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }

    定义属性的方法例如以下,先在res/values下新建一个attrs.xml的文件,然后增加下面内容:

    <?xml version="1.0" encoding="utf-8"?

    > <resources> <declare-styleable name="TabView"> <attr name="tabPageIndicator" format="reference" /> <attr name="tabView" format="reference" /> </declare-styleable> </resources>


    即在这里声明两个属性。一个是tabPageIndicator,还有一个是tabView。它们都是引用类型的。

    但只这样还是不够的,由于我们不过声明了两个属性。并没有设定属性的详细内容,所以我们还须要在styles.xml文件其中设置我们的主题,代码例如以下(styles.xml):

      <style name="AppTheme" parent="Theme.AppCompat.Light">
            <item name="tabView">@style/TabView</item>
            <item name="tabPageIndicator">@style/TabIndicator</item>
        </style>
        <style name="TabIndicator"/>
        <style name="TabView">
            <item name="android:addStatesFromChildren">true</item>
            <item name="android:orientation">vertical</item>
            <item name="android:gravity">bottom|center_horizontal</item>
            <item name="android:layout_width">0dp</item>
            <item name="android:background">@android:color/white</item>
            <item name="android:layout_height">match_parent</item>
        </style>

    到此,我们的IconTabPageIndicator就实现好了。

    接下来在我们的程序中使用它:

    activity的布局文件(activity_my.xml):

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MyActivity">
    
        <com.githang.navigatordemo.IconTabPageIndicator
            android:id="@+id/indicator"
            android:layout_alignParentBottom="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <android.support.v4.view.ViewPager
            android:layout_above="@id/indicator"
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </RelativeLayout>
    

    fragment的布局文件(fragment.xml):

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:gravity="center"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:background="#eee"
        tools:context=".MyActivity">
    
        <TextView
            android:id="@+id/text"
            android:textAppearance="@android:style/TextAppearance.Large"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>
    

    MyActivity类的代码:

    package com.githang.navigatordemo;
    
    import android.os.Bundle;
    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.view.ViewPager;
    
    import com.viewpagerindicator.IconPagerAdapter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    public class MyActivity extends FragmentActivity {
    
        private ViewPager mViewPager;
        private IconTabPageIndicator mIndicator;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my);
    
            initViews();
        }
    
        private void initViews() {
            mViewPager = (ViewPager) findViewById(R.id.view_pager);
            mIndicator = (IconTabPageIndicator) findViewById(R.id.indicator);
            List<BaseFragment> fragments = initFragments();
            FragmentAdapter adapter = new FragmentAdapter(fragments, getSupportFragmentManager());
            mViewPager.setAdapter(adapter);
            mIndicator.setViewPager(mViewPager);
        }
    
        private List<BaseFragment> initFragments() {
            List<BaseFragment> fragments = new ArrayList<BaseFragment>();
    
            BaseFragment userFragment = new BaseFragment();
            userFragment.setTitle("用户");
            userFragment.setIconId(R.drawable.tab_user_selector);
            fragments.add(userFragment);
    
            BaseFragment noteFragment = new BaseFragment();
            noteFragment.setTitle("记事本");
            noteFragment.setIconId(R.drawable.tab_record_selector);
            fragments.add(noteFragment);
    
            BaseFragment contactFragment = new BaseFragment();
            contactFragment.setTitle("联系人");
            contactFragment.setIconId(R.drawable.tab_user_selector);
            fragments.add(contactFragment);
    
            BaseFragment recordFragment = new BaseFragment();
            recordFragment.setTitle("记录");
            recordFragment.setIconId(R.drawable.tab_record_selector);
            fragments.add(recordFragment);
    
            return fragments;
        }
    
        class FragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter {
            private List<BaseFragment> mFragments;
    
            public FragmentAdapter(List<BaseFragment> fragments, FragmentManager fm) {
                super(fm);
                mFragments = fragments;
            }
    
            @Override
            public Fragment getItem(int i) {
                return mFragments.get(i);
            }
    
            @Override
            public int getIconResId(int index) {
                return mFragments.get(index).getIconId();
            }
    
            @Override
            public int getCount() {
                return mFragments.size();
            }
    
            @Override
            public CharSequence getPageTitle(int position) {
                return mFragments.get(position).getTitle();
            }
        }
    
    }
    

    BaseFragment类的代码:

    package com.githang.navigatordemo;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    /**
     * User: Geek_Soledad(msdx.android@qq.com)
     * Date: 2014-08-27
     * Time: 09:01
     * FIXME
     */
    public class BaseFragment extends Fragment {
        private String title;
        private int iconId;
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public int getIconId() {
            return iconId;
        }
    
        public void setIconId(int iconId) {
            this.iconId = iconId;
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment, null, false);
            TextView textView = (TextView) view.findViewById(R.id.text);
            textView.setText(getTitle());
            return view;
        }
    }
    


    项目代码下载地址:http://zdz.la/xvS4Ab

    修订版下载地址:http://download.csdn.net/detail/maosidiaoxian/7913269

    git 代码地址:http://git.oschina.net/msdx/IconTabPageIndicator/tree/1.0

    最新代码已经将library的代码分离出来: CSDN门户

    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    vmware中三种网络连接方式
    【史上最全面经】各岗位汇总目录
    史上最全2019届秋招备战攻略
    程序员常用软件
    经典大数据面试题
    春招实习终于结束啦
    Java程序员自我介绍
    java一些面试题
    收割大厂offer需要具备的条件
    triangular distribution
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4834148.html
Copyright © 2011-2022 走看看