/*
* Copyright (C) 2013 Peng fei Pan <sky@xiaopan.me>
*
* 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 net.oschina.gitapp.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.oschina.gitapp.R;
/**
* 专为ViewPager定制的滑动选项卡 HOME
* @version 1.3.0
* @author Peng fei Pan
*/
public class PagerSlidingTabStrip extends HorizontalScrollView implements View.OnClickListener {
private int currentPosition; //当前位置
private int lastOffset;
private int lastScrollX = 0;
private float currentPositionOffset; //当前位置偏移量
private boolean start;
private boolean allowWidthFull; // 内容宽度无法充满时,允许自动调整Item的宽度以充满
private boolean disableViewPager; // 禁用ViewPager
private View currentSelectedTabView; //当前标题项
private Drawable slidingBlockDrawable; //滑块
private ViewPager viewPager; //ViewPager
private ViewGroup tabsLayout; //标题项布局
private ViewPager.OnPageChangeListener onPageChangeListener; //页面改变监听器
private OnClickTabListener onClickTabListener;
private List<View> tabViews;
public PagerSlidingTabStrip(Context context) {
this(context, null);
}
public PagerSlidingTabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
setHorizontalScrollBarEnabled(false); //隐藏横向滑动提示条
if(attrs != null){
TypedArray attrsTypedArray = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
if(attrsTypedArray != null){
allowWidthFull = attrsTypedArray.getBoolean(R.styleable.PagerSlidingTabStrip_allowWidthFull, false);
slidingBlockDrawable = attrsTypedArray.getDrawable(R.styleable.PagerSlidingTabStrip_slidingBlock);
disableViewPager = attrsTypedArray.getBoolean(R.styleable.PagerSlidingTabStrip_disableViewPager, false);
attrsTypedArray.recycle();
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(!allowWidthFull) return;
ViewGroup tabsLayout = getTabsLayout();
if(tabsLayout == null || tabsLayout.getMeasuredWidth() >= getMeasuredWidth()) return;
if(tabsLayout.getChildCount() <= 0) return;
if(tabViews == null){
tabViews = new ArrayList<View>();
}else{
tabViews.clear();
}
for(int w = 0; w < tabsLayout.getChildCount(); w++){
tabViews.add(tabsLayout.getChildAt(w));
}
adjustChildWidthWithParent(tabViews, getMeasuredWidth()-tabsLayout.getPaddingLeft()-tabsLayout.getPaddingRight(), widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 调整views集合中的View,让所有View的宽度加起来正好等于parentViewWidth
* @param views 子View集合
* @param parentViewWidth 父Vie的宽度
* @param parentWidthMeasureSpec 父View的宽度规则
* @param parentHeightMeasureSpec 父View的高度规则
*/
private void adjustChildWidthWithParent(List<View> views, int parentViewWidth, int parentWidthMeasureSpec, int parentHeightMeasureSpec){
// 先去掉所有子View的外边距
for(View view : views){
if(view.getLayoutParams() instanceof MarginLayoutParams){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams();
parentViewWidth -= lp.leftMargin + lp.rightMargin;
}
}
// 去掉宽度大于平均宽度的View后再次计算平均宽度
int averageWidth = parentViewWidth /views.size();
int bigTabCount = views.size();
while(true){
Iterator<View> iterator = views.iterator();
while(iterator.hasNext()){
View view = iterator.next();
if(view.getMeasuredWidth() > averageWidth){
parentViewWidth -= view.getMeasuredWidth();
bigTabCount--;
iterator.remove();
}
}
averageWidth = parentViewWidth /bigTabCount;
boolean end = true;
for(View view : views){
if(view.getMeasuredWidth() > averageWidth){
end = false;
}
}
if(end){
break;
}
}
// 修改宽度小于新的平均宽度的View的宽度
for(View view : views){
if(view.getMeasuredWidth() < averageWidth){
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
layoutParams.width = averageWidth;
view.setLayoutParams(layoutParams);
// 再次测量让新宽度生效
if(layoutParams instanceof MarginLayoutParams){
measureChildWithMargins(view, parentWidthMeasureSpec, 0, parentHeightMeasureSpec, 0);
}else{
measureChild(view, parentWidthMeasureSpec, parentHeightMeasureSpec);
}
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
ViewGroup tabViewGroup = getTabsLayout();
if(tabViewGroup != null){
// 初始化滑块位置以及选中状态
currentPosition = viewPager != null?viewPager.getCurrentItem():0;
if(!disableViewPager){
scrollToChild(currentPosition, 0); //移动滑块到指定位置
selectedTab(currentPosition); //选中指定位置的TAB
}
//给每一个tab设置点击事件,当点击的时候切换Pager
for(int w = 0; w < tabViewGroup.getChildCount(); w++){
View itemView = tabViewGroup.getChildAt(w);
itemView.setTag(w);
itemView.setOnClickListener(this);
}
}
}
@Override
public void onClick(View v) {
int index = (Integer) v.getTag();
if(onClickTabListener != null){
onClickTabListener.onClickTab(v, index);
}
if(viewPager != null){
viewPager.setCurrentItem(index, true);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(disableViewPager) return;
/* 绘制滑块 */
ViewGroup tabsLayout = getTabsLayout();
if(tabsLayout != null && tabsLayout.getChildCount() > 0 && slidingBlockDrawable != null){
View currentTab = tabsLayout.getChildAt(currentPosition);
if(currentTab != null){
float slidingBlockLeft = currentTab.getLeft();
float slidingBlockRight = currentTab.getRight();
if (currentPositionOffset > 0f && currentPosition < tabsLayout.getChildCount() - 1) {
View nextTab = tabsLayout.getChildAt(currentPosition + 1);
if(nextTab != null){
final float nextTabLeft = nextTab.getLeft();
final float nextTabRight = nextTab.getRight();
slidingBlockLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * slidingBlockLeft);
slidingBlockRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * slidingBlockRight);
}
}
slidingBlockDrawable.setBounds((int)slidingBlockLeft, 0, (int)slidingBlockRight, getHeight());
slidingBlockDrawable.draw(canvas);
}
}
}
/**
* 获取布局
*/
private ViewGroup getTabsLayout(){
if(tabsLayout == null){
if(getChildCount() > 0){
tabsLayout = (ViewGroup) getChildAt(0);
}else{
removeAllViews();
tabsLayout = new LinearLayout(getContext());
addView(tabsLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
return tabsLayout;
}
/**
* 滚动到指定的位置
*/
private void scrollToChild(int position, int offset) {
ViewGroup tabsLayout = getTabsLayout();
if(tabsLayout != null && tabsLayout.getChildCount() > 0 && position < tabsLayout.getChildCount()){
View view = tabsLayout.getChildAt(position);
if(view != null){
//计算新的X坐标
int newScrollX = view.getLeft() + offset;
if (position > 0 || offset > 0) {
newScrollX -= 240 - getOffset(view.getWidth())/2;
}
//如果同上次X坐标不一样就执行滚动
if (newScrollX != lastScrollX) {
lastScrollX = newScrollX;
scrollTo(newScrollX, 0);
}
}
}
}
/**
* 获取偏移量
*/
private int getOffset(int newOffset){
if(lastOffset < newOffset){
if(start){
lastOffset += 1;
return lastOffset;
}else{
start = true;
lastOffset += 1;
return lastOffset;
}
}if(lastOffset > newOffset){
if(start){
lastOffset -= 1;
return lastOffset;
}else{
start = true;
lastOffset -= 1;
return lastOffset;
}
}else{
start = true;
lastOffset = newOffset;
return lastOffset;
}
}
/**
* 选中指定位置的TAB
*/
private void selectedTab(int currentSelectedTabPosition){
ViewGroup tabsLayout = getTabsLayout();
if(currentSelectedTabPosition > -1 && tabsLayout != null && currentSelectedTabPosition < tabsLayout.getChildCount()){
if(currentSelectedTabView != null){
currentSelectedTabView.setSelected(false);
}
currentSelectedTabView = tabsLayout.getChildAt(currentSelectedTabPosition);
if(currentSelectedTabView != null){
currentSelectedTabView.setSelected(true);
}
}
}
/**
* 添加Tab
*/
public void addTab(View tabView, int index){
if(tabView != null){
getTabsLayout().addView(tabView, index);
requestLayout();
}
}
/**
* 添加Tab
*/
public void addTab(View tabView){
addTab(tabView, -1);
}
/**
* 添加Tab
* @param tabViews 可以一次添加多个Tab
*/
public void addTab(View... tabViews) {
if(tabViews != null){
for(View view : tabViews){
getTabsLayout().addView(view);
}
requestLayout();
}
}
/**
* 添加Tab
*/
public void addTab(List<View> tabViews) {
if(tabViews != null){
for(View view : tabViews){
getTabsLayout().addView(view);
}
requestLayout();
}
}
/**
* 设置ViewPager
* @param viewPager ViewPager
*/
public void setViewPager(ViewPager viewPager) {
if(disableViewPager) return;
this.viewPager = viewPager;
this.viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
selectedTab(position);
if(onPageChangeListener != null){
onPageChangeListener.onPageSelected(position);
}
}
@Override
public void onPageScrolled(int nextPagePosition, float positionOffset, int positionOffsetPixels) {
ViewGroup tabsLayout = getTabsLayout();
if(nextPagePosition < tabsLayout.getChildCount()){
View view = tabsLayout.getChildAt(nextPagePosition);
if(view != null){
currentPosition = nextPagePosition;
currentPositionOffset = positionOffset;
scrollToChild(nextPagePosition, (int) (positionOffset * view.getWidth()));
invalidate();
}
}
if(onPageChangeListener != null){
onPageChangeListener.onPageScrolled(nextPagePosition, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageScrollStateChanged(int arg0) {
if(onPageChangeListener != null){
onPageChangeListener.onPageScrollStateChanged(arg0);
}
}
});
requestLayout();
}
/**
* 设置Page切换监听器
* @param onPageChangeListener Page切换监听器
*/
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
this.onPageChangeListener = onPageChangeListener;
}
/**
* 设置是否充满屏幕
* @param allowWidthFull true:当内容的宽度无法充满屏幕时,自动调整每一个Item的宽度以充满屏幕
*/
public void setAllowWidthFull(boolean allowWidthFull) {
this.allowWidthFull = allowWidthFull;
requestLayout();
}
/**
* 设置滑块图片
*/
public void setSlidingBlockDrawable(Drawable slidingBlockDrawable) {
this.slidingBlockDrawable = slidingBlockDrawable;
requestLayout();
}
/**
* 获取Tab总数
*/
public int getTabCount(){
ViewGroup tabsLayout = getTabsLayout();
return tabsLayout!=null?tabsLayout.getChildCount():0;
}
/**
* 设置Tab点击监听器
* @param onClickTabListener Tab点击监听器
*/
public void setOnClickTabListener(OnClickTabListener onClickTabListener) {
this.onClickTabListener = onClickTabListener;
}
/**
* 设置不使用ViewPager
* @param disableViewPager 不使用ViewPager
*/
public void setDisableViewPager(boolean disableViewPager) {
this.disableViewPager = disableViewPager;
if(viewPager != null){
viewPager.setOnPageChangeListener(onPageChangeListener);
viewPager = null;
}
requestLayout();
}
/**
* Tab点击监听器
* @author xiaopan
*
*/
public interface OnClickTabListener {
public void onClickTab(View tab, int index);
}
}