效果如下:


功能类:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.Scroller;
import android.widget.TextView;
import com.nxwna.leartime.R;
import java.util.Calendar;
public class FlipLayout extends FrameLayout {
private TextView mVisibleTextView;//可见的
private TextView mInvisibleTextView;//不可见
private int layoutWidth;
private int layoutHeight;
private Scroller mScroller;
private String TAG = "FlipLayout";
private String timetag;//根据时间标记获取时间
private Camera mCamera = new Camera();
private Matrix mMatrix = new Matrix();
private Rect mTopRect = new Rect();
private Rect mBottomRect = new Rect();
private boolean isUp = true;
private Paint mminutenePaint = new Paint();
private Paint mShadePaint = new Paint();
private boolean isFlipping = false;
private boolean isCountDown = false;
private int maxNumber; //设置显示的最大值
private int flipTimes = 0;
private int timesCount = 0;
private FlipOverListener mFlipOverListener;
public FlipLayout(Context context) {
super(context, null);
}
public FlipLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FlipLayout);
int resId = array.getResourceId(R.styleable.FlipLayout_flipTextBackground,-1);
int color = Color.WHITE;
if(-1 == resId){
color = array.getColor(R.styleable.FlipLayout_flipTextBackground, Color.WHITE);
}
float size = array.getDimension(R.styleable.FlipLayout_flipTextSize,36);
size = px2dip(context,size);
int textColor = array.getColor(R.styleable.FlipLayout_flipTextColor, Color.BLACK);
array.recycle();
init(context,resId,color,size,textColor);
}
private void init(Context context, int resId, int color, float size, int textColor) {
mScroller = new Scroller(context,new DecelerateInterpolator());//减速 动画插入器
Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/Aura.otf");
mInvisibleTextView = new TextView(context);
mInvisibleTextView.setTextSize(size);
mInvisibleTextView.setText("00");
mInvisibleTextView.setGravity(Gravity.CENTER);
mInvisibleTextView.setIncludeFontPadding(false);
mInvisibleTextView.setTextColor(textColor);
mInvisibleTextView.setTypeface(tf);
if(resId == -1){
mInvisibleTextView.setBackgroundColor(color);
}else {
mInvisibleTextView.setBackgroundResource(resId);
}
addView(mInvisibleTextView);
mVisibleTextView = new TextView(context);
mVisibleTextView.setTextSize(size);
mVisibleTextView.setText("00");
mVisibleTextView.setGravity(Gravity.CENTER);
mVisibleTextView.setIncludeFontPadding(false);
mVisibleTextView.setTextColor(textColor);
mVisibleTextView.setTypeface(tf);
if(resId == -1){
mVisibleTextView.setBackgroundColor(color);
}else {
mVisibleTextView.setBackgroundResource(resId);
}
addView(mVisibleTextView);
mShadePaint.setColor(Color.BLACK);
mShadePaint.setStyle(Paint.Style.FILL);
mminutenePaint.setColor(Color.WHITE);
mminutenePaint.setStyle(Paint.Style.FILL);
}
public FlipLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public static float px2dip(Context context, float pxValue){
final float scale = context.getResources().getDisplayMetrics().density;
return pxValue / scale +0.5f;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
layoutWidth = MeasureSpec.getSize(widthMeasureSpec);
layoutHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(layoutWidth,layoutHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int count = getChildCount();
for(int i=0; i 90){
canvas.clipRect(isUp ? mTopRect : mBottomRect);
mCamera.rotateX(isUp ? deg - 180 : -(deg - 180));
view = mInvisibleTextView;
}else {
canvas.clipRect(isUp ? mBottomRect : mTopRect);
mCamera.rotateX(isUp ? deg : -deg);
view = mVisibleTextView ;
}
mCamera.getMatrix(mMatrix);
positionMatrix();
canvas.concat(mMatrix);
if(view != null){
drawChild(canvas,view,0);
}
drawFlippingShademinutene(canvas);
mCamera.restore();
canvas.restore();
}
private float getDeg() {
return mScroller.getCurrY() * 1.0f / layoutHeight * 180;
}
/**绘制翻页时的阳面和阴面*/
private void drawFlippingShademinutene(Canvas canvas) {
final float degreesFlipped = getDeg();
Log.d(TAG,"deg: " + degreesFlipped);
if (degreesFlipped < 90) {
final int alpha = getAlpha(degreesFlipped);
Log.d(TAG,"小于90度时的透明度-------------------> " + alpha);
mminutenePaint.setAlpha(alpha);
mShadePaint.setAlpha(alpha);
canvas.drawRect(isUp ? mBottomRect : mTopRect, isUp ? mminutenePaint : mShadePaint);
} else {
final int alpha = getAlpha(Math.abs(degreesFlipped - 180));
Log.d(TAG,"大于90度时的透明度-------------> " + alpha);
mShadePaint.setAlpha(alpha);
mminutenePaint.setAlpha(alpha);
canvas.drawRect(isUp ? mTopRect : mBottomRect, isUp ? mShadePaint : mminutenePaint);
}
}
private int getAlpha(float degreesFlipped) {
return (int) ((degreesFlipped / 90f) * 100);
}
private void positionMatrix() {
mMatrix.preScale(0.25f, 0.25f);
mMatrix.postScale(4.0f, 4.0f);
mMatrix.preTranslate(-getWidth() / 2, -getHeight() / 2);
mMatrix.postTranslate(getWidth() / 2, getHeight() / 2);
}
/**初始化隐藏textView显示的值*/
private void initCountTextView(int count) {
int visibleValue = count;
int invisibleValue = isUp ? visibleValue - 1 : visibleValue;
if(invisibleValue < 0){
invisibleValue += maxNumber;
}
if(invisibleValue >= maxNumber){
invisibleValue -= maxNumber;
}
String value = String.valueOf(invisibleValue);
if(value.length()<2){
value = "0" +value;
}
mInvisibleTextView.setText(value);
}
/**初始化隐藏textView显示的值*/
private void initTextView() {
int visibleValue = getTime();
int invisibleValue = isUp ? visibleValue - 1 : visibleValue;
if(invisibleValue < 0){
invisibleValue += maxNumber;
}
if(invisibleValue >= maxNumber){
invisibleValue -= maxNumber;
}
String value = String.valueOf(invisibleValue);
if(value.length()<2){
value = "0" +value;
}
mInvisibleTextView.setText(value);
}
/**初始化隐藏textView显示的值*/
private void initCoDownTextView(int count) {
int visibleValue = count;
String value = String.valueOf(visibleValue);
if(value.length()<2){
value = "0" +value;
}
mInvisibleTextView.setText(value);
}
/**根据传入的次数计算动画的时间
* 控制翻页速度
* */
private int getAnimDuration(int times) {
// Log.e(TAG,"计算用的次数: " + times);
if(times <= 0){
times = 1;
}
int animDuration = 500 - (500-100)/9 * times;
// Log.e(TAG, "animDuration: " + animDuration);
return animDuration;
}
public static interface FlipOverListener{
/**
* 翻页完成回调
* @param flipLayout 当前翻页的控件
*/
void onFLipOver(FlipLayout flipLayout);
}
//----------API-------------
/**
* 带动画翻动计时器
* 需要翻动几次
* @param value 需要翻动的次数
* @param isMinus 方向标识 true: 往上翻 - , false: 往下翻 +
*/
public void smoothCountFlip(int value,int maxnumber,String timeTAG, boolean isMinus,int count){
// Log.e(TAG,"翻动的次数: " + value);
timetag = timeTAG;
maxNumber = maxnumber;
if(value <= 0){
//回调接口
if(null != mFlipOverListener){
mFlipOverListener.onFLipOver(FlipLayout.this);
}
return;
}
flipTimes = value;
this.isUp = isMinus;
initCountTextView(count);
isFlipping = true;
mScroller.startScroll(0,0,0,layoutHeight,getAnimDuration(flipTimes - timesCount));
timesCount = 1;
postInvalidate();
}
/**
* 带动画翻动倒计时
* 需要翻动几次
* @param value 需要翻动的次数
* @param isMinus 方向标识 true: 往上翻 - , false: 往下翻 +
*/
public void smoothDownFlip(int value,int maxnumber,String timeTAG, boolean isMinus,int count){
// Log.e(TAG,"翻动的次数: " + value);
timetag = timeTAG;
maxNumber = maxnumber;
if(value <= 0){
//回调接口
if(null != mFlipOverListener){
mFlipOverListener.onFLipOver(FlipLayout.this);
}
return;
}
flipTimes = value;
this.isUp = isMinus;
initCoDownTextView(count);
isFlipping = true;
mScroller.startScroll(0,0,0,layoutHeight,getAnimDuration(flipTimes - timesCount));
timesCount = 1;
postInvalidate();
}
/**
* 带动画翻动
* 需要翻动几次
* @param value 需要翻动的次数
* @param isMinus 方向标识 true: 往上翻 - , false: 往下翻 +
*/
public void smoothFlip(int value,int maxnumber,String timeTAG, boolean isMinus){
// Log.e(TAG,"翻动的次数: " + value);
timetag = timeTAG;
maxNumber = maxnumber;
if(value <= 0){
//回调接口
if(null != mFlipOverListener){
mFlipOverListener.onFLipOver(FlipLayout.this);
}
return;
}
flipTimes = value;
this.isUp = isMinus;
initTextView();
isFlipping = true;
mScroller.startScroll(0,0,0,layoutHeight,getAnimDuration(flipTimes - timesCount));
timesCount = 1;
postInvalidate();
}
/**
* 不带动画翻动
* @param value
*/
public void flip(int value,int maxnumber,String timeTAG){
timetag = timeTAG;
maxNumber = maxnumber;
String text = String.valueOf(value);
if(text.length()<2){
text="0"+text;
}
mVisibleTextView.setText(text);
// if(null != mFlipOverListener){
// mFlipOverListener.onFLipOver(FlipLayout.this,timesCount);
// }
}
public void addFlipOverListener(FlipOverListener flipOverListener){
this.mFlipOverListener = flipOverListener;
}
public TextView getmVisibleTextView() {
return mVisibleTextView;
}
public TextView getmInvisibleTextView() {
return mInvisibleTextView;
}
public boolean isUp() {
return isUp;
}
public int getTimesCount() {
return timesCount;
}
/**
*
* @param resId 图片资源id
*/
public void setFlipTextBackground(int resId){
int count = getChildCount();
for(int i=0; i
xml布局:
用法:
先初始化数据
flip_1.flip(hourTime, 24, TimeTAG.hour)
flip_2.flip(minTime, 60, TimeTAG.min)
flip_3.flip(secondTime, 60, TimeTAG.sec)
private var oldHour: Int = 0
private var oldminute: Int = 0
private var oldsecond: Int = 0
计时功能
fun doCountDown(time: Long) {
val hour1: Int = TimeUtils.getHours(time / 1000).toInt()
val minute1: Int = TimeUtils.getMins(time / 1000).toInt()
val second1: Int = TimeUtils.getSeconds(time / 1000).toInt()
val hour = oldHour - hour1
val minute = oldminute - minute1
val second = oldsecond - second1
oldHour = hour1
oldminute = minute1
oldsecond = second1
if (hour >= 1 || hour == -23) {
flip_1.smoothDownFlip(1, 24, TimeTAG.hour, false, hour1)
}
if (minute >= 1 || minute == -59) {
flip_2.smoothDownFlip(1, 60, TimeTAG.min, false, minute1)
}
if (second >= 1 || second == -59) {
flip_3.smoothDownFlip(1, 60, TimeTAG.sec, false, second1)
}
}
字体内容可私聊