效果如下:
功能类:
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; i90){ 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) } }字体内容可私聊