Android换页指示器TabIndicator

时间:2021-03-11 10:29:03

一、看效果


Android换页指示器TabIndicator


二、实现原理

1、继承线性布局LinearLayout;

2、在测量方法onMeasure中,在原高度上基础上增加下划线高度:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + underlineHeight);
}


3、在布局方法onLayout中,为下划线预留高度:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b - underlineHeight);
}


4、在绘制方法onDraw中,绘制下划线:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

int childCount = getChildCount();

if (childCount <= 0 || currentTab < 0 || currentTab >= childCount) {
return;
}

if (offset < -1) {
offset = -1;
}

if (offset > 1) {
offset = 1;
}

View child = getChildAt(currentTab);

int underlineWidth = child.getWidth();

int left = child.getLeft();
int right = child.getRight();
int bottom = getHeight() - getPaddingBottom();
int top = bottom - underlineHeight;

float offsetWidth = underlineWidth * offset;

paint.setColor(underlineColor);

canvas.drawRect(left + offsetWidth, top, right + offsetWidth, bottom, paint);

}

注意,绘制方法中同时也实现下划线动态偏移的效果,后面可以看完整代码。


三、完整代码

在下述代码中,我们还给出了一个简单设置文本标签的快捷适配器。

import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

public class TabIndicator extends LinearLayout {
protected BaseAdapter adapter;
private int underlineColor;
private int underlineHeight;

private Paint paint;

private int currentTab = 0;
private float offset = 0;

public TabIndicator(Context context) {
this(context, null);
}

public TabIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}

public void setUnderlineColor(int underlineColor) {
this.underlineColor = underlineColor;
}

public void setUnderlineHeight(int underlineHeight) {
this.underlineHeight = underlineHeight;
}

public void setCurrentTab(int currentTab) {
setCurrentTab(currentTab, 0);
}

public void setCurrentTab(int currentTab, float offset) {
this.currentTab = currentTab;
this.offset = offset;
invalidate();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + underlineHeight);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b - underlineHeight);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

int childCount = getChildCount();

if (childCount <= 0 || currentTab < 0 || currentTab >= childCount) {
return;
}

if (offset < -1) {
offset = -1;
}

if (offset > 1) {
offset = 1;
}

View child = getChildAt(currentTab);

int underlineWidth = child.getWidth();

int left = child.getLeft();
int right = child.getRight();
int bottom = getHeight() - getPaddingBottom();
int top = bottom - underlineHeight;

float offsetWidth = underlineWidth * offset;

paint.setColor(underlineColor);

canvas.drawRect(left + offsetWidth, top, right + offsetWidth, bottom, paint);

}

private void initView() {
setOrientation(LinearLayout.HORIZONTAL);
paint = new Paint();
paint.setAntiAlias(true);
}

public void setAdapter(BaseAdapter adapter) {
this.adapter = adapter;
if (this.adapter == null) {
removeAllViews();
return;
}
this.adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
fillChilds();
}

@Override
public void onInvalidated() {
fillChilds();
}

});
fillChilds();
}

protected void fillChilds() {
removeAllViews();
for (int i = 0; i < adapter.getCount(); i++) {
final View child = adapter.getView(i, null, null);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
params.weight=1;
addView(child, params);
}

}

public static abstract class TextAdapter extends BaseAdapter {
private final String[] titles;

public TextAdapter(String[] titles) {
this.titles = titles;
}

@Override
public int getCount() {
if (titles == null || titles.length <= 0) {
return 0;
}
return titles.length;
}

@Override
public Object getItem(int position) {
if (titles == null || titles.length <= 0) {
return null;
}
return titles[position];
}

@Override
public long getItemId(int position) {
return position;
}

public abstract TextView getTextView(int position, View convertView, ViewGroup container);

@Override
public View getView(final int position, View convertView, ViewGroup container) {
TextView tv= getTextView(position, convertView, container);
tv.setText(titles[position]);
return tv;
}

}

}


四、与ViewPager结合使用例子

TabIndicator tab = (TabIndicator) findViewById(R.id.tab);

//设置下划线颜色
tab.setUnderlineColor(Color.parseColor("#FFF44D06"));
//设置下划线高度
tab.setUnderlineHeight(6);
//设置标签视图适配器
tab.setAdapter(new TextAdapter(new String[]{"销量","价格","筛选"}) {
@Override
public TextView getTextView(int position, View convertView, ViewGroup container) {
return new TextView(getApplicationContext());
}
});

ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int arg0) {
}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
//注意,此处是让下划线动态滑动的关键
tab.setCurrentTab(arg0, arg1);
}

@Override
public void onPageScrollStateChanged(int arg0) {

}

});

五、特性总结

1、指示器继承线性布局的一切原有特性和语义,无任何负作用;

2、指示器完全在布局层实现,与标签视图无关,适配器中的视图可以是任意View;

3、代码量精简,使用方便,性能稳定。


国际惯例

————————————————————————————————————————————————————————

作者:薄荷记账  (转载请注明原作者)

简洁   稳定   优雅   无限可能!