2024年安卓最新Android自定义控件之3D上下翻页效果的倒计时控件,2024年最新大厂面试问题

最后

考虑到文章的篇幅问题,我把这些问题和答案以及我多年面试所遇到的问题和一些面试资料做成了PDF文档

喜欢的朋友可以关注、转发、点赞 感谢!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


ids.xml 因为用到的是动态布局,为了能找到控件,得为每个控件设置一个id



<?xml version="1.0" encoding="utf-8"?>
 <item name="dayTextView" type="id" />

 <item name="hourTextView" type="id" />

 <item name="minTextView" type="id" />

 <item name="secTextView" type="id" />

time\_bg.xml 控件的样式



<?xml version="1.0" encoding="utf-8"?>
<corners android:radius="3dp" />



<!-- 实心 即填充颜色 -->

<solid android:color="#0B315C" />

<!-- 按钮文字和边缘距离(内边距) -->

<padding

    android:bottom="3dp"

    android:left="6dp"

    android:right="6dp"

    android:top="3dp" />

clock\_view\_bg.xml 控件外框的样式  

<?xml version="1.0" encoding="utf-8"?>
<corners android:radius="10dp" />



<!-- 实心 即填充颜色 -->

<solid android:color="#00000000" />



<stroke

    android:width="2dp"

    android:color="#2F80E9" />

<!-- 按钮文字和边缘距离(内边距) -->

<padding

    android:bottom="5dp"

    android:left="5dp"

    android:right="5dp"

    android:top="5dp" />

FlipClockView.java  

package com.example.timeticker;

import android.content.Context;

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.drawable.Drawable;

import android.util.AttributeSet;

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;

/**

日历3D翻转效果

*/

public class FlipClockView extends FrameLayout {

private TextView mVisibleTextView;// 可见的

private TextView mInvisibleTextView;// 不可见



private int layoutWidth;

private int layoutHeight;

private Scroller mScroller;



private Camera mCamera = new Camera();

private Matrix mMatrix = new Matrix();

private Rect mTopRect = new Rect();

private Rect mBottomRect = new Rect();

private boolean isUp2Down = true;

private Paint mShinePaint = new Paint();

private Paint mShadePaint = new Paint();

private boolean isFlipping = false;



public FlipClockView(Context context) {

	this(context, null);

}



public FlipClockView(Context context, AttributeSet attrs) {

	super(context, attrs, 0);

	init(context);

}



public FlipClockView(Context context, AttributeSet attrs, int defStyleAttr) {

	super(context, attrs, defStyleAttr);

}



public void init(Context context) {

	mScroller = new Scroller(context, new DecelerateInterpolator());// 减速 // 动画插入器



	mInvisibleTextView = new MyTextView(context);

	mInvisibleTextView.setText("0");

	mInvisibleTextView.setGravity(Gravity.CENTER);

	mInvisibleTextView.setIncludeFontPadding(false);

	addView(mInvisibleTextView);



	mVisibleTextView = new MyTextView(context);

	mVisibleTextView.setText("0");

	mVisibleTextView.setGravity(Gravity.CENTER);

	mVisibleTextView.setIncludeFontPadding(false);

	addView(mVisibleTextView);



	mShadePaint.setColor(Color.BLACK);

	mShadePaint.setStyle(Paint.Style.FILL);

	mShinePaint.setColor(Color.WHITE);

	mShinePaint.setStyle(Paint.Style.FILL);

}



@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();

	//将两个textView放置进去

	for (int i = 0; i < count; i++) {

		View child = getChildAt(i);

		child.layout(0, 0, layoutWidth, layoutHeight);

	}



	mTopRect.top = 0;

	mTopRect.left = 0;

	mTopRect.right = getWidth();

	mTopRect.bottom = getHeight() / 2;



	mBottomRect.top = getHeight() / 2;

	mBottomRect.left = 0;

	mBottomRect.right = getWidth();

	mBottomRect.bottom = getHeight();

}



@Override

protected void dispatchDraw(Canvas canvas) {

	super.dispatchDraw(canvas);

	if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {

		drawTopHalf(canvas);

		drawBottomHalf(canvas);

		drawFlipHalf(canvas);

		postInvalidate();

	} else {

		if (isFlipping) {

			showViews(canvas);

		}



		if (mScroller.isFinished() && !mScroller.computeScrollOffset()) {

			isFlipping = false;

		}

	}

}



/**

 * 显示需要显示的数字

 * 

 * @param canvas

 */

private void showViews(Canvas canvas) {

	String current = mVisibleTextView.getText().toString();

	String past = mInvisibleTextView.getText().toString();

	// Log.e("需要显示的数字--->",current+ "%%    "+past);



	mVisibleTextView.setText(past);

	mInvisibleTextView.setText(current);



	// 防止切换抖动

	drawChild(canvas, mVisibleTextView, 0);

}



/** 画下半部分 */

private void drawBottomHalf(Canvas canvas) {

	canvas.save();



	canvas.clipRect(mBottomRect);

	View drawView = !isUp2Down ? mInvisibleTextView : mVisibleTextView;

	drawChild(canvas, drawView, 0);



	canvas.restore();

}



/** 画上半部分 */

private void drawTopHalf(Canvas canvas) {

	canvas.save();



	canvas.clipRect(mTopRect);

	View drawView = !isUp2Down ? mVisibleTextView : mInvisibleTextView;

	drawChild(canvas, drawView, 0);



	canvas.restore();



}



/** 画翻页部分 */

private void drawFlipHalf(Canvas canvas) {

	canvas.save();

	mCamera.save();



	View view = null;

	float deg = getDeg();

	if (deg > 90) {

		canvas.clipRect(!isUp2Down ? mTopRect : mBottomRect);

		mCamera.rotateX(!isUp2Down ? deg - 180 : -(deg - 180));

		view = mInvisibleTextView;

	} else {

		canvas.clipRect(!isUp2Down ? mBottomRect : mTopRect);

		mCamera.rotateX(!isUp2Down ? deg : -deg);

		view = mVisibleTextView;

	}



	mCamera.getMatrix(mMatrix);

	positionMatrix();

	canvas.concat(mMatrix);



	if (view != null) {

		drawChild(canvas, view, 0);

	}



	drawFlippingShadeShine(canvas);



	mCamera.restore();

	canvas.restore();

}



private float getDeg() {

	return mScroller.getCurrY() * 1.0f / layoutHeight * 180;

}



/** 绘制翻页时的阳面和阴面 */

private void drawFlippingShadeShine(Canvas canvas) {

	final float degreesFlipped = getDeg();

	// Log.d(TAG, "deg: " + degreesFlipped);

	if (degreesFlipped < 90) {

		final int alpha = getAlpha(degreesFlipped);

		// Log.d(TAG, "小于90度时的透明度-------------------> " + alpha);

		mShinePaint.setAlpha(alpha);

		mShadePaint.setAlpha(alpha);

		canvas.drawRect(!isUp2Down ? mBottomRect : mTopRect, !isUp2Down ? mShinePaint

				: mShadePaint);

	} else {

		final int alpha = getAlpha(Math.abs(degreesFlipped - 180));

		// Log.d(TAG, "大于90度时的透明度-------------> " + alpha);

		mShadePaint.setAlpha(alpha);

		mShinePaint.setAlpha(alpha);

		canvas.drawRect(!isUp2Down ? mTopRect : mBottomRect, !isUp2Down ? mShadePaint

				: mShinePaint);

	}

}



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 initTextView() {

	int visibleValue = Integer.parseInt(mVisibleTextView.getText()

			.toString());

//	int invisibleValue = isUp2Down ? visibleValue - 1 : visibleValue + 1;//这里控制是 + 还是 -

	int invisibleValue = visibleValue - 1;

	if (invisibleValue < 10) {

		mInvisibleTextView.setText("0" + invisibleValue);

	} else {

		mInvisibleTextView.setText("" + invisibleValue);

	}

}



/**

 *

 * @param isUp2Down

 *     方向标识 true: 从上往下翻  , false: 从下往上翻

 */

public void setFlipDirection(boolean isUp2Down) {

    this.isUp2Down = isUp2Down;

}



public void smoothFlip() {

	//Log.e(TAG, "翻动 ");

	initTextView();

	isFlipping = true;



	mScroller.startScroll(0, 0, 0, layoutHeight, 700);

	postInvalidate();

}



public TextView getmVisibleTextView() {

	return mVisibleTextView;

}



public TextView getmInvisibleTextView() {

	return mInvisibleTextView;

}



public boolean isFlipping() {

	return isFlipping && !mScroller.isFinished()

			&& mScroller.computeScrollOffset();

}



/**

 * 获取当前View值

 * 

 * @return

 */

public int getCurrentValue() {

	return Integer.parseInt(mVisibleTextView.getText().toString());

}



/**

 * 设置view的时间值

 * @param textTime

 */

public void setClockTime(String textTime) {

	mVisibleTextView.setText(textTime);

}



/**

 * 设置时间数字的背景

 * @param drawable

 */

public void setClockBackground(Drawable drawable) {

	mVisibleTextView.setBackground(drawable);

	mInvisibleTextView.setBackground(drawable);

}



/**

 * 设置时间数字的颜色

 * @param color

 */

public void setClockTextColor(int color) {

	mVisibleTextView.setTextColor(color);

	mInvisibleTextView.setTextColor(color);

}



/**

 * 设置时间数字的大小

 * @param size

 */

public void setClockTextSize(float size){

	mVisibleTextView.setTextSize(size);

	mInvisibleTextView.setTextSize(size);

}

}


  



MyClockView.java



import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Color;

import android.graphics.drawable.Drawable;

import android.os.Handler;

import android.util.AttributeSet;

import android.util.Log;

import android.view.Gravity;

import android.view.ViewGroup;

import android.widget.RelativeLayout;

import android.widget.TextView;

public class MyClockView extends RelativeLayout {

private float dayTextSize, hourTextSize, minTextSize, secTextSize;

private FlipClockView dayTextView, hourTextView, minTextView, secTextView;

private TextView dTextView, hTextView, mTextView, sTextView;

private LayoutParams dayLayoutParams, hourLayoutParams, minLayoutParams,

		secLayoutParams;

private DownCountTimerListener mDownCountTimerListener;

private Handler mHandler;

private Runnable mRunnable;

private long totalTime = 0;

private boolean isRunning = true;

private int screenW;

private long outNumber=0;//超过的最大计数的时间(秒数)



public MyClockView(Context context, AttributeSet attrs) {

	super(context, attrs);

	initView(context, attrs);

}



public MyClockView(Context context) {

	this(context, null);

}



public MyClockView(Context context, AttributeSet attrs, int defStyleAttr) {

	super(context, attrs, defStyleAttr);

	initView(context, attrs);

}



public void initView(Context context, AttributeSet attrs) {

	screenW=getScreenWidth(context);

	TypedArray tArray = context.obtainStyledAttributes(attrs,

			R.styleable.MyClock);

	dayTextSize = tArray.getDimension(R.styleable.MyClock_dayTextSize, 26f);

	hourTextSize = tArray.getDimension(R.styleable.MyClock_hourTextSize,

			26f);

	minTextSize = tArray.getDimension(R.styleable.MyClock_minTextSize, 26f);

	secTextSize = tArray.getDimension(R.styleable.MyClock_secTextSize, 26f);

	int dayTextColor = tArray.getColor(R.styleable.MyClock_dayTextColor,

			0xffffff);

	int hourTextColor = tArray.getColor(R.styleable.MyClock_hourTextColor,

			0xffffff);

	int minTextColor = tArray.getColor(R.styleable.MyClock_minTextColor,

			0xffffff);

	int secTextColor = tArray.getColor(R.styleable.MyClock_secTextColor,

			0xffffff);

	Drawable dayTextBg = tArray

			.getDrawable(R.styleable.MyClock_dayTextBackground);

	Drawable hourTextBg = tArray

			.getDrawable(R.styleable.MyClock_hourTextBackground);

	Drawable minTextBg = tArray

			.getDrawable(R.styleable.MyClock_minTextBackground);

	Drawable secTextBg = tArray

			.getDrawable(R.styleable.MyClock_secTextBackground);



	tArray.recycle();



	dayTextView = new FlipClockView(context);

	hourTextView = new FlipClockView(context);

	minTextView = new FlipClockView(context);

	secTextView = new FlipClockView(context);

	

	dayTextView.setId(R.id.dayTextView);

	hourTextView.setId(R.id.hourTextView);

	minTextView.setId(R.id.minTextView);

	secTextView.setId(R.id.secTextView);

	

	dTextView = new TextView(context);

	hTextView = new TextView(context);

	mTextView = new TextView(context);

	sTextView = new TextView(context);

	dTextView.setText("DAYS");

	hTextView.setText("HOURS");

	mTextView.setText("MINUTES");

	sTextView.setText("SECONDS");

	dTextView.setTextColor(Color.parseColor("#ffffff"));

	hTextView.setTextColor(Color.parseColor("#ffffff"));

	mTextView.setTextColor(Color.parseColor("#ffffff"));

	sTextView.setTextColor(Color.parseColor("#ffffff"));

	dTextView.setTextSize(10f);

	hTextView.setTextSize(10f);

	mTextView.setTextSize(10f);

	sTextView.setTextSize(10f);



	dayTextView.setClockBackground(dayTextBg);

	dayTextView.setClockTextSize(dayTextSize);

	dayTextView.setClockTextColor(dayTextColor);

	hourTextView.setClockBackground(hourTextBg);

	hourTextView.setClockTextSize(dayTextSize);

	hourTextView.setClockTextColor(hourTextColor);

	minTextView.setClockBackground(minTextBg);

	minTextView.setClockTextSize(dayTextSize);

	minTextView.setClockTextColor(minTextColor);

	secTextView.setClockBackground(secTextBg);

	secTextView.setClockTextSize(dayTextSize);

	secTextView.setClockTextColor(secTextColor);

	//secTextView.setFlipDirection(false);



	//Log.e("---->","屏幕的宽"+screenW);

    int viewWidth=(int)(screenW*0.14);

	int viewMargin=(int)(screenW*0.05);

	dTextView.setWidth(viewWidth);

	dTextView.setGravity(Gravity.CENTER);

	hTextView.setWidth(viewWidth);

	hTextView.setGravity(Gravity.CENTER);

	mTextView.setWidth(viewWidth);

	mTextView.setGravity(Gravity.CENTER);

	sTextView.setWidth(viewWidth);

	sTextView.setGravity(Gravity.CENTER);



	dayLayoutParams = new LayoutParams(viewWidth,

            viewWidth);

	dayLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);

	dayLayoutParams.setMargins(0, 60, 0, 0);

	addView(dayTextView, dayLayoutParams);

	LayoutParams dLayoutParams = new LayoutParams(

			ViewGroup.LayoutParams.WRAP_CONTENT,

			ViewGroup.LayoutParams.WRAP_CONTENT);

	dLayoutParams.addRule(RelativeLayout.BELOW, R.id.dayTextView);

	dLayoutParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.dayTextView);

	dLayoutParams.setMargins(0, 5, 0, 60);

	addView(dTextView, dLayoutParams);



	hourLayoutParams = new LayoutParams(viewWidth,

            viewWidth);

	hourLayoutParams.addRule(RelativeLayout.RIGHT_OF, R.id.dayTextView);

	hourLayoutParams.setMargins(viewMargin, 60, viewMargin, 0);

	addView(hourTextView, hourLayoutParams);

	LayoutParams hLayoutParams = new LayoutParams(

			ViewGroup.LayoutParams.WRAP_CONTENT,

			ViewGroup.LayoutParams.WRAP_CONTENT);

	hLayoutParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.hourTextView);

	hLayoutParams.addRule(RelativeLayout.BELOW, R.id.hourTextView);

	hLayoutParams.setMargins(0, 5, 0, 0);

	addView(hTextView, hLayoutParams);



	minLayoutParams = new LayoutParams(viewWidth,

			viewWidth);

	minLayoutParams.setMargins(0, 60, 0, 0);

	minLayoutParams.addRule(RelativeLayout.RIGHT_OF, R.id.hourTextView);

	addView(minTextView, minLayoutParams);

	LayoutParams mLayoutParams = new LayoutParams(

			ViewGroup.LayoutParams.WRAP_CONTENT,

			ViewGroup.LayoutParams.WRAP_CONTENT);

	mLayoutParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.minTextView);

	mLayoutParams.addRule(RelativeLayout.BELOW, R.id.minTextView);

	mLayoutParams.setMargins(0, 5, 0, 0);

	addView(mTextView, mLayoutParams);



	secLayoutParams = new LayoutParams(viewWidth,

            viewWidth);

	secLayoutParams.addRule(RelativeLayout.RIGHT_OF, R.id.minTextView);

	secLayoutParams.setMargins(viewMargin, 60, 0, 0);

	addView(secTextView, secLayoutParams);

	LayoutParams sLayoutParams = new LayoutParams(

			ViewGroup.LayoutParams.WRAP_CONTENT,

			ViewGroup.LayoutParams.WRAP_CONTENT);

	sLayoutParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.secTextView);

	sLayoutParams.addRule(RelativeLayout.BELOW, R.id.secTextView);

	sLayoutParams.setMargins(0, 5, 0, 0);

	addView(sTextView, sLayoutParams);



	dayTextView.setClockTime("00");

	hourTextView.setClockTime("00");

	minTextView.setClockTime("00");

	secTextView.setClockTime("00");



	mHandler = new Handler();

}



public interface DownCountTimerListener {



	void stopDownCountTimer();



}



/**

 * 暂停计时

 */

public void pauseDownCountTimer() {

	if (mRunnable != null) {

		mHandler.removeCallbacks(mRunnable);

		dayTextView.setClockTime("00");

		hourTextView.setClockTime("00");

		minTextView.setClockTime("00");

		secTextView.setClockTime("00");

	//	Log.e("暂停计时", "-=-=-=-=-=");

	}

}



public void setDownCountTimerListener(DownCountTimerListener listener) {

	this.mDownCountTimerListener = listener;

}



/**

 * 获取设置倒计时的总时间

 * @return

 */

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

wnCountTimer();

}



/**

 * 暂停计时

 */

public void pauseDownCountTimer() {

	if (mRunnable != null) {

		mHandler.removeCallbacks(mRunnable);

		dayTextView.setClockTime("00");

		hourTextView.setClockTime("00");

		minTextView.setClockTime("00");

		secTextView.setClockTime("00");

	//	Log.e("暂停计时", "-=-=-=-=-=");

	}

}



public void setDownCountTimerListener(DownCountTimerListener listener) {

	this.mDownCountTimerListener = listener;

}



/**

 * 获取设置倒计时的总时间

 * @return

 */

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

[外链图片转存中…(img-JVAlSssx-1715812471365)]

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

[外链图片转存中…(img-Vkm9dlL4-1715812471365)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值