前言
太久没发CSDN,最近排名降得厉害,因为平时比较忙,加之内卷严重,着实有心无力,此次抽空发一个项目中用到的控件,懒得去找了自己撸一个,放这里方便大家也方便自己
Tablayout
效果图
如果效果是你想要的 请接着往下看
XML
<com.guide.lib_common.components.UnitSelectView
android:id="@+id/unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/lib_common_10dp"
app:backColor="@color/lib_common_222124"
app:boardColor="@color/lib_common_0A84FF"
app:itemHeight="@dimen/lib_common_38dp"
app:itemWidth="@dimen/lib_common_130dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/layout_head"
app:selectColor="@color/lib_common_0A84FF"
app:selectStr="英制,公制"
app:selectTextSize="@dimen/lib_common_16sp"
app:unselectColor="@color/lib_common_c1c1c1" />
<com.guide.lib_common.components.UnitSelectView
android:id="@+id/unit_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backColor="@color/lib_common_222124"
app:boardColor="@color/lib_common_444444"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:selectColor="@color/lib_common_0A84FF"
app:selectStr="yard,m"
app:selectTextSize="@dimen/lib_common_16sp"
app:unselectColor="@color/lib_common_c1c1c1" />
PS:selectStr方便调试,实际使用中使用其setUnits,详情见下方源码
Style
<declare-styleable name="UnitSelectView">
<attr name="unselectColor" format="color|reference" />
<attr name="selectColor" format="color|reference" />
<attr name="backColor" format="color|reference" />
<attr name="boardColor" format="color|reference" />
<attr name="selectStr" format="string" />
<attr name="selectTextSize" format="reference" />
<attr name="itemWidth" format="reference" />
<attr name="itemHeight" format="reference" />
</declare-styleable>
参数名 | 含义 |
---|---|
unselectColor | 未选中颜色 |
selectColor | 选中颜色 |
backColor | 背景颜色 |
boardColor | 边框颜色 |
selectStr | 文本(可扩展) |
selectTextSize | 文本字体大小 |
itemWidth | 单块宽度 |
itemHeight | 单块高度 |
源代码
class UnitSelectView : View {{
val mContext: Context
var mSelectStr: String
var mTotalCount: Int
var mSelectColor: Int
var mUnselectColor: Int
var mBoardColor: Int
var mBackColor: Int
var mTextsize: Float = 0F
var mItemWidth: Float = 0F
var mItemHeight: Float = 0F
var mPadding: Float = 0F
var mStrokeWidth: Float = 0F
var mOffest: Float = 0F
var mRadiusSize: Float = 0F
var mFatherHeight: Float = 0F
var mFatherWidth: Float = 0F
lateinit var mFatherRectF: RectF
lateinit var mTextPaint: TextPaint
lateinit var mBackPaint: Paint
lateinit var mBoardPaint: Paint
lateinit var mSonRectFs: ArrayList<RectF>
lateinit var mSontexts: List<String>
lateinit var mChooseSontexts: List<String>
public interface OnClickListener {
fun onClick(touchIndex: Int)
fun outOftouch()
}
var mOnClickListener: OnClickListener? = null
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
) {
mContext = context
val a = context.obtainStyledAttributes(attrs, R.styleable.UnitSelectView)
mTextsize = a.getDimension(R.styleable.UnitSelectView_selectTextSize, dp2px(14f))
mItemWidth = a.getDimension(R.styleable.UnitSelectView_itemWidth, dp2px(62f))
mItemHeight = a.getDimension(R.styleable.UnitSelectView_itemHeight, dp2px(30f))
mContext.resources.getDimensionPixelSize(com.guide.lib_common.R.dimen.lib_common_60dp)
mSelectStr = a.getString(R.styleable.UnitSelectView_selectStr) ?: mContext.getString(
com.guide.lib_common.R.string.test
)
//test code 方便预览
mSontexts = mSelectStr.split(",")
mChooseSontexts = mSelectStr.split(",")
Log.d("qqq", "mSelectStr " + mSontexts)
mTotalCount = mSontexts.size
//test code 方便预览
mSelectColor = a.getColor(
R.styleable.UnitSelectView_selectColor, ContextCompat.getColor(
mContext, R.color.lib_common_c1c1c1
)
)
mUnselectColor = a.getColor(
R.styleable.UnitSelectView_unselectColor, ContextCompat.getColor(
mContext, R.color.lib_common_white
)
)
mBoardColor = a.getColor(
R.styleable.UnitSelectView_boardColor, ContextCompat.getColor(
mContext, R.color.lib_common_c1c1c1
)
)
mBackColor = a.getColor(
R.styleable.UnitSelectView_backColor, ContextCompat.getColor(
mContext, R.color.lib_common_222124
)
)
a.recycle()
init()
}
private fun init() {
setWillNotDraw(false)
mPadding = dp2px(3F)
mStrokeWidth = dp2px(1F)
mOffest = dp2px(1F)
mRadiusSize = dp2px(8F)
initPaint()
}
private fun initPaint() {
mTextPaint = TextPaint()
mTextPaint.color = mSelectColor
mTextPaint.textSize = mTextsize
mTextPaint.style = Paint.Style.FILL
mBoardPaint = Paint()
mBoardPaint.color = mBoardColor
mBoardPaint.isAntiAlias = true
mBoardPaint.style = Paint.Style.STROKE
mBoardPaint.strokeWidth = mStrokeWidth
mBackPaint = Paint()
mBackPaint.color = mBackColor
mBackPaint.isAntiAlias = true
mBackPaint.style = Paint.Style.FILL
// fatherWidth = getTextW(textPaint, mSelectStr) + mPadding
// fatherHeight = getTextH(textPaint, mSelectStr) + mPadding / 2
// 宽度限制死
// mFatherWidth = mItemWidth * mTotalCount + mPadding * 2
// mFatherHeight = mItemHeight + mPadding * 2
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (isTouch) {
mTextPaint.color = mUnselectColor
} else {
mTextPaint.color = mSelectColor
}
//画背景
canvas.drawRoundRect(
mFatherRectF.left + mOffest,
mFatherRectF.top + mOffest,
mFatherRectF.right - mOffest,
mFatherRectF.bottom - mOffest,
mRadiusSize, mRadiusSize, mBackPaint
)
//画外边框
mBoardPaint.style = Paint.Style.STROKE
mBoardPaint.color = mBoardColor
canvas.drawRoundRect(
mFatherRectF.left + mOffest,
mFatherRectF.top + mOffest,
mFatherRectF.right - mOffest,
mFatherRectF.bottom - mOffest,
mRadiusSize,
mRadiusSize,
mBoardPaint
)
//画小框
mBoardPaint.style = Paint.Style.FILL
mBoardPaint.color = mSelectColor
mSonRectFs.forEachIndexed { index, it ->
Log.d("qqq", "画小框 index" + index)
Log.d("qqq", "画小框 " + mSonRectFs)
Log.d("qqq", "画小框 " + mSonRectFs.size)
Log.d("qqq", "画小框 " + mSontexts.size)
Log.d("qqq", "画小框 " + mChooseSontexts.size)
Log.d("qqq", "画小框 " + mSontexts)
Log.d("qqq", "画小框 it.right " + it.right)
if (index == touchIndex) {
mTextPaint.color = mSelectColor
canvas.drawRoundRect(it, mRadiusSize, mRadiusSize, mBoardPaint)
mTextPaint.color = Color.WHITE
canvas.drawText(
mChooseSontexts[index],
(it.left + it.right - getTextW(mTextPaint, mChooseSontexts[index])) / 2,
(getTextH(mTextPaint, mChooseSontexts[index]) + it.top + it.bottom) / 2,
mTextPaint
)
} else {
mTextPaint.color = mUnselectColor
canvas.drawText(
mChooseSontexts[index],
(it.left + it.right - getTextW(mTextPaint, mChooseSontexts[index])) / 2,
(getTextH(mTextPaint, mChooseSontexts[index]) + it.top + it.bottom) / 2,
mTextPaint
)
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//不能超过父布局大小
if (mFatherWidth > widthSize) {
mFatherWidth = widthSize.toFloat()
}
recreatRectF()
Log.d("ssss", "widthMode " + widthMode)
Log.d("ssss", "widthSize " + widthSize)
Log.d("ssss", "heightSize " + heightSize)
Log.d("ssss", "mItemHeight " + mItemHeight)
Log.d("ssss", "mFatherHeight " + mFatherHeight)
}
private fun recreatRectF() {
mFatherWidth = mItemWidth * mTotalCount + mPadding * 2
mFatherHeight = mItemHeight + mPadding * 2
mFatherRectF = RectF(0F, 0F, mFatherWidth, mFatherHeight)
mSonRectFs = ArrayList()
//画小框
for (i in 0 until mTotalCount) {
mSonRectFs.add(
RectF(
mItemWidth * i + mPadding, mPadding,
mItemWidth + mItemWidth * i + mPadding, mItemHeight + mPadding
)
)
}
setMeasuredDimension(mFatherWidth.toInt(), mFatherHeight.toInt())
}
private fun dp2px(dpValue: Float): Float {
val scale = Resources.getSystem().displayMetrics.density
return dpValue * scale + 0.5f
}
private fun getTextH(pFont: TextPaint, text: String): Int {
val rect = Rect()
pFont.getTextBounds(text, 0, text.length, rect)
return rect.height()
}
private fun getTextW(pFont: TextPaint, text: String): Float {
return pFont.measureText(text)
}
var isTouch = false
var touchIndex = -1
var lastAction: Int = -1
var actionDownTime: Long = 0
var actionUpTime: Long = 0L
var clickSpaceTime: Long = 250L
var lastClickTime: Long = 0L
override fun onTouchEvent(event: MotionEvent): Boolean {
if (MotionEvent.ACTION_DOWN == event.action) {
actionDownTime = System.currentTimeMillis()
lastAction = MotionEvent.ACTION_DOWN
isTouch = true
invalidate()
} else if (MotionEvent.ACTION_MOVE == event.action) {
lastAction = MotionEvent.ACTION_MOVE
isTouch = true
invalidate()
} else if (MotionEvent.ACTION_UP == event.action) {
lastAction = MotionEvent.ACTION_UP
actionUpTime = System.currentTimeMillis()
val canClickSpace = actionUpTime - lastClickTime > clickSpaceTime
val isClick = actionUpTime - actionDownTime < clickSpaceTime
if (canClickSpace && isClick) {
lastClickTime = actionUpTime
var ret = -1
for ((index, it) in mSonRectFs.withIndex()) {
if (it.contains(event.x, event.y)) {
touchIndex = index
ret = index
break
}
}
if (touchIndex == ret) {
mOnClickListener?.onClick(touchIndex)
} else {
mOnClickListener?.outOftouch()
}
}
isTouch = false
invalidate()
}
return true
}
fun setUnits(
list: List<Pair<String, String>>,
bean: Enum<*>,
mOnClickListener: OnClickListener
) {
this.mOnClickListener = mOnClickListener
mSontexts = list.map { it.first }
mChooseSontexts = list.map { it.second }
mTotalCount = mSontexts.size
touchIndex = mSontexts.indexOf(bean.name)
recreatRectF()
postInvalidate()
}
}
使用
unitSelect.setUnits(arr, value,
object : UnitSelectView.OnClickListener {
override fun onClick(touchIndex: Int) {
Log.d( "qqq","AppUnitList ========================================="+ touchIndex)
}
override fun outOftouch() {
}
})
Indicator
效果图
源代码
public class Indicator extends View {
private static final int DEFAULT_TOTAL_INDEX = 5;
private static final int DEFAULT_CURRENT_INDEX = 0;
private static final int DEFAULT_CIRCLE_DISTANCE = 40;
private static final int DEFAULT_CIRCLE_RADIUS = 8;
private static final int DEFAULT_CIRCLE_SELECTED_RADIUS = 11;
private int selectedColor;
private int unselectedColor;
private int currentIndex;
private int totalIndex;
private Paint paint;
private int startX;
private int startSelectedY;
private int startY;
private int centreX;
public Indicator(Context context) {
this(context, null);
}
public Indicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Indicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.Indicator, defStyleAttr, 0);
selectedColor = typedArray.getColor(R.styleable.Indicator_selectedColor, Color.LTGRAY);
unselectedColor = typedArray.getColor(R.styleable.Indicator_unselectedColor, Color.WHITE);
typedArray.recycle();
totalIndex = DEFAULT_TOTAL_INDEX;
currentIndex = DEFAULT_CURRENT_INDEX;
paint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centreX = getWidth() / 2;
startSelectedY = getHeight() / 2 - DEFAULT_CIRCLE_SELECTED_RADIUS;
startY = getHeight() / 2 - DEFAULT_CIRCLE_RADIUS;
if (totalIndex % 2 == 0){
startX = centreX - (int)(1.0 * (totalIndex - 1)/2 * DEFAULT_CIRCLE_DISTANCE);
}else{
startX = centreX - totalIndex / 2 * DEFAULT_CIRCLE_DISTANCE;
}
paint.setAntiAlias(true);
paint.setColor(unselectedColor);
int tempX = startX;
for(int i = 0 ; i < totalIndex ; i++ ){
RectF rectF = new RectF(tempX - DEFAULT_CIRCLE_RADIUS,startY,
tempX + DEFAULT_CIRCLE_RADIUS,startY + 2 * DEFAULT_CIRCLE_RADIUS);
if (i == currentIndex) {
paint.setColor(selectedColor);
rectF = new RectF(tempX - DEFAULT_CIRCLE_SELECTED_RADIUS,startSelectedY,
tempX + DEFAULT_CIRCLE_SELECTED_RADIUS,startSelectedY + 2 * DEFAULT_CIRCLE_SELECTED_RADIUS);
}
canvas.drawOval(rectF,paint);
if (paint.getColor() == selectedColor)
paint.setColor(unselectedColor);
tempX += DEFAULT_CIRCLE_DISTANCE;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
private int measureHeight(int measureSpec){
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int desired = DEFAULT_CIRCLE_SELECTED_RADIUS * 2 + getPaddingBottom() + getPaddingTop();
if(specMode == MeasureSpec.EXACTLY) {
result = Math.max(desired,specSize);
}else{
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(desired,specSize);
}
else result = desired;
}
return result;
}
private int measureWidth(int measureSpec){
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int desired = (totalIndex - 1) * DEFAULT_CIRCLE_DISTANCE + DEFAULT_CIRCLE_SELECTED_RADIUS * 2 + getPaddingLeft() + getPaddingRight();
if(specMode == MeasureSpec.EXACTLY) {
result = Math.max(desired,specSize);
}else{
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(desired,specSize);
}else result = desired;
}
return result;
}
public void setTotalIndex(int totalIndex){
int oldTotalIndex = this.totalIndex;
if (totalIndex < 1)
return;
if (totalIndex < oldTotalIndex){
if (currentIndex == totalIndex )
currentIndex = totalIndex - 1;
}
this.totalIndex = totalIndex;
invalidate();
}
public void setCurrentIndex(int currentIndex){
this.currentIndex = currentIndex;
invalidate();
}
}
Style
<resources>
<attr name="selectedColor" format="color" />
<attr name="unselectedColor" format="color" />
<declare-styleable name="Indicator">
<attr name="selectedColor" />
<attr name="unselectedColor" />
</declare-styleable>
</resources>
使用
code
indicator.setCurrentIndex(position)
xml
<com.guide.lib_common.components.Indicator
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="@dimen/lib_common_40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="@+id/viewPager"
app:layout_constraintRight_toRightOf="@+id/viewPager"
app:selectedColor="@color/lib_common_959595"
app:unselectedColor="@color/lib_common_F2F2F7">
</com.guide.lib_common.components.Indicator>