完整项目:Github,包括使用说明
传送门
https://github.com/CodeManLH/SegmentedControl
先上图


写项目时有个需求需要展示SegmentedControl(分段控制器),iOS是有的,可惜Android没有,本来想网上找一个用的,但是都不符合需求,项目用的是两行,网上大多数都是用RadioGroup,去实现的,这里我用自定义继承LinearLayout 实现,因为是两行没办法,那就自己动手写一个吧,实现不复杂,上代码
先说单个Segmented,定义一个类继承LinearLayout
Segmented
public class Segmented extends LinearLayout {
...
// 这里接收定义好的属性,然后给属性赋值
public Segmented(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SegmentedItem);
mText = a.getString(R.styleable.SegmentedItem_text);
mDescription = a.getString(R.styleable.SegmentedItem_description);
// 接收Dimension时要注意,如果在布局文件定义sp,在setTextSize时,统一把单位换成px,不然字体会很大
mTextSize = a.getDimension(R.styleable.SegmentedItem_textSize,DensityUtil.toSp(context,12));
mDescriptionSize = a.getDimension(R.styleable.SegmentedItem_descriptionSize,DensityUtil.toSp(context,12));
mTextColor = a.getColor(R.styleable.SegmentedItem_textColor,0);
mTextSelectedColor = a.getColor(R.styleable.SegmentedItem_textSelectedColor,Color.WHITE);
mDescriptionColor = a.getColor(R.styleable.SegmentedItem_descriptionColor,0);
mDescriptionSelectedColor = a.getColor(R.styleable.SegmentedItem_descriptionSelectedColor,Color.WHITE);
a.recycle();
initView();
}
}
// onAttachedToWindow , 如果想要获得父控件的话,在这个方法获取,不然会报空指针,我是在这个方法获取父控件的颜色,赋值给字体,为了在父控件设置颜色是,这里保持一样
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTextColor == 0) {
SegmentedView segmentedView = (SegmentedView) getParent();
mTextColor = segmentedView.getTintColor();
}
int[] textColors = new int[] { mTextSelectedColor, mTextColor };
mTvText.setTextColor(createColorStateList(textColors));
}
// 然后就是做初始化和添加子控件
private void initView() {
setClickable(true);
setOrientation(LinearLayout.VERTICAL);
// 设置居中
setGravity(Gravity.CENTER);
setVerticalGravity(Gravity.CENTER_VERTICAL);
}
// 添加子控件
private void initTitle() {
// 创建
mTvText = new AppCompatTextView(mContext);
// 刚才上面说的是这里,需要把尺寸换PX,不然字体会很大
mTvText.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
// 子控件设置布局
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams
.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
mTvText.setLayoutParams(layoutParams);
if (!TextUtils.isEmpty(mText)) {
mTvText.setText(mText);
}
// 添加子控件
addView(mTvText);
}
// 核心,不同的状态对不同的颜色,平常设置textColor,我都是用XML文件的selector去设置不同状态对不同的颜色,这里我选择用代码
private ColorStateList createColorStateList(int[] colors){
// 参数的Color,第一个对应下面states[0] ,的颜色,以此类推
// int[] textColors = new int[] { mTextSelectedColor, mTextColor };
// 下面定义状态
int[][] states = new int[2][];
states[0] = new int[] { android.R.attr.state_selected };
// 正常颜色不设置状态
states[1] = new int[] { };
ColorStateList colorStateList = new ColorStateList(states,colors);
// 返回一个ColorStateList,TextView.setTextColor(colorStateList)
return colorStateList;
}
每个一分段核心的代码就是这些了,剩下的都是set get 属性
SegmentedControl
还是一样,自定义一个类继承LinearLayout
// 定义一个数组存储Segmented
private List<Segmented> mSegmentedItems;
// 这里都差不多,接收XML布局文件设置的属性
public SegmentedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SegmentedView);
mSelectedIndex = a.getInteger(R.styleable.SegmentedView_selectedIndex,0);
...
a.recycle();
}
// 第一个注意的地方,在这里生命周期方法做初始化或绘制图形
/**
* 所有子视图添加完毕
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 绘制圆角矩形
drawBackgroup();
// 绘制左边item图形
drawLeftItemBackGroupSelector();
// 绘制左边item图形
drawRightItemBackGroupSelector();
if (getChildCount() > 0) {
mSegmentedItems = new ArrayList<>();
initView();
}
}
/**
* 绘制背景圆角矩形
*/
private void drawBackgroup() {
// 这里选择用GradientDrawable绘制大背景圆角矩形,一开始我是用XML去画的,后面不想依赖那么多,就代码上了
GradientDrawable backGroupDrawable = new GradientDrawable();
backGroupDrawable.setShape(GradientDrawable.RECTANGLE);
backGroupDrawable.setCornerRadius(mCornerRadius);
backGroupDrawable.setStroke(mStrokeWidth,mTintColor);
// setPadding(0,mStrokeWidth,0,mStrokeWidth);
setBackground(backGroupDrawable);
}
// 重点,也是注意的地方,最左边的设置左上和左下的圆角,不然会覆盖背景框的圆角
/**
* 绘制左边item图形
*/
private void drawLeftItemBackGroupSelector() {
float[] radii = {mInnerCornerRadius,mInnerCornerRadius,0,0,0,0, mInnerCornerRadius,mInnerCornerRadius};
mLeftItemBackgroup = new StateListDrawable();
mLeftItemBackgroup.addState(new int[] { android.R.attr.state_selected }, createRoundShape(radii,mTintColor));
mLeftItemBackgroup.addState(new int[] { }, createRoundShape(radii,colorTransparent));
}
// 最右边绘制那个仿照左边的即可
// 这里说说为什么要删除XML定义的Segmented子布局,为了要添加每个Segmented的分割线,如果不删除,插入时会不好计算位置,省事我就先删除,用集合存起来,下面在添加回去,和分割线一起添加
/**
* 初始化视图
*/
private void initView() {
setOrientation(LinearLayout.HORIZONTAL);
// 获取子元素, 并删除
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
if (childView instanceof SegmentedItem) {
mSegmentedItems.add((SegmentedItem) childView);
}
removeViewAt(i);
i--;
}
initSegmentedItem();
}
/**
* 初始化SegmentedItem点击事件
*/
private void initSegmentedItem() {
// 设置当前已选择位置
mCurrentSelectedPosition = 0;
// 遍历刚才存入的Segmented,设置点击事件和添加Segmented、分割线
for (int i = 0; i < mSegmentedItems.size(); i++) {
SegmentedItem segmentedItem = mSegmentedItems.get(i);
LinearLayout.LayoutParams layoutParams = (LayoutParams) segmentedItem.getLayoutParams();
layoutParams.weight = 1;
segmentedItem.setLayoutParams(layoutParams);
if (i == 0) { // 设置左边Item背景
segmentedItem.setBackground(mLeftItemBackgroup);
}else if (i == mSegmentedItems.size() - 1){ // 设置右边Item背景
segmentedItem.setBackground(mRightItemBackgroup);
}else { // 设置中间Item背景
segmentedItem.setBackground(drawMidItemBackGroupSelector());
}
addView(segmentedItem);
// 添加分割线
if (i != mSegmentedItems.size() - 1){
addView(createDevider());
}
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(GradientDrawable.RECTANGLE);
if (i == mSelectedIndex) {
segmentedItem.setSelected(true);
mCurrentSelectedPosition = mSelectedIndex;
}
setSegmentedItemClickListener(segmentedItem,i);
}
}
SegmentedControl 大致重点就是上面那些了,其实很简单
属性文件
都是一些很基本的东西
<declare-styleable name="Segmented">
<attr name="selectedIndex" format="integer" />
<attr name="tintColor" format="color" />
<attr name="strokeWidth" format="dimension" />
<attr name="cornerRadius" format="dimension" />
</declare-styleable>
<declare-styleable name="Segmented">
<attr name="text" format="string" />
<attr name="description" format="string" />
<attr name="textSize" format="dimension" />
<attr name="descriptionSize" format="dimension" />
<attr name="textColor" format="color" />
<attr name="descriptionColor" format="color" />
<attr name="textSelectedColor" format="color" />
<attr name="descriptionSelectedColor" format="color" />
</declare-styleable>
完整项目:Github,包括使用说明,如果对你有帮助记得给个Start
传送门
https://github.com/CodeManLH/SegmentedControl
网友评论