Android Tab切换表格,如图:
1.横向:
2.竖向:
xml:布局
<com.example.momo.myconcept.view.SegmentControl xmlns:segmentcontrol="http://schemas.android.com/apk/res-auto"
android:id="@+id/segment_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="13sp"
segmentcontrol:colors="#0099CC"
segmentcontrol:cornerRadius="6dp"
segmentcontrol:direction="horizon"
segmentcontrol:gaps="10dp"
segmentcontrol:horizonGap="20dp"
segmentcontrol:texts="语文|数学|外语/>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SegmentControl">
<attr name="cornerRadius" format="dimension|reference" />
<attr name="colors" format="color|reference" />
<attr name="texts" format="string|reference" />
<attr name="android:textSize" />
<attr name="direction" format="enum">
<enum name="horizon" value="0" />
<enum name="vertical" value="1" />
</attr>
<attr name="gaps" format="dimension|reference" />
<attr name="horizonGap" format="dimension|reference" />
<attr name="verticalGap" format="dimension|reference" />
</declare-styleable>
</resources>
java:
package com.example.momo.myconcept.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.example.momo.myconcept.R;
public class SegmentControl extends View {
private String[] mTexts;
private Rect[] mCacheBounds;
private Rect[] mTextBounds;
private RadiusDrawable mBackgroundDrawable;
private RadiusDrawable mSelectedDrawable;
private int mCurrentIndex;
private int mTouchSlop;
private boolean inTapRegion;
private float mStartX;
private float mStartY;
private float mCurrentX;
private float mCurrentY;
private int mHorizonGap;
private int mVerticalGap;
private int mCenterX;
private int mCenterY;
private int mChildrenWidth;
private int mChildrenHeight;
private int mSingleChildWidth;
private int mSingleChildHeight;
private Paint mPaint;
public enum Direction{
HORIZON(0), VERTICAL(1);
int mV;
private Direction(int v){
mV = v;
}
}
private Direction mDirection;
private int mTextSize;
private ColorStateList mColors;
private int mCornerRadius;
public interface OnSegmentControlClickListener{
public void onSegmentControlClick(int index);
}
private OnSegmentControlClickListener mOnSegmentControlClickListener;
public SegmentControl(Context context){
this(context, null);
}
public SegmentControl(Context context,AttributeSet attrs){
this(context, attrs, 0);
}
public SegmentControl(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SegmentControl);
String textArray = ta.getString(R.styleable.SegmentControl_texts);
if(textArray != null){
mTexts = textArray.split("\\|");
}
mTextSize = ta.getDimensionPixelSize(R.styleable.SegmentControl_android_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics()));
mColors = ta.getColorStateList(R.styleable.SegmentControl_colors);
mCornerRadius = ta.getDimensionPixelSize(R.styleable.SegmentControl_cornerRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, context.getResources().getDisplayMetrics()));
mDirection = Direction.values()[ta.getInt(R.styleable.SegmentControl_direction, 0)];
mHorizonGap = ta.getDimensionPixelSize(R.styleable.SegmentControl_horizonGap, 0);
mVerticalGap = ta.getDimensionPixelSize(R.styleable.SegmentControl_verticalGap, 0);
int gap = ta.getDimensionPixelSize(R.styleable.SegmentControl_gaps, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics()));
if(mHorizonGap == 0) mHorizonGap = gap;
if(mVerticalGap == 0) mVerticalGap = gap;
ta.recycle();
mBackgroundDrawable = new RadiusDrawable(mCornerRadius, true, 0);
mBackgroundDrawable.setStrokeWidth(2);
if(mColors == null){
mColors = new ColorStateList(new int[][]{{}}, new int[]{0xFF0099CC});
}
mBackgroundDrawable.setStrokeColor(mColors.getDefaultColor());
if(Build.VERSION.SDK_INT < 16){
setBackgroundDrawable(mBackgroundDrawable);
}else{
setBackground(mBackgroundDrawable);
}
mSelectedDrawable = new RadiusDrawable(mCornerRadius, false, mColors.getDefaultColor());
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(mTextSize);
mPaint.setColor(mColors.getDefaultColor());
//here's the tricky thing, when you doing a click detect on a capacitive touch screen,
//sometimes the touch points of touchDown and touchUp are different(it's call slop) even when you didn't actually move your finger,
//so we set a distance limit for the distance of this two touch points to create a better user experience;
int touchSlop = 0;
if(context == null){
touchSlop = ViewConfiguration.getTouchSlop();
}else{
final ViewConfiguration config = ViewConfiguration.get(context);
touchSlop = config.getScaledTouchSlop();
}
mTouchSlop = touchSlop * touchSlop;
inTapRegion = false;
}
public void setOnSegmentControlClickListener(OnSegmentControlClickListener onSegmentControlClickListener){
mOnSegmentControlClickListener = onSegmentControlClickListener;
}
public OnSegmentControlClickListener getOnSegmentControlClicklistener(){
return mOnSegmentControlClickListener;
}
public void setText(String... texts){
mTexts = texts;
if(mTexts != null){
requestLayout();
}
}
public void setColors(ColorStateList colors){
mColors = colors;
if(mBackgroundDrawable != null){
mBackgroundDrawable.setStrokeColor(colors.getDefaultColor());
}
if(mSelectedDrawable != null){
mSelectedDrawable.setColor(colors.getDefaultColor());
}
mPaint.setColor(colors.getDefaultColor());
invalidate();
}
public void setCornerRadius(int cornerRadius){
mCornerRadius = cornerRadius;
if(mBackgroundDrawable != null){
mBackgroundDrawable.setRadius(cornerRadius);
}
invalidate();
}
public void setDirection(Direction direction){
Direction tDirection = mDirection;
mDirection = direction;
if(tDirection != direction){
requestLayout();
invalidate();
}
}
public void setTextSize(int textSize){
setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
}
public void setTextSize(int unit, int textSize){
mPaint.setTextSize((int) (TypedValue.applyDimension(unit, textSize, getContext().getResources().getDisplayMetrics())));
if(textSize != mTextSize){
mTextSize = textSize;
requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
if(mTexts != null && mTexts.length > 0){
if(mCacheBounds == null || mCacheBounds.length != mTexts.length){
mCacheBounds = new Rect[mTexts.length];
}
if(mTextBounds == null || mTextBounds.length != mTexts.length){
mTextBounds = new Rect[mTexts.length];
}
for(int i = 0; i < mTexts.length; i++){
String text = mTexts[i];
if(text != null){
if(mTextBounds[i] == null) mTextBounds[i] = new Rect();
mPaint.getTextBounds(text, 0, text.length(), mTextBounds[i]);
if(mSingleChildWidth < mTextBounds[i].width() + mHorizonGap * 2) mSingleChildWidth = mTextBounds[i].width() + mHorizonGap * 2;
if(mSingleChildHeight < mTextBounds[i].height() + mVerticalGap * 2) mSingleChildHeight = mTextBounds[i].height() + mVerticalGap * 2;
}
}
for(int i = 0; i < mTexts.length; i++){
if (mCacheBounds[i] == null) mCacheBounds[i] = new Rect();
if(mDirection == Direction.HORIZON){
mCacheBounds[i].left = i * mSingleChildWidth;
mCacheBounds[i].top = 0;
}else{
mCacheBounds[i].left = 0;
mCacheBounds[i].top = i * mSingleChildHeight;
}
mCacheBounds[i].right = mCacheBounds[i].left + mSingleChildWidth;
mCacheBounds[i].bottom = mCacheBounds[i].top + mSingleChildHeight;
}
switch(widthMode){
case MeasureSpec.AT_MOST:
if(mDirection == Direction.HORIZON){
if(widthSize <= mSingleChildWidth * mTexts.length){
mSingleChildWidth = widthSize / mTexts.length;
width = widthSize;
}else{
width = mSingleChildWidth * mTexts.length;
}
}else{
width = widthSize <= mSingleChildWidth ? widthSize : mSingleChildWidth;
}
break;
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
if(mDirection == Direction.HORIZON){
width = mSingleChildWidth * mTexts.length;
}else{
width = widthSize <= mSingleChildWidth ? widthSize : mSingleChildWidth;
}
break;
}
switch(heightMode){
case MeasureSpec.AT_MOST:
if(mDirection == Direction.VERTICAL){
if(heightSize <= mSingleChildHeight * mTexts.length){
mSingleChildHeight = heightSize / mTexts.length;
height = heightSize;
}else{
height = mSingleChildHeight * mTexts.length;
}
}else{
height = heightSize <= mSingleChildHeight ? heightSize : mSingleChildHeight;
}
break;
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
if(mDirection == Direction.VERTICAL){
height = mSingleChildHeight * mTexts.length;
}else{
height = heightSize <= mSingleChildHeight ? heightSize : mSingleChildHeight;
}
break;
}
mChildrenWidth = mDirection == Direction.HORIZON ? mSingleChildWidth * mTexts.length : mSingleChildWidth;
mChildrenHeight = mDirection == Direction.VERTICAL ? mSingleChildHeight * mTexts.length : mSingleChildHeight;
}else{
width = widthMode == MeasureSpec.UNSPECIFIED ? 0 : widthSize;
height = heightMode == MeasureSpec.UNSPECIFIED ? 0 : heightSize;
}
mCenterX = width / 2;
mCenterY = height / 2;
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event){
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
inTapRegion = true;
mStartX = event.getX();
mStartY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mCurrentX = event.getX();
mCurrentY = event.getY();
int dx = (int) (mCurrentX - mStartX);
int dy = (int) (mCurrentY - mStartY);
int distance = dx * dx + dy * dy;
if(distance > mTouchSlop){
inTapRegion = false;
}
break;
case MotionEvent.ACTION_UP:
if(inTapRegion){
int index = 0;
if(mDirection == Direction.HORIZON){
index = (int) (mStartX / mSingleChildWidth);
}else{
index = (int) (mStartY / mSingleChildHeight);
}
if(mOnSegmentControlClickListener != null) mOnSegmentControlClickListener.onSegmentControlClick(index);
mCurrentIndex = index;
invalidate();
}
break;
}
return true;
}
public void setSelectedIndex(int index){
mCurrentIndex = index;
invalidate();
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
if(mTexts != null && mTexts.length > 0){
for(int i = 0; i < mTexts.length; i++){
//draw separate lines
if(i < mTexts.length - 1){
mPaint.setColor(mColors.getDefaultColor());
if(mDirection == Direction.HORIZON){
canvas.drawLine(mCacheBounds[i].right, 0, mCacheBounds[i].right, getHeight(), mPaint);
}else{
canvas.drawLine(mCacheBounds[i].left, mSingleChildHeight * (i + 1), mCacheBounds[i].right, mSingleChildHeight * (i + 1), mPaint);
}
}
//draw selected drawable
if(i == mCurrentIndex && mSelectedDrawable != null){
int topLeftRadius = 0;
int topRightRadius = 0;
int bottomLeftRadius = 0;
int bottomRightRadius = 0;
if(mDirection == Direction.HORIZON){
if(i == 0){
topLeftRadius = mCornerRadius;
bottomLeftRadius = mCornerRadius;
}else if(i == mTexts.length - 1){
topRightRadius = mCornerRadius;
bottomRightRadius = mCornerRadius;
}
}else{
if(i == 0){
topLeftRadius = mCornerRadius;
topRightRadius = mCornerRadius;
}else if(i == mTexts.length - 1){
bottomLeftRadius = mCornerRadius;
bottomRightRadius = mCornerRadius;
}
}
mSelectedDrawable.setRadiuses(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
mSelectedDrawable.setBounds(mCacheBounds[i]);
mSelectedDrawable.draw(canvas);
mPaint.setColor(0xFFFFFFFF);
}else{
mPaint.setColor(mColors.getDefaultColor());
}
//draw texts
canvas.drawText(mTexts[i], mCacheBounds[i].left + (mSingleChildWidth - mTextBounds[i].width()) / 2, mCacheBounds[i].top + ((mSingleChildHeight + mTextBounds[i].height()) / 2), mPaint);
}
}
}
}
用法 MainActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SegmentControl segmentControl = (SegmentControl)findViewById(R.id.segment_control);
segmentControl.setSelectedIndex(1);
segmentControl.setOnSegmentControlClickListener(new SegmentControl.OnSegmentControlClickListener() {
@Override
public void onSegmentControlClick(int index) {
Toast.makeText(MainActivity.this,"this "+index,Toast.LENGTH_LONG).show();
}
});
}
}
参考:Git: SHSegmentControl
程序代码参考:点击打开链接