BottomDotTextView--底部有个小圆点
BottomDotTextView
package com.android.myapplication.demo5;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.Layout;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.WindowManager;
import android.view.animation.BounceInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
import com.android.myapplication.demo6.utils.UiUtils;
import androidx.annotation.Nullable;
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
@SuppressLint("AppCompatCustomView")
public class BottomRopTextView extends TextView {
private static final int DEFAULT_CIRCLE_RADIUS = 2;
private static final int DEFAULT_CIRCLE_COLOR = Color.RED;
private static final int DEFAULT_TEXT_COLOR = Color.WHITE;//Color.WHITE
private static final int DEFAULT_TOP_GAP = 8;
private static final int DEFAULT_CLOCK_VIEW_WIDTH = 150;
private static final String DEFAULT_TEXT_VALUE = "开关";
private int mScaleViewWidth;
private int mScaleViewHeight;
private int mScaleViewGap;
private int mScaleViewRadius;
private int mCirCleColor;
private int mDigitalTimeTextStartX;
private int mDigitalTimeTextStartY;
private int mScaleTextSize;
private int mScaleTextColor;
private String mScaleText;
private Paint mPaint;
private Rect textRect;//控制文本范围
private Rect mClockViewRect;//边框
/**
* 是否显示
*/
private boolean isShowDot = false;
private boolean refreshIImmediately = true;
public BottomRopTextView(Context context) {
this(context, null);
}
public BottomRopTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomRopTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScaleViewGap = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_TOP_GAP, getResources().getDisplayMetrics());
mScaleViewRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CIRCLE_RADIUS, getResources().getDisplayMetrics());
mScaleTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
20, getResources().getDisplayMetrics());
mScaleViewWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,DEFAULT_CLOCK_VIEW_WIDTH,
getResources().getDisplayMetrics());
mScaleText = DEFAULT_TEXT_VALUE;
mScaleTextColor = DEFAULT_TEXT_COLOR;
mCirCleColor = DEFAULT_CIRCLE_COLOR;
//init
textRect = new Rect();
mClockViewRect = new Rect();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(mScaleTextColor);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(mScaleTextSize);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int len = w > h ? h : w;
mClockViewRect.set(0, 0, len, len);
mClockViewRect.offset((w - len) / 2, (h - len) / 2);
int mClockViewCenterX = mClockViewRect.centerX();
int mClockViewCenterY = mClockViewRect.centerY();
mDigitalTimeTextStartX = mClockViewCenterX - textRect.left - textRect.width() / 2;
mDigitalTimeTextStartY = mClockViewCenterY - textRect.top - textRect.height() / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//拿到Text的宽高
mPaint.getTextBounds(mScaleText, 0, mScaleText.length(), textRect);
canvas.drawText(mScaleText, mDigitalTimeTextStartX, mDigitalTimeTextStartY, mPaint);
canvas.save();
// 控制小球绘制的位置
float x = (textRect.width() + getPaddingRight() + getPaddingLeft()) / 2;
float circleX = mDigitalTimeTextStartX + x;
float circleY = mDigitalTimeTextStartY + mScaleViewGap;
//是否绘制小球
if (isShowDot) {
canvas.drawCircle(circleX,circleY,mScaleViewRadius, mPaint);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthSpecMode != MeasureSpec.EXACTLY) {
widthMeasureSpec = mScaleViewWidth;
}
if (heightSpecMode != MeasureSpec.EXACTLY) {
}
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
private ValueAnimator valueAnimator;
public void performAnimation(){
cancelAnimation();
valueAnimator = ValueAnimator.ofInt(0, mScaleViewRadius);
valueAnimator.setDuration(300);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mScaleViewRadius = (int) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.start();
}
public void cancelAnimation() {
if (valueAnimator != null) {
valueAnimator.removeAllUpdateListeners();
valueAnimator.removeAllListeners();
valueAnimator.cancel();
valueAnimator = null;
}
}
public void isRefreshIImmediately() {
//invalidate();
//postInvalidate();
}
public void isShowDot(boolean isShow){
isShowDot = isShow;
//invalidate();
}
public void setCirCleColor(int cirCleColor) {
mCirCleColor = cirCleColor;
}
public void setDefaultTextColor(int textColor){
mScaleTextColor = textColor;
}
public void setDefaultCircleRadius(int circleRadius){
mScaleViewRadius = circleRadius;
}
public void setDefaultCircleGap(int circleGap){
mScaleViewGap = circleGap;
}
public void setDefaultTextValue(String textValue){
mScaleText = textValue;
}
}
半成品--失败
public class DotTextView extends TextView {
private static final int DEFAULT_CIRCLE_COLOR = Color.RED;
private static final int DEFAULT_CIRCLE_RADIUS = 2;
private static final int DEFAULT_TOP_GAP = 8;
private int cirCleColor;
private int circleRadius;
private int textToCircle_Y;
private Rect textRect;
private Paint mPaint;
private boolean isDebug;
private Paint debugPaint = new Paint();
public DotTextView(Context context) {
this(context, null);
}
public DotTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DotTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
textToCircle_Y = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TOP_GAP,
getResources().getDisplayMetrics());
circleRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_CIRCLE_RADIUS,
getResources().getDisplayMetrics());
cirCleColor = DEFAULT_CIRCLE_COLOR;
//init
textRect = new Rect();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(cirCleColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final Layout textLayout = getLayout();
final float contentTextLeftX = textLayout.getPrimaryHorizontal(0); //文字x起点
final float contentTextRightX = contentTextLeftX + textLayout.getLineWidth(0);//文字x终点
// 获取View本身文本的Paint对象,再得到文本的Rect对象
getPaint().getTextBounds(getText().toString(), 0, getText().length(), textRect);
final float circleX;
final float circleY;
final int Width = textRect.centerX();
final int Height = textRect.centerY();
//getTotalPaddingStart() ,getTotalPaddingStart(),getPaddingTop() padding 的值
circleX = contentTextRightX / 2 + getTotalPaddingStart();
circleY = getTotalPaddingTop() + getLineHeight() * textLayout.getLineCount() + textToCircle_Y;
Log.d("tww", "left" + contentTextLeftX);
Log.d("tww", "right" + contentTextRightX);
Log.d("tww", "circleX" + circleX);
/*
* left0.0
<com.android.myapplication.demo7.DotTextView
android:layout_width="200dp"
android:layout_height="60dp"
android:gravity="center_vertical"
android:text="开关"
android:textSize="25sp"
android:paddingLeft="100dp"
/>
*
* */
canvas.drawCircle(circleX, circleY, circleRadius, mPaint); // 画圆点
if (isDebug) {
debugPaint.setColor(Color.GRAY);
final int textWidth = textRect.width();
final int textHeight = textRect.height();
final String textAttr = String.format("[(文字总宽度=%s, 高度=%s, 行数=%s)]", textWidth, textHeight, textLayout.getLineCount());
canvas.drawText(textAttr, 0, textHeight, debugPaint);
final String viewAttr = String.format("[(DotTextView宽度=%s, 高度=%s)]", getWidth(), getHeight());
canvas.drawText(viewAttr, 0, textHeight * 2, debugPaint);
final String appAttr = String.format("[(每行文字高度(包含文字间距)=%s)]", getLineHeight());
canvas.drawText(appAttr, 0, textHeight * 3, debugPaint);
final String drawableAttr = String.format("[(左图标间距=%s,右图标间距=%s,上图标间距=%s,下图标间距=%s)]", getCompoundPaddingStart(), getCompoundPaddingEnd(), getCompoundPaddingTop(), getCompoundPaddingBottom());
canvas.drawText(drawableAttr, 0, textHeight * 4, debugPaint);
}
}
/*
* switch (dotGravity) {
case LEFT_TOP:
circleX = contentTextLeftX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + dotOffsetY;
break;
case RIGHT_TOP:
circleX = contentTextRightX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + dotOffsetY;
break;
case LEFT_BOTTOM:
circleX = contentTextLeftX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + getLineHeight() * textLayout.getLineCount() + dotOffsetY; // 总内间距 + 每行文字高度 * 文字行数 + 圆点上边距
break;
case RIGHT_BOTTOM:
circleX = contentTextRightX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + getLineHeight() * textLayout.getLineCount() + dotOffsetY; // 同 LEFT_BOTTOM
break;
case LEFT_CENTER:
circleX = contentTextLeftX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + (getLineHeight() * textLayout.getLineCount()) / 2 + dotOffsetY;
break;
case RIGHT_CENTER:
circleX = contentTextRightX + dotOffsetX + getTotalPaddingStart();
circleY = getTotalPaddingTop() + (getLineHeight() * textLayout.getLineCount()) / 2 + dotOffsetY;
break;
case LEFT_DRAWABLE_CENTER:
if (getCompoundDrawables()[0] == null) {
circleX = contentTextLeftX + dotOffsetX + getTotalPaddingStart();
} else {
circleX = contentTextLeftX + dotOffsetX;
}
circleY = getTotalPaddingTop() + (getLineHeight() * textLayout.getLineCount()) / 2 + dotOffsetY;
break;
case RIGHT_DRAWABLE_CENTER:
if (getCompoundDrawables()[2] == null) {
circleX = contentTextRightX + dotOffsetX + getTotalPaddingStart();
} else {
circleX = contentTextRightX + dotOffsetX + getTotalPaddingEnd() + getTotalPaddingStart();
}
circleY = getTotalPaddingTop() + (getLineHeight() * textLayout.getLineCount()) / 2 + dotOffsetY;
break;
default:
circleX = 0;
circleY = 0;
break;
}
*
* */
}
组合控件-MulDotTextView
点击查看代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:minHeight="48dp"
android:layout_height="48dp"
>
<TextView
android:id="@+id/tab_item"
android:text="开关"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="20sp"
android:textColor="@color/black"
android:gravity="center"
/>
<ImageView
android:id="@+id/iv_bottom"
android:layout_width="3dp"
android:layout_height="3dp"
android:layout_marginTop="-10dp"
android:src="@drawable/small_circle"
android:layout_below="@id/tab_item"
android:layout_centerInParent="true"
/>
</RelativeLayout>
//--------------分割线
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
>
<solid android:color="@color/white"/>
<size android:width="5dp" android:height="5dp"/>
</shape>
//-------------
public class MulDotTextView extends RelativeLayout {
private TextView mTabTextView;
private ImageView mDotView;
public MulDotTextView(Context context) {
this(context, null);
}
public MulDotTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MulDotTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater layoutInflater = LayoutInflater.from(context);
layoutInflater.inflate(R.layout.tab_dot_textview, this);
mTabTextView = findViewById(R.id.tab_item);
mDotView = findViewById(R.id.iv_bottom);
}
public void outAnimator() {
ObjectAnimator outAnimator = ObjectAnimator.ofFloat(mDotView, "alpha", 1f, 0);
DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
ObjectAnimator translationAnimator = outAnimator.ofFloat(mDotView, "translationX", 0, 20f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1500);
animatorSet.setInterpolator(decelerateInterpolator);
animatorSet.playTogether(translationAnimator,outAnimator);
animatorSet.start();
}
public void inAnimator() {
ObjectAnimator inAnimator = ObjectAnimator.ofFloat(mDotView, "alpha", 0, 1f);
DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
ObjectAnimator translationAnimator = inAnimator.ofFloat(mDotView, "translationX", -20f, 0);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1500);
animatorSet.setInterpolator(decelerateInterpolator);
animatorSet.playTogether(translationAnimator,inAnimator);
animatorSet.start();
}
public interface getNewAnimators {
void inAnimator();
void outAnimator();
}
}
//-----------判断viewPager左滑还是右滑
//记录上一次滑动的positionOffsetPixels值
int lastValue = -1;
boolean isLeft = true;
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Log.d("tww","positionOffset"+positionOffset);
//Log.d("tww","positionOffsetPixels"+positionOffsetPixels);
if(positionOffset != 0){
if(lastValue >= positionOffsetPixels){
//右滑
isLeft = false;
}else if(lastValue < positionOffsetPixels){
//左滑
isLeft = true;
}
}
//--> positionOffsetPixels ↑ <--positionOffsetPixels ↓
lastValue = positionOffsetPixels;
}
@Override
public void onPageSelected(int position) {
if (isLeft){
m1.inAnimator();
m2.outAnimator();
Log.d("tww","--->left");
}else {
m2.inAnimator();
m1.outAnimator();
Log.d("tww","--->right");
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
分类:
408-计算机基础
, Android-自定义控件-学习记录
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!