FontResize
FontResize是一個仿微信/支付寶 字型大小 調整控制元件的庫
gradle依賴
implementation 'com.ljx.view:fontresize:1.0.1'
複製程式碼
原始碼下載 該庫裡面只有一個類FontResizeView
,故不用擔心庫很大。
效果圖
屬性說明
<declare-styleable name="FontResizeView">
<attr name="minSize" format="reference|dimension" /> <!--最小size-->
<attr name="maxSize" format="reference|dimension" /> <!--最大size-->
<attr name="totalGrade" format="integer" /> <!--可調整的等級、即總的等級-->
<attr name="standardGrade" format="integer" /> <!--標準等級-->
<attr name="leftText" format="reference|string" /> <!--左邊文字,預設為 'A'-->
<attr name="middleText" format="reference|string" /> <!--中間文字,預設為 '標準'-->
<attr name="rightText" format="reference|string" /> <!--右邊文字,預設為 'A'-->
<attr name="leftTextColor" format="reference|color" /> <!--左邊文字顏色,預設為全黑-->
<attr name="middleTextColor" format="reference|color" /> <!--中間文字顏色,預設為全黑-->
<attr name="rightTextColor" format="reference|color" /> <!--右邊文字顏色,預設為全黑-->
<attr name="sliderRadius" format="reference|dimension" /> <!--滑塊半徑-->
<attr name="sliderColor" format="reference|color" /> <!--滑塊顏色-->
<attr name="sliderShadowColor" format="reference|color" /> <!--滑塊邊緣陰影顏色-->
<attr name="lineColor" format="reference|color" /> <!--線條顏色-->
<attr name="lineStrokeWidth" format="reference|dimension" /> <!--線條寬度,即粗細程度-->
<attr name="horizontalLineLength" format="reference|dimension" /> <!--橫向線條長度-->
<attr name="verticalLineLength" format="reference|dimension" /> <!--縱向線條長度-->
</declare-styleable>
複製程式碼
xml配置
<com.ljx.view.FontResizeView
android:id="@+id/font_resize_view"
android:layout_width="match_parent"
android:layout_height="140dp"
android:background="@android:color/white"
app:maxSize="25sp"
app:minSize="13sp"
app:sliderColor="@android:color/white"
app:sliderRadius="12dp"
app:standardGrade="2"
app:totalGrade="7" />
複製程式碼
設定回撥
FontResizeView fontResizeView = findViewById(R.id.font_resize_view);
fontResizeView.setOnFontChangeListener(new OnFontChangeListener() {
@Override
public void onFontChange(float fontSize) {
//字型size改變回撥 單位:sp
}
});
複製程式碼
這樣配置過後,程式碼跑起來效果就跟上面的 gif 動圖一樣
我們把所有屬性都加上,看看效果
<com.ljx.view.FontResizeView
android:id="@+id/font_resize_view"
android:layout_width="match_parent"
android:layout_height="140dp"
android:background="@android:color/white"
app:horizontalLineLength="300dp"
app:leftText="AA"
app:leftTextColor="#FF0000"
app:lineColor="#009999"
app:lineStrokeWidth="2dp"
app:maxSize="31sp"
app:minSize="15sp"
app:middleText="標準1"
app:middleTextColor="#00FF00"
app:rightText="AAA"
app:rightTextColor="#0000FF"
app:sliderColor="@android:color/white"
app:sliderRadius="13dp"
app:sliderShadowColor="#FF0000"
app:standardGrade="3"
app:totalGrade="9"
app:verticalLineLength="15dp" />
複製程式碼
效果如下
相信以上屬性已經能滿足大部分場景,如果不能滿足您的場景,請提出您的需求,我會第一時間回應您。強烈推薦
RxLife 一款輕量級別的RxJava生命週期管理庫(一)
最後,放出原始碼
/**
* 字型大小調整控制元件
* User: ljx
* Date: 2018/05/11
* Time: 09:53
*/
public class FontResizeView extends View {
//預設線條顏色
private static final int DEFAULT_LINE_COLOR = Color.parseColor("#222222");
private boolean isCoincide;//是否重合
private int width, height;//FontAdjustView的寬高
private float minSize;//最小字型大小
private float maxSize;//最大字型大小
private float standardSize;//標準字型大小
private String leftText; //左邊文字
private String middleText; //中間文字
private String rightText; //右邊文字
private int leftTextColor; //左邊文字顏色
private int middleTextColor; //中間文字顏色
private int rightTextColor; //右邊文字顏色
private int totalGrade; //總的等級
private int standardGrade; //標準等級
private int lineColor; //線條顏色
private int horizontalLineLength; //橫向線段長度
private int verticalLineLength; //縱向線段長度
private int lineStrokeWidth; //線條寬度
private int lineAverageWidth; //每段水平線條的長度
private int sliderGrade; //滑塊等級
private int sliderColor; //滑塊顏色
private int sliderShadowColor; //滑塊陰影顏色
private Point sliderPoint; //滑塊位置
private Paint mPaint;//畫筆
private Line mHorizontalLine; //一條橫線
private Line[] mVerticalLines; //n條豎線
private GestureDetector mGestureDetector; //手勢檢測
private OnFontChangeListener onFontChangeListener; //字型size改變監聽器
public FontResizeView(Context context) {
this(context, null);
}
public FontResizeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FontResizeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int padding = dp2px(35);
setPadding(padding, padding, padding, padding);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontResizeView);
minSize = ta.getDimension(R.styleable.FontResizeView_minSize, dp2px(15));
maxSize = ta.getDimension(R.styleable.FontResizeView_maxSize, dp2px(25));
totalGrade = ta.getInt(R.styleable.FontResizeView_totalGrade, 6);
standardGrade = ta.getInt(R.styleable.FontResizeView_standardGrade, 2);
if (standardGrade < 1 || standardGrade > 6) {
standardGrade = 1;
}
sliderGrade = standardGrade;
leftText = ta.getString(R.styleable.FontResizeView_leftText);
if (TextUtils.isEmpty(leftText)) leftText = "A";
middleText = ta.getString(R.styleable.FontResizeView_middleText);
if (TextUtils.isEmpty(middleText))
middleText = context.getString(R.string.font_resize_standard);
rightText = ta.getString(R.styleable.FontResizeView_rightText);
if (TextUtils.isEmpty(rightText)) rightText = "A";
leftTextColor = ta.getColor(R.styleable.FontResizeView_leftTextColor, Color.BLACK);
middleTextColor = ta.getColor(R.styleable.FontResizeView_middleTextColor, Color.BLACK);
rightTextColor = ta.getColor(R.styleable.FontResizeView_rightTextColor, Color.BLACK);
lineColor = ta.getColor(R.styleable.FontResizeView_lineColor, DEFAULT_LINE_COLOR);
lineStrokeWidth = ta.getDimensionPixelOffset(R.styleable.FontResizeView_lineStrokeWidth, dp2px(0.5f));
horizontalLineLength = ta.getDimensionPixelOffset(R.styleable.FontResizeView_horizontalLineLength, -1);
verticalLineLength = ta.getDimensionPixelOffset(R.styleable.FontResizeView_verticalLineLength, -1);
sliderColor = ta.getColor(R.styleable.FontResizeView_sliderColor, Color.WHITE);
sliderShadowColor = ta.getColor(R.styleable.FontResizeView_sliderShadowColor, Color.GRAY);
float sliderRadius = ta.getDimension(R.styleable.FontResizeView_sliderRadius, dp2px(25));
ta.recycle();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
width = getResources().getDisplayMetrics().widthPixels;
height = dp2px(140);
standardSize = (maxSize - minSize) / (totalGrade - 1) * (standardGrade - 1) + minSize;
mHorizontalLine = new Line();
mVerticalLines = new Line[totalGrade];
for (int i = 0; i < mVerticalLines.length; i++) {
mVerticalLines[i] = new Line();
}
sliderPoint = new Point(sliderRadius);
mGestureDetector = new GestureDetector(context, gestureListener);
}
GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
isCoincide = sliderPoint.coincide(e.getX(), e.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return super.onDown(e);
}
/**
* 單擊事件
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
final Line horizontalLine = mHorizontalLine;
float x = e.getX();
if (x > horizontalLine.stopX) {
x = horizontalLine.stopX;
} else if (x < horizontalLine.startX) {
x = horizontalLine.startX;
}
moveSlider(x - horizontalLine.startX, true);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
getParent().requestDisallowInterceptTouchEvent(true);
if (isCoincide) {
float x = sliderPoint.getX();
setSliderPointX(x - distanceX, false);
postInvalidate();
return true;
}
return super.onScroll(e1, e2, distanceX, distanceY);
}
};
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);
int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);
switch (specWidthMode) {
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
width = Math.min(width, specWidthSize);
break;
case MeasureSpec.EXACTLY:
width = specWidthSize;
break;
}
switch (specHeightMode) {
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
height = Math.min(height, specHeightSize);
break;
case MeasureSpec.EXACTLY:
height = specHeightSize;
break;
}
setMeasuredDimension(width, height);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (horizontalLineLength == -1)
horizontalLineLength = w - getPaddingLeft() - getPaddingRight();
if (verticalLineLength == -1) verticalLineLength = dp2px(10);
lineAverageWidth = horizontalLineLength / (totalGrade - 1);
//初始化橫線起點位置
int horizontalLineStartX = (width - horizontalLineLength) / 2;
int horizontalLineStartY = (int) (height * 0.6);
//初始化橫線起點、終點位置
mHorizontalLine.set(horizontalLineStartX, horizontalLineStartY, horizontalLineStartX + horizontalLineLength, horizontalLineStartY);
float lineAverageWidth = horizontalLineLength * 1.0f / (totalGrade - 1);
final Line[] verticalLines = mVerticalLines;
for (int i = 0; i < verticalLines.length; i++) {
float startX = horizontalLineStartX + lineAverageWidth * i;
verticalLines[i].set(startX, horizontalLineStartY - verticalLineLength / 2f, startX, horizontalLineStartY + verticalLineLength / 2f);
}
//初始化滑塊的等級及位置
sliderPoint.setGrade(sliderGrade - 1);
setSliderPointX(verticalLines[sliderGrade - 1].startX, true);
sliderPoint.setY(verticalLines[sliderGrade - 1].startY + verticalLines[sliderGrade - 1].getHeight() / 2);
}
@Override
protected void onDraw(Canvas canvas) {
final Line horizontalLine = mHorizontalLine;
mPaint.setColor(lineColor);
mPaint.setStrokeWidth(lineStrokeWidth);
//繪製橫線
canvas.drawLine(horizontalLine.startX, horizontalLine.startY, horizontalLine.stopX, horizontalLine.stopY, mPaint);
//繪製線段
for (Line line : mVerticalLines) {
canvas.drawLine(line.startX, line.startY, line.stopX, line.stopY, mPaint);
}
//繪製左邊文字
mPaint.setColor(leftTextColor);
mPaint.setTextSize(minSize);
float width = mPaint.measureText(leftText);
float startY = horizontalLine.startY - dp2px(20);
canvas.drawText(leftText, horizontalLine.startX - width / 2, startY, mPaint);
//繪製右邊文字
mPaint.setColor(rightTextColor);
mPaint.setTextSize(maxSize);
width = mPaint.measureText(rightText);
canvas.drawText(rightText, horizontalLine.stopX - width / 2, startY, mPaint);
//繪製中間文字
mPaint.setColor(middleTextColor);
mPaint.setTextSize(standardSize);
width = mPaint.measureText(middleText);
float startX = mVerticalLines[standardGrade - 1].startX - width / 2;
if (standardGrade == 1 || standardGrade == totalGrade) {
startY -= dp2px(7) + standardSize;
}
canvas.drawText(middleText, startX, startY, mPaint);
//繪製 滑塊
mPaint.setColor(sliderColor);
float radius = sliderPoint.getRadius();
mPaint.setShadowLayer(10f, 2, 2, sliderShadowColor);
canvas.drawCircle(sliderPoint.getX(), sliderPoint.getY(), radius, mPaint);
mPaint.setShadowLayer(0, 0, 0, sliderShadowColor);//關閉陰影
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event)) {
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP && isCoincide) {
//手指抬起時,滑塊不在整點,則移動到整點
float x = sliderPoint.getX() - mHorizontalLine.startX;
moveSlider(x, false);
}
return true;
}
/**
* 移動滑塊
*/
private void moveSlider(float destX, final boolean isClick) {
int grade = (int) destX / lineAverageWidth;//目標等級
float remainder = destX % lineAverageWidth;
if (remainder > lineAverageWidth / 2) grade++;
final int tempGrade = grade;
int gradeDiffer = Math.abs(sliderPoint.getGrade() - tempGrade);
if (gradeDiffer == 0) {
if (isClick) return;
gradeDiffer = 1;
}
ValueAnimator animator = ValueAnimator.ofFloat(sliderPoint.getX(), mVerticalLines[tempGrade].startX);
animator.setDuration(100 + gradeDiffer * 30);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
//如果是單擊觸發的移動事件,動畫未結束前僅更新滑塊的位置,動畫結束後再更新滑塊的等級
setSliderPointX(value, isClick);
postInvalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setSliderPointX(mVerticalLines[tempGrade].startX, false);
}
});
animator.start();
}
/**
* 設定滑塊X座標的統一入口
*
* @param onlySetX 是否僅僅更新滑塊的位置
*/
private void setSliderPointX(float x, boolean onlySetX) {
float horizontalLineStartX = mHorizontalLine.startX;
float horizontalLineStopX = mHorizontalLine.stopX;
if (x < horizontalLineStartX) {
x = horizontalLineStartX;
} else if (x > horizontalLineStopX) {
x = horizontalLineStopX;
}
sliderPoint.setX(x);
if (onlySetX) return;
int oldGrade = sliderPoint.getGrade();
int newGrade = (int) (x - horizontalLineStartX) / lineAverageWidth;
if (oldGrade == newGrade) return;
sliderPoint.setGrade(newGrade);
if (onFontChangeListener != null) {
float size = (maxSize - minSize) / (totalGrade - 1);
float sp = (minSize + size * newGrade) / getResources().getDisplayMetrics().scaledDensity;
onFontChangeListener.onFontChange(sp);
}
}
/**
* @return 返回當前設定的字型大小 單位 :sp
*/
public float getFontSize() {
float size = (maxSize - minSize) / (totalGrade - 1);
return (minSize + size * (sliderGrade - 1)) / getResources().getDisplayMetrics().scaledDensity;
}
/**
* 設定當前字型大小
*
* @param fontSize 字型大小 單位 :sp
*/
public void setFontSize(float fontSize) {
fontSize *= getResources().getDisplayMetrics().scaledDensity;
int grade = (int) ((fontSize - minSize) / ((maxSize - minSize) / (totalGrade - 1))) + 1;
setSliderGrade(grade);
}
/**
* 設定滑塊等級
*
* @param grade 滑塊等級
*/
public void setSliderGrade(int grade) {
if (grade < 0) grade = 1;
if (grade > totalGrade) grade = totalGrade;
sliderGrade = grade;
}
private int dp2px(float dipValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
public void setOnFontChangeListener(OnFontChangeListener onFontChangeListener) {
this.onFontChangeListener = onFontChangeListener;
}
public interface OnFontChangeListener {
/**
* @param fontSize 字型大小、單位sp
*/
void onFontChange(float fontSize);
}
class Point {
//滑塊位置及半徑
float x;
float y;
float radius;
int grade; //滑塊等級
Point(float radius) {
this.radius = radius;
}
float getX() {
return x;
}
void setX(float x) {
this.x = x;
}
float getY() {
return y;
}
void setY(float y) {
this.y = y;
}
float getRadius() {
return radius;
}
int getGrade() {
return grade;
}
void setGrade(int grade) {
this.grade = grade;
}
//判斷手指按下的點是否與滑塊重合
boolean coincide(float movingX, float movingY) {
//開方,如果兩點之間的距離小於規定的半徑r則定義為重合
return Math.sqrt((x - movingX) * (x - movingX)
+ (y - movingY) * (y - movingY)) < radius + dp2px(20);
}
}
class Line {
float startX;
float startY;
float stopX;
float stopY;
void set(float startX, float startY, float stopX, float stopY) {
this.startX = startX;
this.startY = startY;
this.stopX = stopX;
this.stopY = stopY;
}
float getHeight() {
return Math.abs(stopY - startY);
}
}
}
複製程式碼
本文為原創文章,如需轉載,請註明出處,??。