Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,264 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 手势锁自定义视图
|
||||
*/
|
||||
public class GestureLockView extends View {
|
||||
private static final int MATRIX_SIZE = 3; // 3x3矩阵
|
||||
|
||||
private Paint mPaint; // 画笔
|
||||
private List<GesturePoint> mPoints; // 手势点集合
|
||||
private List<Integer> mSelectedPoints; // 已选择的点集合
|
||||
private List<GestureLine> mLines; // 连接线集合
|
||||
private GesturePoint mCurrentPoint; // 当前手指位置点
|
||||
private boolean mIsDrawing; // 是否正在绘制
|
||||
|
||||
private OnGestureCompleteListener mListener; // 手势完成监听器
|
||||
|
||||
public interface OnGestureCompleteListener {
|
||||
void onGestureComplete(List<Integer> selectedPoints);
|
||||
}
|
||||
|
||||
public static class GesturePoint {
|
||||
public float x; // X坐标
|
||||
public float y; // Y坐标
|
||||
public int index; // 点的索引 (0-8)
|
||||
public boolean isSelected; // 是否被选中
|
||||
|
||||
public GesturePoint(float x, float y, int index) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.index = index;
|
||||
this.isSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GestureLine {
|
||||
public GesturePoint startPoint; // 起始点
|
||||
public GesturePoint endPoint; // 结束点
|
||||
|
||||
public GestureLine(GesturePoint start, GesturePoint end) {
|
||||
this.startPoint = start;
|
||||
this.endPoint = end;
|
||||
}
|
||||
}
|
||||
|
||||
public GestureLockView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public GestureLockView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public GestureLockView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true); // 抗锯齿
|
||||
mPaint.setStrokeWidth(4); // 线宽
|
||||
|
||||
mPoints = new ArrayList<>();
|
||||
mSelectedPoints = new ArrayList<>();
|
||||
mLines = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
|
||||
// 计算点的位置
|
||||
int padding = Math.min(w, h) / 10; // 边距
|
||||
int cellWidth = (w - 2 * padding) / (MATRIX_SIZE - 1); // 每个格子的宽度
|
||||
int cellHeight = (h - 2 * padding) / (MATRIX_SIZE - 1); // 每个格子的高度
|
||||
|
||||
mPoints.clear();
|
||||
for (int i = 0; i < MATRIX_SIZE; i++) {
|
||||
for (int j = 0; j < MATRIX_SIZE; j++) {
|
||||
float x = padding + j * cellWidth;
|
||||
float y = padding + i * cellHeight;
|
||||
mPoints.add(new GesturePoint(x, y, i * MATRIX_SIZE + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
// 绘制所有点
|
||||
for (GesturePoint point : mPoints) {
|
||||
drawPoint(canvas, point);
|
||||
}
|
||||
|
||||
// 绘制连接线
|
||||
for (GestureLine line : mLines) {
|
||||
drawLine(canvas, line);
|
||||
}
|
||||
|
||||
// 如果正在绘制,绘制从最后一点到当前手指位置的连线
|
||||
if (mIsDrawing && mCurrentPoint != null && !mSelectedPoints.isEmpty()) {
|
||||
GesturePoint lastPoint = mPoints.get(mSelectedPoints.get(mSelectedPoints.size() - 1));
|
||||
mPaint.setColor(Color.parseColor("#FFA500")); // 橙色
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
canvas.drawLine(lastPoint.x, lastPoint.y, mCurrentPoint.x, mCurrentPoint.y, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPoint(Canvas canvas, GesturePoint point) {
|
||||
float radius = Math.min(getWidth(), getHeight()) / 15f; // 点的半径
|
||||
|
||||
if (point.isSelected) {
|
||||
// 绘制选中的点(大圆圈)
|
||||
mPaint.setColor(Color.parseColor("#FFA500")); // 橙色
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawCircle(point.x, point.y, radius, mPaint);
|
||||
|
||||
// 绘制内部小圆点
|
||||
mPaint.setColor(Color.WHITE);
|
||||
canvas.drawCircle(point.x, point.y, radius / 2, mPaint);
|
||||
} else {
|
||||
// 绘制未选中的点(圆环)
|
||||
mPaint.setColor(Color.GRAY);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(4);
|
||||
canvas.drawCircle(point.x, point.y, radius, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawLine(Canvas canvas, GestureLine line) {
|
||||
mPaint.setColor(Color.parseColor("#FFA500")); // 橙色
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(8);
|
||||
canvas.drawLine(line.startPoint.x, line.startPoint.y,
|
||||
line.endPoint.x, line.endPoint.y, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
handleTouchDown(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
handleTouchMove(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
handleTouchUp();
|
||||
break;
|
||||
}
|
||||
|
||||
invalidate(); // 重绘
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleTouchDown(float x, float y) {
|
||||
mSelectedPoints.clear();
|
||||
mLines.clear();
|
||||
mIsDrawing = true;
|
||||
|
||||
// 检查是否点击了某个点
|
||||
for (GesturePoint point : mPoints) {
|
||||
float distance = (float) Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
|
||||
if (distance < Math.min(getWidth(), getHeight()) / 10f) { // 如果在点的范围内
|
||||
selectPoint(point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTouchMove(float x, float y) {
|
||||
if (!mIsDrawing) return;
|
||||
|
||||
// 更新当前手指位置
|
||||
mCurrentPoint = new GesturePoint(x, y, -1);
|
||||
|
||||
// 检查是否有新的点被经过
|
||||
for (GesturePoint point : mPoints) {
|
||||
if (point.isSelected) continue; // 已经选过的点不再处理
|
||||
|
||||
float distance = (float) Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
|
||||
if (distance < Math.min(getWidth(), getHeight()) / 10f) { // 如果在点的范围内
|
||||
selectPoint(point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTouchUp() {
|
||||
if (!mIsDrawing) return;
|
||||
|
||||
mIsDrawing = false;
|
||||
mCurrentPoint = null;
|
||||
|
||||
// 通知手势完成
|
||||
if (mListener != null && !mSelectedPoints.isEmpty()) {
|
||||
mListener.onGestureComplete(mSelectedPoints);
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
reset();
|
||||
}
|
||||
|
||||
private void selectPoint(GesturePoint point) {
|
||||
if (!point.isSelected) {
|
||||
point.isSelected = true;
|
||||
mSelectedPoints.add(point.index);
|
||||
|
||||
// 添加连接线(如果不是第一个点)
|
||||
if (mSelectedPoints.size() > 1) {
|
||||
int lastIndex = mSelectedPoints.get(mSelectedPoints.size() - 2);
|
||||
GesturePoint lastPoint = mPoints.get(lastIndex);
|
||||
mLines.add(new GestureLine(lastPoint, point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
for (GesturePoint point : mPoints) {
|
||||
point.isSelected = false;
|
||||
}
|
||||
mSelectedPoints.clear();
|
||||
mLines.clear();
|
||||
mCurrentPoint = null;
|
||||
}
|
||||
|
||||
public void setOnGestureCompleteListener(OnGestureCompleteListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前手势并重置视图
|
||||
*/
|
||||
public void clearGesture() {
|
||||
reset();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前手势点的数量
|
||||
*/
|
||||
public int getSelectedPointsCount() {
|
||||
return mSelectedPoints.size();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,65 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.micode.notes.R;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
|
||||
private static final int SPLASH_DURATION = 3000; // 3秒
|
||||
private static final int TEXT_FADE_IN_DELAY = 2000; // 2秒后文字淡入
|
||||
|
||||
// 启动页创建,初始化动画界面和跳转逻辑
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_splash);
|
||||
|
||||
ImageView logo = findViewById(R.id.splash_logo);
|
||||
TextView text = findViewById(R.id.splash_text);
|
||||
|
||||
// 加载文字滑动动画
|
||||
Animation slideUpAnimation = AnimationUtils.loadAnimation(this, R.anim.text_slide_up);
|
||||
|
||||
// 2秒后显示文字动画
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
text.setVisibility(android.view.View.VISIBLE);
|
||||
text.startAnimation(slideUpAnimation);
|
||||
}
|
||||
}, TEXT_FADE_IN_DELAY);
|
||||
|
||||
// 3秒后跳转到主界面
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onSplashComplete();
|
||||
}
|
||||
}, SPLASH_DURATION);
|
||||
}
|
||||
|
||||
// 动画完成回调方法
|
||||
private void onSplashComplete() {
|
||||
Intent intent = new Intent(SplashActivity.this, LoginRegisterActivity.class);
|
||||
startActivity(intent);
|
||||
finish(); // 结束启动页,防止用户返回到此页面
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
}
|
||||
|
||||
// 处理屏幕旋转等配置变更
|
||||
@Override
|
||||
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
// 重新启动启动画面,以正确处理方向更改
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class WaveAnimation {
|
||||
|
||||
// 整行文字从左到右淡入显示动画
|
||||
public static void applyFadeInAnimation(TextView textView) {
|
||||
// 设置初始状态为完全透明
|
||||
textView.setAlpha(0f);
|
||||
textView.setVisibility(android.view.View.VISIBLE);
|
||||
|
||||
// 创建透明度渐变动画
|
||||
ValueAnimator fadeInAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
fadeInAnimator.setDuration(2000); // 2秒淡入效果
|
||||
fadeInAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
|
||||
fadeInAnimator.addUpdateListener(animation -> {
|
||||
float alpha = (float) animation.getAnimatedValue();
|
||||
textView.setAlpha(alpha);
|
||||
});
|
||||
|
||||
// 创建从左到右的滑入效果
|
||||
ValueAnimator slideInAnimator = ValueAnimator.ofFloat(-100f, 0f);
|
||||
slideInAnimator.setDuration(1500); // 1.5秒滑入
|
||||
slideInAnimator.setInterpolator(new android.view.animation.OvershootInterpolator());
|
||||
|
||||
slideInAnimator.addUpdateListener(animation -> {
|
||||
float translationX = (float) animation.getAnimatedValue();
|
||||
textView.setTranslationX(translationX);
|
||||
});
|
||||
|
||||
// 同时启动两个动画
|
||||
fadeInAnimator.start();
|
||||
slideInAnimator.start();
|
||||
}
|
||||
|
||||
// 从左到右逐字淡入效果(如果需要的话)
|
||||
public static void applyCharByCharFadeIn(TextView textView) {
|
||||
String text = textView.getText().toString();
|
||||
int length = text.length();
|
||||
|
||||
// 设置初始状态
|
||||
textView.setAlpha(0f);
|
||||
textView.setVisibility(android.view.View.VISIBLE);
|
||||
|
||||
// 为整行文字创建统一的淡入动画
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
|
||||
animator.setDuration(2500); // 总时长2.5秒
|
||||
animator.setInterpolator(new android.view.animation.DecelerateInterpolator());
|
||||
|
||||
animator.addUpdateListener(animation -> {
|
||||
float progress = (float) animation.getAnimatedValue();
|
||||
textView.setAlpha(progress);
|
||||
|
||||
// 添加轻微的缩放效果
|
||||
float scale = 0.8f + (progress * 0.2f); // 从0.8倍放大到1倍
|
||||
textView.setScaleX(scale);
|
||||
textView.setScaleY(scale);
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
// 兼容旧版本的方法名 - 使用新的淡入效果
|
||||
public static void applyComplexWaveAnimation(TextView textView) {
|
||||
applyFadeInAnimation(textView);
|
||||
}
|
||||
|
||||
// 兼容旧版本的另一个方法名
|
||||
public static void applyCharWaveAnimation(TextView textView) {
|
||||
applyCharByCharFadeIn(textView);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue