Android-ViewPagerIndicator使用:--TabPageIndicator浅析

白智
2023-12-01

Android-ViewPagerIndicator使用:--TabPageIndicator浅析

TabPageIndicator这个类,可以加入图标进行切换,并且可以点击切换,可以形成放微信左右切换的效果。

  • 首先:进行布局xml
      <LinearLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">
          <!-- 注意:indicator and ViewPager 要挨在一起 -->
          <android.support.v4.view.ViewPager
              android:id="@+id/maintab_pager"
              android:layout_width="fill_parent"
              android:layout_height="0dp"
              android:layout_weight="1"
              />
           <com.viewpagerindicator.TabPageIndicator
                 android:id="@+id/maintab_indicator"
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 /> 
      </LinearLayout>
    
  • 在MainTabsWithIcons的oncreate中使用:

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          Common.mainContext=this;    //獲取主函數的context,為了fragment里的handler
          setContentView(R.layout.activity_page_tabs);
    
          FragmentPagerAdapter adapter = new GoogleMusicAdapter(getSupportFragmentManager());
    
          ViewPager pager = (ViewPager)findViewById(R.id.maintab_pager);
          pager.setOffscreenPageLimit(4);    //缓存4个页面
          pager.setAdapter(adapter);
    
          TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.maintab_indicator);
          indicator.setViewPager(pager);    //把indicator和viewpager的两者绑定在一起
          //监听ViewPager中包含的Fragment的改变(手滑动切换了页面)
          indicator.setOnPageChangeListener(new OnPageChangeListener() {
    
              @Override
              public void onPageSelected(int arg0) {
              }
    
              @Override
              public void onPageScrolled(int arg0, float arg1, int arg2) {
              }
    
              @Override
              public void onPageScrollStateChanged(int arg0) {
              }
          });
      }
    

    上面的代码注意三点:

    • pager.setOffscreenPageLimit(4); //缓存4个页面,有效的防止了卡顿现象
    • FragmentPagerAdapter的使用,以viewpager的方式来显示页面,并注意destroyItem方法,用以销毁显示在viewpager上的fragment视图,重写此方法,也可以达到防止卡顿现象
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
        // 这里Destroy的是Fragment的视图层次,并不是Destroy Fragment对象 
        //super.destroyItem(container, position, object);
        }
      
      最后加上一点防止卡顿现象,在androidmanifest.xml文件上的application中加入android:hardwareAccelerated="true"开启硬件加速
    • setOnPageChangeListener用法--》监听ViewPager中包含的Fragment的改变(手滑动切换了页面)
  • MainTabsWithIcons资源中引用图标的设置
      <!-- 选择时的图片 -->
      <item android:state_selected="true" android:drawable="@drawable/tab_selected_news"/>
      <!-- 默认时的图片 -->
      <item android:drawable="@drawable/tab_default_news"/>
    
  • TestFragment中依靠传进来的第几个子项,进行创建第几个fragment
      switch (currentNum) {
      case 0:
           mFragment=new InformationFragment();
          break;
      case 1:
           mFragment=new MapFragment();
          break;
      case 2:
           mFragment=new BBSFragment();
          break;
      case 3:
           mFragment=new MineFragment();
          break;
      default:
           mFragment=new InformationFragment();
          break;
      }
    
  • 分析最关键的TabPageIndicator类,进行理解,并修改,变成符合自己的要求

    • 继承了HorizontalScrollView,这个是水平滚动的view。和scrollview垂直滚动
    • 关注onMeasure()测量尺寸的方法。
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Log.d("onMeaureS", "widthMeasureSpec=" + widthMeasureSpec+",heightMeasureSpec="+heightMeasureSpec);
            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
            setFillViewport(lockedExpanded);
            final int childCount = mTabLayout.getChildCount();
            if (childCount > 1
                    && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
                if (childCount > 2) {
                    mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) / 4f);//* 0.4f 进行合理的分配,可以跨屏。/4 分成4块,将所有的挤在一起
                } else {
                    mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
                    Log.d("onMeaureF", "mMaxTabWidth=" + mMaxTabWidth
                            + ",widthMode=" + widthMode);
                }
            } else {
                mMaxTabWidth = -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.
                setCurrentItem(mSelectedTabIndex);
            }
        }
      
      其中:
      mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) / 4f);//* 0.4f 进行合理的分配,可以跨屏。/4 分成4块,将所有的挤在一起,形成微信的效果
      
    • addTab()方法,这是修改tab的大小设置标题设置位置等。
      private void addTab(int index, CharSequence text, int iconResId,int count) {

        final TabView tabView = new TabView(getContext());
        tabView.mIndex = index;
        tabView.setFocusable(true);
        tabView.setOnClickListener(mTabClickListener);
        //tabView.setText(text);
        if (iconResId != 0) {
            //自己设定图标的大小
            Drawable myIconBg = getResources().getDrawable(iconResId);
      
            DisplayMetrics dm = getResources().getDisplayMetrics();    //得到屏幕的大小,android 4.2好像有问题
            if(dm.widthPixels!=0){
                int currentWidth=dm.widthPixels/count;    //宽:依照有n块进行分割
                int currentHeight=currentWidth*dm.widthPixels/dm.heightPixels;//高:依照手机的屏幕比例进行分割
                myIconBg.setBounds(0,0,currentWidth,currentHeight);
            }
            else{
                myIconBg.setBounds(0,0,120,80);
            }
            //上、下、左、右设置图标
            tabView.setCompoundDrawables(myIconBg, null, null, null);
            //tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);    //依靠图标的大小来决定
        }
        //指定高、宽、权重
        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
                WRAP_CONTENT, 1));
      

      }
      这里涉及到三个知识点:

      • 如何得到屏幕的分别率【DisplayMetrics
      • 对于view的setCompoundDrawablessetCompoundDrawablesWithIntrinsicBounds两者的用法,以及区别

      • LayoutParams的用法,动态自定义控件并设置大小和相应的属性

  • 最后附加上涉及到相关类的完整代码
MainTabsWithIcons类:
package cn.hclab.PageIndicator;

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 android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.ViewGroup;
import cn.hclab.activity.Common;
import cn.hclab.sgu.R;

import com.viewpagerindicator.IconPagerAdapter;
import com.viewpagerindicator.TabPageIndicator;

public class MainTabsWithIcons extends FragmentActivity {
	//标识的标签文字
    private static final String[] CONTENT = new String[] { "Calendar", "Camera", "Alarms", "Location" };
    private static final int[] ICONS = new int[] {
            R.drawable.tab_news,
            R.drawable.tab_map,
            R.drawable.tab_talk,
            R.drawable.tab_mine,
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Common.mainContext=this;	//獲取主函數的context,為了fragment里的handler
        setContentView(R.layout.activity_page_tabs);

        FragmentPagerAdapter adapter = new GoogleMusicAdapter(getSupportFragmentManager());

        ViewPager pager = (ViewPager)findViewById(R.id.maintab_pager);
        pager.setOffscreenPageLimit(4);	//缓存4个页面
        pager.setAdapter(adapter);

        TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.maintab_indicator);
        indicator.setViewPager(pager);	//把indicator和viewpager的两者绑定在一起
        //监听ViewPager中包含的Fragment的改变(手滑动切换了页面)
        indicator.setOnPageChangeListener(new OnPageChangeListener() {
			
			@Override
			public void onPageSelected(int arg0) {
			}
			
			@Override
			public void onPageScrolled(int arg0, float arg1, int arg2) {
			}
			
			@Override
			public void onPageScrollStateChanged(int arg0) {
			}
		});
    }
    //使用FragmentPagerAdapter,使得Fragment会一直在内存中,不会被销毁,但它的视图层次是会被销毁的
    class GoogleMusicAdapter extends FragmentPagerAdapter implements IconPagerAdapter {
        public GoogleMusicAdapter(FragmentManager fm) {
            super(fm);
        }
//4.
        @Override
        public Fragment getItem(int position) {
            return TestFragment.newInstance(CONTENT[position % CONTENT.length],position);
        }
        /**2.
         * 继承PagerAdapter或PagerAdapter子类的时候需要实现这个方法,用于设置标题
         */
        @Override
        public CharSequence getPageTitle(int position) {
            
            Log.d("getPageTitle", "position"+position);
            return CONTENT[position % CONTENT.length].toUpperCase();
        }
        /**
         * 返回标签的图片
         */
        @Override 
        public int getIconResId(int index) {
          return ICONS[index];
        }
        //1.
        @Override
        public int getCount() {
          return CONTENT.length;// 代表页数
        }
        @Override
	    public void destroyItem(ViewGroup container, int position, Object object) {
        // 这里Destroy的是Fragment的视图层次,并不是Destroy Fragment对象 
	    super.destroyItem(container, position, object);
	    }
    }
}

TestFragment类:
package cn.hclab.PageIndicator;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import cn.hclab.fragment.BBSFragment;
import cn.hclab.fragment.InformationFragment;
import cn.hclab.fragment.MapFragment;
import cn.hclab.fragment.MineFragment;
/*
 * 所有ViewPager上真正显示的视图。
 */
public final class TestFragment extends Fragment {
    private static final String KEY_CONTENT = "TestFragment:Content";
    public int currentNum;
	public static Fragment newInstance(String content,int currentNum) {
		Fragment mFragment=null;
		Log.d("currentNum=",currentNum+"");
		switch (currentNum) {
		case 0:
			 mFragment=new InformationFragment();
			break;
		case 1:
			 mFragment=new MapFragment();
			break;
		case 2:
			 mFragment=new BBSFragment();
			break;
		case 3:
			 mFragment=new MineFragment();
			break;
		default:
			 mFragment=new InformationFragment();
			break;
		}

        return mFragment;
    }
	public TestFragment(){
		
	}
    private String mContent = "???";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if ((savedInstanceState != null) && savedInstanceState.containsKey(KEY_CONTENT)) {
            mContent = savedInstanceState.getString(KEY_CONTENT);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    		Bundle savedInstanceState) {
    	return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(KEY_CONTENT, mContent);
    }
}

TabPageIndicator类:
/*
 * Copyright (C) 2011 The Android Open Source Project
 * Copyright (C) 2011 Jake Wharton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.viewpagerindicator;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import cn.hclab.sgu.R;

/**
 * This widget implements the dynamic action bar tab behavior that can change
 * across different configurations or circumstances.
 */
public class TabPageIndicator extends HorizontalScrollView 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 OnClickListener mTabClickListener = new OnClickListener() {
		public void onClick(View view) {
			TabView tabView = (TabView) view;
			final int oldSelected = mViewPager.getCurrentItem();
			final int newSelected = tabView.getIndex();
			mViewPager.setCurrentItem(newSelected);
			if (oldSelected == newSelected && mTabReselectedListener != null) {
				mTabReselectedListener.onTabReselected(newSelected);
			}
		}
	};

	private final IcsLinearLayout mTabLayout;

	private ViewPager mViewPager;
	private ViewPager.OnPageChangeListener mListener;//監聽頁面的改變

	private int mMaxTabWidth;
	private int mSelectedTabIndex;

	private OnTabReselectedListener mTabReselectedListener;
	//构造函数
	public TabPageIndicator(Context context) {
		this(context, null);
	}
	//1.
	public TabPageIndicator(Context context, AttributeSet attrs) {
		super(context, attrs);
		setHorizontalScrollBarEnabled(false);

		mTabLayout = new IcsLinearLayout(context,
				R.attr.vpiTabPageIndicatorStyle);
		addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT,
				MATCH_PARENT));
	}

	public void setOnTabReselectedListener(OnTabReselectedListener listener) {
		mTabReselectedListener = listener;
	}
	//9.
	@Override
	public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		Log.d("onMeaureS", "widthMeasureSpec=" + widthMeasureSpec+",heightMeasureSpec="+heightMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
		setFillViewport(lockedExpanded);

		final int childCount = mTabLayout.getChildCount();
		if (childCount > 1
				&& (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
			if (childCount > 2) {
				mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) / 4f);//* 0.4f 进行合理的分配,可以跨屏。/4 分成4块,将所有的挤在一起
			} else {
				mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
				Log.d("onMeaureF", "mMaxTabWidth=" + mMaxTabWidth
						+ ",widthMode=" + widthMode);
			}
		} else {
			mMaxTabWidth = -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.
			setCurrentItem(mSelectedTabIndex);
		}
	}
	//6.
	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;
				smoothScrollTo(scrollPos, 0);
				mTabSelector = null;
			}
		};
		post(mTabSelector);
	}
	//8.
	@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);
		}
	}
	/*4.
	 * text 标签的名字
	 * iconResId 图标资源的id
	 */
	private void addTab(int index, CharSequence text, int iconResId,int count) {
		final TabView tabView = new TabView(getContext());
		tabView.mIndex = index;
		tabView.setFocusable(true);
		tabView.setOnClickListener(mTabClickListener);
//		tabView.setText(text);
		
		if (iconResId != 0) {
			//自己设定图标的大小
			Drawable myIconBg = getResources().getDrawable(iconResId);
			
			DisplayMetrics dm = getResources().getDisplayMetrics();	//得到屏幕的大小,android 4.2有问题
			if(dm.widthPixels!=0){
				int currentWidth=dm.widthPixels/count;	//宽:依照有n块进行分割
				int currentHeight=currentWidth*dm.widthPixels/dm.heightPixels;//高:依照手机的屏幕比例进行分割
				myIconBg.setBounds(0,0,currentWidth,currentHeight);
			}
			else{
				myIconBg.setBounds(0,0,120,80);
			}
			//上、下、左、右设置图标
			tabView.setCompoundDrawables(myIconBg, null, null, null);
//			tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);	//依靠图标的大小来决定
		}
		//指定高、宽、权重
		mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
				WRAP_CONTENT, 1));
	}

	@Override
	public void onPageScrollStateChanged(int arg0) {
		if (mListener != null) {
			mListener.onPageScrollStateChanged(arg0);
		}
	}
	//11.
	@Override
	public void onPageScrolled(int arg0, float arg1, int arg2) {
		if (mListener != null) {
			mListener.onPageScrolled(arg0, arg1, arg2);
		}
	}

	@Override
	public void onPageSelected(int arg0) {
		setCurrentItem(arg0);
		if (mListener != null) {
			mListener.onPageSelected(arg0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 2.
	 * @see
	 * com.viewpagerindicator.PageIndicator#setViewPager(android.support.v4.
	 * view.ViewPager) indicator和ViewPager进行关联在一起
	 */
	@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();
	}

	/*3.
	 * notifyDataSetChanged通知方法,表示为这个ViewPager提供View(一般是Fragment)的 Adapter
	 * 里面的数据集发生变化时,执行的动作,这里可增加相关的逻辑。
	 */
	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,count);
		}
		if (mSelectedTabIndex > count) {
			mSelectedTabIndex = count - 1;
		}
		setCurrentItem(mSelectedTabIndex);
		requestLayout();
	}

	@Override
	public void setViewPager(ViewPager view, int initialPosition) {
		setViewPager(view);
		setCurrentItem(initialPosition);
	}
	//5.
	@Override
	public void setCurrentItem(int item) {
		if (mViewPager == null) {
			throw new IllegalStateException("ViewPager has not been bound.");
		}
		mSelectedTabIndex = item;
		mViewPager.setCurrentItem(item);

		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);
			}
		}
	}
	//7.
	@Override
	public void setOnPageChangeListener(OnPageChangeListener listener) {
		mListener = listener;
	}

	private class TabView extends TextView {
		private int mIndex;

		public TabView(Context context) {
			super(context, null, R.attr.vpiTabPageIndicatorStyle);
		}
		//10.
		@Override
		public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
			super.onMeasure(widthMeasureSpec, heightMeasureSpec);

			// Re-measure if we went beyond our maximum size.
			if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
				super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth,
						MeasureSpec.EXACTLY), heightMeasureSpec);
			}
		}

		public int getIndex() {
			return mIndex;
		}
	}
}

资源文件:activity_page_tabs.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="@color/white"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
	<!-- indicator and ViewPager 要挨在一起 -->
	<android.support.v4.view.ViewPager
        android:id="@+id/maintab_pager"
        android:background="@color/white"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />
     <com.viewpagerindicator.TabPageIndicator
	       
	       android:id="@+id/maintab_indicator"
	       android:layout_height="wrap_content"
	       android:layout_width="wrap_content"
	       /> 
</LinearLayout>

图标资源:tab_news.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
	
    
    <!-- 选择时的图片 -->
    <item android:state_selected="true" android:drawable="@drawable/tab_selected_news"/>
    
    <!-- 默认时的图片 -->
    <item android:drawable="@drawable/tab_default_news"/>
</selector>
建议:
先到此博客,进行了解Android-viewPagerIndicator使用
http://www.cnblogs.com/qinghuaideren/p/3501999.html


 类似资料: