闲暇时间写了个小软件,一是留作一个纪念,二是作为碎的知识点学习一下,后期还会加一些小功能进行维护升级,欢迎大家使用并提出宝贵意见。
应用主要截图:
启动页:
注册登录:
日期设置与修改:
首页:
辅页:
关于:
主要代码:
启动页动画视图:
package com.cwj.love_lhh.view;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.Nullable;
public class BeatingHeartView extends View {
/**
* 跳动幅度
**/
private int beating = 70;
private int mMeasureWidth;
private int mWidthMode;
private int mMeasureHeight;
private int mHeightMode;
/**
* 偏移量
**/
private int offset, cfOffset;
/**
* 心形状
**/
private Paint heartPaint;
/**
* 心电图
**/
private Paint cgPaint, cgPointPaint;
private PathMeasure pathMeasure;
private volatile int pathLength;
public BeatingHeartView(Context context) {
super(context);
init();
}
public BeatingHeartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
heartPaint = new Paint();
heartPaint.setAntiAlias(true);
heartPaint.setStrokeWidth(6);
heartPaint.setColor(Color.RED);
cgPaint = new Paint();
cgPaint.setAntiAlias(true);
cgPaint.setColor(Color.GRAY);
cgPaint.setStrokeWidth(7);
cgPaint.setStyle(Paint.Style.STROKE);
cgPointPaint = new Paint();
cgPointPaint.setAntiAlias(true);
cgPointPaint.setStyle(Paint.Style.STROKE);
cgPointPaint.setStrokeWidth(7);
cgPointPaint.setColor(Color.WHITE);
cgPointPaint.setStrokeCap(Paint.Cap.ROUND);
cgPointPaint.setShadowLayer(10, 0, 0, Color.RED);
pathMeasure = new PathMeasure();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//进行测量
mWidthMode = MeasureSpec.getMode(widthMeasureSpec);
mHeightMode = MeasureSpec.getMode(heightMeasureSpec);
mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec);
mMeasureHeight = MeasureSpec.getSize(heightMeasureSpec);
if (mWidthMode == MeasureSpec.AT_MOST && mHeightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(200, 300);
} else if (mWidthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(200, mMeasureHeight);
} else if (mHeightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mMeasureWidth, 300);
} else {
setMeasuredDimension(mMeasureWidth, mMeasureHeight);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制背景
canvas.drawColor(Color.TRANSPARENT);
drawHeart(canvas);
drawCardiogram(canvas);
}
/**
* 画心脏
*
* @param canvas
*/
private void drawHeart(Canvas canvas) {
int width = getWidth();
int height = getHeight();
// 绘制心形
Path path = new Path();
path.moveTo(width / 2, height / 4 + offset / 4);
path.cubicTo((width * 6) / 7, height / 9,
(width * 12) / 13 - offset, (height * 2) / 5,
width / 2, (height * 7) / 14 - offset / 2);
canvas.drawPath(path, heartPaint);
Path path2 = new Path();
path2.moveTo(width / 2, height / 4 + offset / 4);
path2.cubicTo(width / 7, height / 9,
width / 13 + offset, (height * 2) / 5,
width / 2, (height * 7) / 14 - offset / 2);
canvas.drawPath(path2, heartPaint);
}
/**
* 画心电图
*
* @param canvas
*/
private void drawCardiogram(Canvas canvas) {
int baseWith = getWidth() / 10;
int moveHeight = (getHeight() / 4 + (getHeight() * 7) / 12) / 2;
// 心电图轮廓
Path path = new Path();
path.moveTo(0, moveHeight);
path.rLineTo((float) (baseWith * 2.5), 0);
path.rLineTo(baseWith, -baseWith);
path.rLineTo(baseWith, baseWith * 3);
path.rLineTo(baseWith, -baseWith * 6);
path.rLineTo(baseWith, baseWith * 5);
path.rLineTo(baseWith, -baseWith);
path.lineTo(getWidth(), moveHeight);
//背景线
// canvas.drawPath(path,cgPaint);
//分割路径
Path cutPath = new Path();
pathMeasure.setPath(path, false);
pathLength = (int) pathMeasure.getLength();
pathMeasure.getSegment(-pathLength + cfOffset, cfOffset, cutPath, true);
canvas.drawPath(cutPath, cgPointPaint);
}
/**
* 打开动画
*/
public void startAnimition() {
final ValueAnimator anima = ValueAnimator.ofInt(0, beating, 0);
anima.setInterpolator(new AccelerateDecelerateInterpolator());
anima.setDuration(1300);
anima.setRepeatCount(ValueAnimator.INFINITE);
anima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int whtie = (int) anima.getAnimatedValue();
offset = whtie;
postInvalidate();
}
});
anima.start();
final ValueAnimator cgAnima = ValueAnimator.ofInt(0, pathLength * 2);
cgAnima.setInterpolator(new AccelerateDecelerateInterpolator());
cgAnima.setDuration(1300);
cgAnima.setRepeatCount(ValueAnimator.INFINITE);
cgAnima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
cfOffset = (int) cgAnima.getAnimatedValue();
postInvalidate();
}
});
cgAnima.start();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("test", pathLength + "");
startAnimition();
}
}, 500);
}
}
文字动画库使用:
在app的build.gradle中引入:
//文本路径动画库
implementation 'com.yanzhikai:TextPathView:0.2.1'
布局内使用:
java代码属性设置:
stpv.startAnimation(0, 1);
//设置画笔特效
// stpv.setPathPainter(new PenPainter());//笔形
stpv.setPathPainter(new FireworksPainter());//火花
// stpv.setPathPainter(new ArrowPainter());//箭头
//设置动画播放完后填充颜色
stpv.setFillColor(true);
设置的日期存储与登录注册集成了Bmob后端云,集成方法很简便,详细集成方式看官方文档就行,讲的很详细。
官方文档地址:http://doc.bmob.cn/data/android/index.html
使用示例:
/**
* 账号密码注册
*/
private void signUp(final View view) {
final User user = new User();
user.setUsername("" + etUsername.getText().toString());
user.setPassword("" + etPassword.getText().toString());
user.signUp(new SaveListener() {
@Override
public void done(User user, BmobException e) {
if (e == null) {
ToastUtil.showTextToast(RegisterActivity.this, "注册成功");
loadingDialog.dismiss();
finish();
} else {
loadingDialog.dismiss();
ToastUtil.showTextToast(RegisterActivity.this, "该用户名已被注册,换个试试");
}
}
});
}
User类继承BmobUser就可以了:
BmobUser继承BmobObject,有默认属性:username、password、email、emailVerified、mobilePhoneNumber、mobilePhoneNumberVerified所以不需要自定义变量,如果你的用户需要其他属性,如性别、年龄、头像等,则需要继承BmobUser类进行自定义扩展。
package com.cwj.love_lhh.model;
import cn.bmob.v3.BmobUser;
public class User extends BmobUser {
}
公农历互转类:
package com.cwj.love_lhh.utils;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* 工具类,通过查表法实现公农历互转
*/
public class LunarUtils {
/**
* 支持转换的最小农历年份
*/
private static final int MIN_YEAR = 1900;
/**
* 支持转换的最大农历年份
*/
private static final int MAX_YEAR = 2099;
/**
* 公历每月前的天数
*/
private static final int DAYS_BEFORE_MONTH[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
/**
* 用来表示1900年到2099年间农历年份的相关信息,共24位bit的16进制表示,其中:
* 1. 前4位表示该年闰哪个月;为0表示不润月
* 2. 5-17位从左至右表示农历年份13个月的大小月分布,闰月紧接着月份,0表示小,1表示大;
* 3. 最后7位表示农历年首(正月初一)对应的公历日期。 高两位表示月份,低5位表示日期
*
* 以2014年的数据0x955ABF为例说明:
* 1001 0101 0101 1010 1011 1111
* 闰九月 农历正月初一对应公历1月31号
*/
private static final int LUNAR_INFO[] =
{
0x84B6BF,/*1900*/
0x04AE53, 0x0A5748, 0x5526BD, 0x0D2650, 0x0D9544, 0x46AAB9, 0x056A4D, 0x09AD42, 0x24AEB6, 0x04AE4A,/*1901-1910*/
0x6A4DBE, 0x0A4D52, 0x0D2546, 0x5D52BA, 0x0B544E, 0x0D6A43, 0x296D37, 0x095B4B, 0x749BC1, 0x049754,/*1911-1920*/
0x0A4B48, 0x5B25BC, 0x06A550, 0x06D445, 0x4ADAB8, 0x02B64D, 0x095742, 0x2497B7, 0x04974A, 0x664B3E,/*1921-1930*/
0x0D4A51, 0x0EA546, 0x56D4BA, 0x05AD4E, 0x02B644, 0x393738, 0x092E4B, 0x7C96BF, 0x0C9553, 0x0D4A48,/*1931-1940*/
0x6DA53B, 0x0B554F, 0x056A45, 0x4AADB9, 0x025D4D, 0x092D42, 0x2C95B6, 0x0A954A, 0x7B4ABD, 0x06CA51,/*1941-1950*/
0x0B5546, 0x555ABB, 0x04DA4E, 0x0A5B43, 0x352BB8, 0x052B4C, 0x8A953F, 0x0E9552, 0x06AA48, 0x6AD53C,/*1951-1960*/
0x0AB54F, 0x04B645, 0x4A5739, 0x0A574D, 0x052642, 0x3E9335, 0x0D9549, 0x75AABE, 0x056A51, 0x096D46,/*1961-1970*/
0x54AEBB, 0x04AD4F, 0x0A4D43, 0x4D26B7, 0x0D254B, 0x8D52BF, 0x0B5452, 0x0B6A47, 0x696D3C, 0x095B50,/*1971-1980*/
0x049B45, 0x4A4BB9, 0x0A4B4D, 0xAB25C2, 0x06A554, 0x06D449, 0x6ADA3D, 0x0AB651, 0x095746, 0x5497BB,/*1981-1990*/
0x04974F, 0x064B44, 0x36A537, 0x0EA54A, 0x86B2BF, 0x05AC53, 0x0AB647, 0x5936BC, 0x092E50, 0x0C9645,/*1991-2000*/
0x4D4AB8, 0x0D4A4C, 0x0DA541, 0x25AAB6, 0x056A49, 0x7AADBD, 0x025D52, 0x092D47, 0x5C95BA, 0x0A954E,/*2001-2010*/
0x0B4A43, 0x4B5537, 0x0AD54A, 0x955ABF, 0x04BA53, 0x0A5B48, 0x652BBC, 0x052B50, 0x0A9345, 0x474AB9,/*2011-2020*/
0x06AA4C, 0x0AD541, 0x24DAB6, 0x04B64A, 0x6a573D, 0x0A4E51, 0x0D2646, 0x5E933A, 0x0D534D, 0x05AA43,/*2021-2030*/
0x36B537, 0x096D4B, 0xB4AEBF, 0x04AD53, 0x0A4D48, 0x6D25BC, 0x0D254F, 0x0D5244, 0x5DAA38, 0x0B5A4C,/*2031-2040*/
0x056D41, 0x24ADB6, 0x049B4A, 0x7A4BBE, 0x0A4B51, 0x0AA546, 0x5B52BA, 0x06D24E, 0x0ADA42, 0x355B37,/*2041-2050*/
0x09374B, 0x8497C1, 0x049753, 0x064B48, 0x66A53C, 0x0EA54F, 0x06AA44, 0x4AB638, 0x0AAE4C, 0x092E42,/*2051-2060*/
0x3C9735, 0x0C9649, 0x7D4ABD, 0x0D4A51, 0x0DA545, 0x55AABA, 0x056A4E, 0x0A6D43, 0x452EB7, 0x052D4B,/*2061-2070*/
0x8A95BF, 0x0A9553, 0x0B4A47, 0x6B553B, 0x0AD54F, 0x055A45, 0x4A5D38, 0x0A5B4C, 0x052B42, 0x3A93B6,/*2071-2080*/
0x069349, 0x7729BD, 0x06AA51, 0x0AD546, 0x54DABA, 0x04B64E, 0x0A5743, 0x452738, 0x0D264A, 0x8E933E,/*2081-2090*/
0x0D5252, 0x0DAA47, 0x66B53B, 0x056D4F, 0x04AE45, 0x4A4EB9, 0x0A4D4C, 0x0D1541, 0x2D92B5 /*2091-2099*/
};
/**
* 将农历日期转换为公历日期
*
* @param year 农历年份
* @param month 农历月,若为闰月则传入负数
* @param monthDay 农历日
* @param //查询区间 1900年正月初一 至 2099年腊月三十
* @return 返回农历日期对应的公历日期,year0, month1, day2.若返回null表示运行错误,请检查输入数据是否合法。
*/
public static int[] lunarToSolar(int year, int month, int monthDay) {
int dayOffset;
int leapMonth;
int i;
int pos = Math.abs(month);
if (year < MIN_YEAR || year > MAX_YEAR || pos < 1 || pos > 12 || monthDay < 1 || monthDay > 30) {
return null;
}
leapMonth = leapMonth(year);
if (month < 0 && pos != leapMonth) {
return null;
}
//0x001F为 1 1111;按位与取低5位并减1,得到当年春节的日与1的天数
dayOffset = (LUNAR_INFO[year - MIN_YEAR] & 0x001F) - 1;
//0x60为 01100000,按位与取得春节所对应的月份+5个0,右移5位后取得春节所对应的月份
int basemonth = (int) ((LUNAR_INFO[year - MIN_YEAR] & 0x0060) >> 5);
//若春节对应的月份不为1月,则需将前面几个月的天数加上
for (int m = 1; m < basemonth; m++) {
dayOffset += daysInMonth(year, m);
}
//加上农历月份的天数差
if (leapMonth != 0) {
if (month < 0 || month > leapMonth) {
pos = pos + 1;
}
}
for (i = 1; i < pos; i++) {
dayOffset += daysInMyMonth(year, i);
}
//加上农历日的天数差
dayOffset += monthDay;
//阳历已跨年处理
int yeardays = daysInYear(year);
if (dayOffset > yeardays) {
year += 1;
dayOffset -= yeardays;
}
int[] solarInfo = new int[3];
for (i = 1; i < 13; i++) {
boolean isleap = isLeapYear(year);
int iPos = DAYS_BEFORE_MONTH[i];
if (isleap && i > 2) {
iPos += 1;
}
if (iPos >= dayOffset) {
solarInfo[1] = i;
iPos = DAYS_BEFORE_MONTH[i - 1];
if (isleap && i > 2) {
iPos += 1;
}
solarInfo[2] = dayOffset - iPos;
break;
}
}
solarInfo[0] = year;
return solarInfo;
}
/**
* 将公历日期转换为农历日期,且标识是否是闰月
*
* @param year
* @param month
* @param monthDay
* @param //查询区间 1900-01-31 至 2100-02-08
* @return 返回公历日期对应的农历日期,year0,month1,day2,若返回月份为负数,则表示该月为闰月
*/
public static int[] solarToLunar(int year, int month, int monthDay) {
int[] lunarDate = new int[3];
if (year < 1900 || year > 2100 || month < 1 || month > 12 || monthDay < 1 || monthDay > 31) {
return null;
} else if (year == 1900 && month == 1 && monthDay < 31) {
return null;
} else if (year == 2100) {
if (month > 2) {
return null;
} else if (month == 2 && monthDay > 8) {
return null;
}
}
Date baseDate = new GregorianCalendar(1900, 0, 31).getTime();
Date objDate = new GregorianCalendar(year, month - 1, monthDay).getTime();
//与1900年春节的天数差,86400000 = 1000 * 60 * 60 * 24
int offset = (int) ((objDate.getTime() - baseDate.getTime()) / 86400000L);
// 用offset减去每农历年的天数计算当天是农历第几天
// iYear最终结果是农历的年份, offset是当年的第几天
int iYear, daysOfYear = 0;
for (iYear = MIN_YEAR; iYear <= MAX_YEAR && offset > 0; iYear++) {
daysOfYear = daysInLunarYear(iYear);
offset -= daysOfYear;
}
if (offset < 0) {
offset += daysOfYear;
iYear--;
}
// 农历年份
lunarDate[0] = iYear;
int leapMonth = leapMonth(iYear); // 闰哪个月,1-12
boolean isLeap = false;
// 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
int iMonth, daysOfMonth = 0;
for (iMonth = 1; iMonth <= 13 && offset > 0; iMonth++) {
daysOfMonth = daysInMyMonth(iYear, iMonth);
offset -= daysOfMonth;
}
// 月份需要校正
if (offset < 0) {
offset += daysOfMonth;
--iMonth;
}
// 当前月超过闰月,要校正
if (leapMonth != 0 && iMonth > leapMonth) {
--iMonth;
if (iMonth == leapMonth) {
isLeap = true;
}
}
if (isLeap) {
lunarDate[1] = -1 * iMonth;
} else {
lunarDate[1] = iMonth;
}
lunarDate[2] = offset + 1;
return lunarDate;
}
/**
* 将农历日期转换为公历日期
*
* @param year 农历年份
* @param month 农历月,若为闰月则传入负数
* @param monthDay 农历日
* @param //查询区间 1900年正月初一 至 2099年腊月三十
* @return 返回农历日期对应的公历日期yyyy-MM-dd格式标准字符串.若返回空串,请检查输入数据是否合法。
*/
public static String getTranslateSolarString(int year, int month, int monthDay) {
String result = "";
int[] soloar = lunarToSolar(year, month, monthDay);
if (soloar != null) {
result = soloar[0] + "-";
if (soloar[1] < 10) {
result = result + "0" + soloar[1];
} else {
result = result + soloar[1];
}
if (soloar[2] < 10) {
result = result + "-0" + soloar[2];
} else {
result = result + "-" + soloar[2];
}
}
return result;
}
/**
* 将公历日期转换为农历日期字符串
*
* @param year
* @param month
* @param monthDay
* @param //查询区间 1900-01-31 至 2100-02-08
* @return 返回公历日期对应的农历日期字符串,若返回为空串,则表示参数错误
*/
public static String getTranslateLunarString(int year, int month, int monthDay) {
String result = "";
int[] lunar = solarToLunar(year, month, monthDay);
if (lunar != null) {
String tp = getLunarMonthName(lunar[1]);
result = lunar[0] + "年" + tp + "月";
tp = getLunarDayName(lunar[2]);
result = result + tp;
}
return result;
}
/**
* 根据阳历的年月日获取字符串
*
* @param year 年
* @param month 月
* @param monthDay 日
* @return
*/
public String getSolarString(int year, int month, int monthDay) {
String result = "";
if (year > 0) {
result = year + "-";
}
if (month < 10) {
result = result + "0" + month;
} else {
result = result + month;
}
if (monthDay < 10) {
result = result + "-0" + monthDay;
} else {
result = result + "-" + monthDay;
}
return result;
}
/**
* 根据农历的年月日获取字符串
*
* @param year 年
* @param month 月
* @param monthDay 日
* @return
*/
public String getLunarString(int year, int month, int monthDay) {
String result = "";
if (year > 0) {
result = year + "年";
}
String tp = getLunarMonthName(month);
result += tp + "月";
tp = getLunarDayName(monthDay);
result += tp;
return result;
}
/**
* 获取阳历日期是星期几
*/
public int getWeekByDateStr(int year, int month, int day) {
int x = -1;
try {
Calendar cal = Calendar.getInstance();
cal.set(year, month, day);
x = (cal.get(Calendar.DAY_OF_WEEK) + 6) % 7;
if (x == 0) {
x = 7;
}
} catch (Exception e) {
}
return x;
}
/**
* 判断阳历year年是否为闰年
*
* @param year 将要计算的年份
* @return 返回传入年份的总天数
*/
public static boolean isLeapYear(int year) {
if (year % 4 != 0) {
return false;
} else if (year % 100 == 0) {
if (year % 400 == 0) {
return true;
} else {
return false;
}
} else {
return true;
}
}
/**
* 传回阳历 year年的总天数
*
* @param year 将要计算的年份
* @return 返回传入年份的总天数
*/
public static int daysInYear(int year) {
return isLeapYear(year) ? 366 : 365;
}
/**
* 传回阳历 year年month月的总天数
*
* @param year 将要计算的年份 month 要传入的月份
* @return 返回传入年份的总天数
*/
public static int daysInMonth(int year, int month) {
if (month != 2) {
if (month <= 7) {
if (month % 2 == 1) {
return 31;
} else {
return 30;
}
} else {
if (month % 2 == 1) {
return 30;
} else {
return 31;
}
}
} else {
if (year > 0 && isLeapYear(year)) {
return 29;
} else {
return 28;
}
}
}
/**
* 传回农历 year年的总天数
*
* @param year 将要计算的年份
* @return 返回传入年份的总天数,若返回-1则表示运行错误,请检查传入参数
*/
public static int daysInLunarYear(int year) {
if (year < 1900 || year > 2099) {
return -1;
}
int i = 0;
int sum = 348;
//若不闫月,且所有月都是小月,农历一年348天;若闫且所有月都是小月,则为377天
if (leapMonth(year) != 0) {
sum = 377;
}
//0x0FFF80为1111 1111 1111 1000 0000,取当年各个月份的大小,每有一个大月加一天
int monthInfo = LUNAR_INFO[year - MIN_YEAR] & 0x0FFF80;
//0x80000为 1000 0000 0000 0000 0000,0x40为100 0000
for (i = 0x80000; i > 0x40; i >>= 1) {
if ((monthInfo & i) != 0)
sum += 1;
}
return sum;
}
/**
* 传回农历 year年闰哪个月 1-12 , 没闰传回 0
*
* @param year 将要计算的年份
* @return 传回农历 year年闰哪个月1-12, 没闰传回 0.若传回-1则表示传入参数错误
*/
public static int leapMonth(int year) {
if (year < 1900 || year > 2099) {
return -1;
}
return (int) ((LUNAR_INFO[year - MIN_YEAR] & 0xF00000)) >> 20;
}
/**
* 传回农历 year年month月的总天数,错误返回-1
* 若要查询闰月的天数,请将此月份设为负数
*/
public int daysInLunarMonth(int year, int month) {
int pos = Math.abs(month);
if (year < 1900 || year > 2099 || pos > 12 || pos == 0) {
return -1;
}
int leap = leapMonth(year);
if (month < 0 && pos != leap) {
return -1;
}
if (leap != 0) {
if (month < 0 || month > leap) {
pos = pos + 1;
}
}
if ((LUNAR_INFO[year - MIN_YEAR] & (0x100000 >> pos)) == 0)
return 29;
else
return 30;
}
/**
* 传回农历 year年的月份列表,闰月以负数表示
* 若要查询通用年份的月份列表,则将年份设为负数
*
* @return 以int数组的形式返回月份列表,若返回null表示参数错误
*/
public int[] getLunarMonths(int year) {
if ((year > 0 && year < 1900) || year > 2099) {
return null;
}
int[] months;
int leap = 0;
if (year > 0) {
leap = leapMonth(year);
}
if (leap == 0) {
months = new int[12];
for (int i = 0; i < 12; i++) {
months[i] = i + 1;
}
} else {
months = new int[13];
for (int i = 0; i < 13; i++) {
if (i + 1 <= leap) {
months[i] = i + 1;
} else if (i == leap) {
months[i] = -1 * leap;
} else {
months[i] = i;
}
}
}
return months;
}
/**
* 根据农历的数字月份获取农历月份的字符串形式
*
* @return 若返回null表示参数错误
*/
public static String getLunarMonthName(int month) {
String res = "";
String[] months = {"正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"};
int x = Math.abs(month);
if (month < 0) {
res = "闰";
}
res += months[x - 1];
return res;
}
/**
* 根据农历的数字月份获取农历月份的字符串形式
*
* @return 若返回null表示参数错误
*/
public static String getLunarDayName(int day) {
String res = "";
String[] days1 = {"初", "十", "廿", "三"};
String[] days2 = {"十", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
int x = (int) day / 10;
int y = day % 10;
if (x == 1 && y == 0) {
res = "初十";
} else {
res = days1[x] + days2[y];
}
return res;
}
/**
* 传回农历 year年pos月的总天数,总共有13个月包括闰月
* 月份为按13个月的位置
*/
private static int daysInMyMonth(int year, int pos) {
if ((LUNAR_INFO[year - MIN_YEAR] & (0x100000 >> pos)) == 0)
return 29;
else
return 30;
}
}
首页主要代码:
package com.cwj.love_lhh.fragment;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.text.LoginFilter;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.cwj.love_lhh.R;
import com.cwj.love_lhh.activity.AboutActivity;
import com.cwj.love_lhh.activity.HomeActivity;
import com.cwj.love_lhh.activity.LoginActivity;
import com.cwj.love_lhh.activity.SetTimeActivity;
import com.cwj.love_lhh.activity.SplashActivity;
import com.cwj.love_lhh.model.Day;
import com.cwj.love_lhh.model.User;
import com.cwj.love_lhh.utils.ChinaDate;
import com.cwj.love_lhh.utils.ChinaDate2;
import com.cwj.love_lhh.utils.LunarUtils;
import com.cwj.love_lhh.utils.NotificationUtils;
import com.cwj.love_lhh.utils.PictureSelectorUtils;
import com.cwj.love_lhh.utils.TimeUtils;
import com.cwj.love_lhh.utils.ToastUtil;
import com.jaeger.library.StatusBarUtil;
import java.io.File;
import java.text.ParseException;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import cn.bmob.v3.BmobQuery;
import cn.bmob.v3.BmobUser;
import cn.bmob.v3.datatype.BmobFile;
import cn.bmob.v3.exception.BmobException;
import cn.bmob.v3.listener.FindListener;
import cn.bmob.v3.listener.UploadFileListener;
//我们
public class UsFragment extends Fragment {
Unbinder unbinder;
@BindView(R.id.wv)
WebView wv;
@BindView(R.id.tv)
TextView tv;
@BindView(R.id.tv_time)
TextView tvTime;
@BindView(R.id.tv_in_harness_year)
TextView tvInHarnessYear;
@BindView(R.id.tv_get_married_year)
TextView tvGetMarriedYear;
@BindView(R.id.tv_change_date)
TextView tvChangeDate;
@BindView(R.id.tv_about)
TextView tvAbout;
@BindView(R.id.cl_view)
CoordinatorLayout clView;
@BindView(R.id.tv_jh)
TextView tvJh;
@BindView(R.id.tv_y)
TextView tvY;
@BindView(R.id.tv_set_backgground)
TextView tvSetBackgground;
@BindView(R.id.iv_bg)
ImageView ivBg;
@BindView(R.id.tv_reset)
TextView tvReset;
@BindView(R.id.tv_wedding_day)
TextView tvWeddingDay;
@BindView(R.id.tv_fall_in_love)
TextView tvFallInLove;
private String togetherTime, getMarriedTime, getMarriedTime2, getMarriedTime3, thisyeargetMarriedTime, nextyeargetMarriedTime, url;
SharedPreferences sprfMain;
private boolean isFrist = true;
private boolean isFrist2 = true;
@RequiresApi(api = Build.VERSION_CODES.O)
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_us, container, false);
unbinder = ButterKnife.bind(this, view);
queryPostAuthor();
initView();
return view;
}
/**
* 查询一对一关联,查询当前用户下的日期
*/
private void queryPostAuthor() {
if (BmobUser.isLogin()) {
BmobQuery query = new BmobQuery<>();
query.addWhereEqualTo("author", BmobUser.getCurrentUser(User.class));
query.order("-updatedAt");
//包含作者信息
query.include("author");
query.findObjects(new FindListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void done(List object, BmobException e) {
if (e == null) {
togetherTime = object.get(0).getTogetherTime();
getMarriedTime = object.get(0).getGetMarriedTime();
getMarriedTime2 = object.get(0).getGetMarriedTime2();
getMarriedTime3 = object.get(0).getGetMarriedTime3();
tvTime.setText(togetherTime + "我们在一起" + "\n\n" + getMarriedTime + "我们结婚");
update();//显示数据
//开始计时
handler.postDelayed(runnable, 1000);
//停止计时
//handler.removeCallbacks(runnable);
isFrist = true;
isFrist2 = true;
} else {
startActivity(new Intent(getActivity(), SetTimeActivity.class));
getActivity().finish();
}
}
});
} else {
ToastUtil.showTextToast(getActivity(), "请先登录");
startActivity(new Intent(getActivity(), LoginActivity.class));
getActivity().finish();
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void initView() {
StatusBarUtil.setTranslucentForImageView(getActivity(), 0, clView);//沉浸状态栏
// 设置WebView属性,能够执行Javascript脚本
wv.getSettings().setJavaScriptEnabled(true);
//语言设置防止加载乱码
wv.getSettings().setDefaultTextEncodingName("GBK");
// 即asserts文件夹下有一个color2.html
wv.loadUrl("file:///android_asset/index.html");
sprfMain = getActivity().getSharedPreferences("counter", Context.MODE_PRIVATE);
//设置背景
if (TextUtils.isEmpty(sprfMain.getString("path", ""))) {
wv.setVisibility(View.VISIBLE);
} else {
wv.setVisibility(View.GONE);
Glide.with(this).load(Uri.fromFile(new File(sprfMain.getString("path", "")))).into(ivBg);
}
}
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.O)
public void run() {
update();//获取新数据
handler.postDelayed(this, 1000); //n秒刷新一次
}
};
private ChinaDate lunar;
@RequiresApi(api = Build.VERSION_CODES.O)
private void update() {
long nowTime, startTime, apartTime, remainderHour, remainderMinute, remainderSecond, thisYearTogetherTimestamp,
nextyearTogetherTimestamp, thisYearGetMarriedTimestamp, nextyearGetMarriedTimestamp, getLunarTimestamp = 0, thisYearTimestamp;
int inHarnessYear, getMarriedYear, setTogetherTime, setGetMarriedTime;
String setTogetherDate, thisYearTogetherDate, nextyearTogetherDate, setGetMarriedDate, thisYearGetMarriedDate,
nextyearGetMarriedDate, getLunarnowTime = null, thisYearDate;
try {
nowTime = TimeUtils.getTimeStame();//当前时间戳
startTime = Long.parseLong(TimeUtils.dateToStamp2(togetherTime));//在一起的时间
apartTime = (nowTime - startTime) / 1000 / 60 / 60 / 24;//天数
remainderHour = (nowTime - startTime) / 1000 / 60 / 60 % 24;//小时
remainderMinute = (nowTime - startTime) / 1000 / 60 % 60;//分钟
remainderSecond = (nowTime - startTime) / 1000 % 60;//秒
tv.setText(apartTime + "天" + remainderHour + "小时" + remainderMinute + "分" + remainderSecond + "秒");
setTogetherTime = Integer.parseInt(togetherTime.substring(0, 4));//取出在一起年
setGetMarriedTime = Integer.parseInt(getMarriedTime.substring(3, 7));//取出结婚年
setTogetherDate = togetherTime.substring(4, 10);//取出在一起月日
thisYearDate = TimeUtils.dateToString(nowTime, "yyyy-MM-dd");//当前年月日
thisYearTogetherDate = TimeUtils.dateToString(nowTime, "yyyy") + setTogetherDate;//取出今年在一起的年月日
nextyearTogetherDate = (Integer.parseInt(TimeUtils.dateToString(nowTime, "yyyy")) + 1) + setTogetherDate;//取出下一年在一起的年月日
thisYearTimestamp = Long.parseLong(TimeUtils.dateToStamp2(thisYearDate));//当前年月日的时间戳
thisYearTogetherTimestamp = Long.parseLong(TimeUtils.dateToStamp2(thisYearTogetherDate));//今年在一起的年月日的时间戳
nextyearTogetherTimestamp = Long.parseLong(TimeUtils.dateToStamp2(nextyearTogetherDate));//下一年在一起的年月日的时间戳
if ((thisYearTogetherTimestamp - thisYearTimestamp) > 0) {
tvFallInLove.setText("" + (thisYearTogetherTimestamp - thisYearTimestamp) / 1000 / 60 / 60 / 24 + "天");//相恋纪念日
} else if ((thisYearTogetherTimestamp - thisYearTimestamp) == 0) {
tvFallInLove.setText("" + (thisYearTogetherTimestamp - thisYearTimestamp) / 1000 / 60 / 60 / 24 + "天");
if (isFrist) {
NotificationUtils.showNotification(getActivity(), null, "今天是你们的相恋日,问候ta一下吧!", 0, "", 0, 0);
isFrist = false;
}
} else {
tvFallInLove.setText("" + (nextyearTogetherTimestamp - thisYearTimestamp) / 1000 / 60 / 60 / 24 + "天");
}
try {
getLunarnowTime = TimeUtils.dateToString(nowTime, "yyyy-MM-dd");
getLunarTimestamp = Long.parseLong(TimeUtils.dateToStamp2(getLunarnowTime));//得到当前的时间戳
} catch (Exception e) {
e.printStackTrace();
}
if ("闰".equals(getMarriedTime3.substring(5, 6))) {//2020-闰04-01
thisYearGetMarriedDate = TimeUtils.dateToString(nowTime, "yyyy") + "-" + getMarriedTime3.substring(6, 11);//取出今年结婚的年月日
nextyearGetMarriedDate = (Integer.parseInt(TimeUtils.dateToString(nowTime, "yyyy")) + 1) + "-" + getMarriedTime3.substring(6, 11);//取出下一年结婚的年月日
int year = Integer.parseInt(thisYearGetMarriedDate.substring(0, 4));
int month = Integer.parseInt(thisYearGetMarriedDate.substring(5, 7));
int monthDay = Integer.parseInt(thisYearGetMarriedDate.substring(8, 10));
thisyeargetMarriedTime = LunarUtils.getTranslateSolarString(year, -month, monthDay);
int nextyear = Integer.parseInt(nextyearGetMarriedDate.substring(0, 4));
int nextmonth = Integer.parseInt(nextyearGetMarriedDate.substring(5, 7));
int nextmonthDay = Integer.parseInt(nextyearGetMarriedDate.substring(8, 10));
nextyeargetMarriedTime = LunarUtils.getTranslateSolarString(nextyear, nextmonth, nextmonthDay);
} else {//2020-04-01
thisYearGetMarriedDate = TimeUtils.dateToString(nowTime, "yyyy") + getMarriedTime3.substring(4, 10);//取出今年结婚的年月日
nextyearGetMarriedDate = (Integer.parseInt(TimeUtils.dateToString(nowTime, "yyyy")) + 1) + getMarriedTime3.substring(4, 10);//取出下一年结婚的年月日
int year = Integer.parseInt(thisYearGetMarriedDate.substring(0, 4));
int month = Integer.parseInt(thisYearGetMarriedDate.substring(5, 7));
int monthDay = Integer.parseInt(thisYearGetMarriedDate.substring(8, 10));
thisyeargetMarriedTime = LunarUtils.getTranslateSolarString(year, month, monthDay);
int nextyear = Integer.parseInt(nextyearGetMarriedDate.substring(0, 4));
int nextmonth = Integer.parseInt(nextyearGetMarriedDate.substring(5, 7));
int nextmonthDay = Integer.parseInt(nextyearGetMarriedDate.substring(8, 10));
nextyeargetMarriedTime = LunarUtils.getTranslateSolarString(nextyear, nextmonth, nextmonthDay);
}
thisYearGetMarriedTimestamp = Long.parseLong(TimeUtils.dateToStamp2(thisyeargetMarriedTime));//今年结婚的年月日的时间戳
nextyearGetMarriedTimestamp = Long.parseLong(TimeUtils.dateToStamp2(nextyeargetMarriedTime));//下一年结婚的年月日的时间戳
if ((thisYearGetMarriedTimestamp - getLunarTimestamp) > 0) {
tvWeddingDay.setText("" + (thisYearGetMarriedTimestamp - getLunarTimestamp) / 1000 / 60 / 60 / 24 + "天");//结婚纪念日
} else if ((thisYearGetMarriedTimestamp - getLunarTimestamp) == 0) {
tvWeddingDay.setText("" + (thisYearGetMarriedTimestamp - getLunarTimestamp) / 1000 / 60 / 60 / 24 + "天");
if (isFrist2) {
NotificationUtils.showNotification(getActivity(), null, "今天是你们的结婚纪念日,记得给ta一个惊喜哦!", 1, "", 0, 0);
isFrist2 = false;
}
} else {
tvWeddingDay.setText("" + (nextyearGetMarriedTimestamp - getLunarTimestamp) / 1000 / 60 / 60 / 24 + "天");
}
inHarnessYear = Integer.parseInt(TimeUtils.dateToString(nowTime, "yyyy")) - setTogetherTime;//在一起年数
getMarriedYear = Integer.parseInt(TimeUtils.dateToString(nowTime, "yyyy")) - setGetMarriedTime;//结婚年数
tvInHarnessYear.setText("" + inHarnessYear);
long getMarriedTimestamp = Long.parseLong(TimeUtils.dateToStamp2(getMarriedTime2));//阳历结婚时间毫秒数
if ((thisYearTimestamp - getMarriedTimestamp) >= 0) {
tvGetMarriedYear.setText("" + getMarriedYear);
tvJh.setVisibility(View.VISIBLE);
tvY.setVisibility(View.VISIBLE);
} else {
tvGetMarriedYear.setText("还有" + (getMarriedTimestamp - thisYearTimestamp) / 1000 / 60 / 60 / 24 + "天我们就结婚啦");
tvJh.setVisibility(View.GONE);
tvY.setVisibility(View.GONE);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
private void doCode() {
PictureSelectorUtils.ofImage(this, REQUEST_CODE_SELECT_USER_ICON);
}
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//执行代码,这里是已经申请权限成功了,可以不用做处理
doCode();
} else {
Toast.makeText(getActivity(), "权限申请失败", Toast.LENGTH_SHORT).show();
}
break;
}
}
SharedPreferences.Editor editorMain;
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {//判断是否返回成功
if (requestCode == REQUEST_SEARCH) {//判断来自哪个Activity
queryPostAuthor();//刷新数据
}
}
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_SELECT_USER_ICON) {
String userIconPath = PictureSelectorUtils.forResult(resultCode, data);
if (userIconPath == null) {
} else {
Glide.with(this).load(Uri.fromFile(new File(userIconPath))).into(ivBg);
wv.setVisibility(View.GONE);
ivBg.setVisibility(View.VISIBLE);
sprfMain = getActivity().getSharedPreferences("counter", Context.MODE_PRIVATE);
editorMain = sprfMain.edit();
editorMain.putString("path", userIconPath);
editorMain.commit();
}
}
}
}
public static final int REQUEST_SEARCH = 100;
private static final int REQUEST_CODE_SELECT_USER_ICON = 100;
@OnClick({R.id.tv_change_date, R.id.tv_about, R.id.tv_set_backgground, R.id.tv_reset})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.tv_change_date://日期修改
Intent intent = new Intent(getActivity(), SetTimeActivity.class);
startActivityForResult(intent, REQUEST_SEARCH);
break;
case R.id.tv_about://关于
startActivity(new Intent(getActivity(), AboutActivity.class));
break;
case R.id.tv_set_backgground://设置背景
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//没有权限则申请权限
this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
//有权限直接执行,docode()不用做处理
doCode();
}
} else {
//小于6.0,不用申请权限,直接执行
doCode();
}
break;
case R.id.tv_reset://重置背景
if (TextUtils.isEmpty(sprfMain.getString("path", ""))) {
ToastUtil.showTextToast(getActivity(), "已经是原始背景,请勿重试!");
} else {
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
.setTitle("提示")
.setMessage("确定重置当前背景吗?")
.setCancelable(true)
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
wv.setVisibility(View.VISIBLE);
ivBg.setVisibility(View.GONE);
editorMain = sprfMain.edit();
editorMain.putString("path", "");
editorMain.commit();
}
})
.create();
alertDialog.show();
//设置颜色和弹窗宽度一定要放在show之下,要不然会报错或者不生效
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(Color.BLACK);
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent));
}
break;
}
}
}
引用的主要三方库:
//butterknife
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
//沉浸状态栏
implementation 'com.jaeger.statusbarutil:library:1.5.1'
//文本路径动画库
implementation 'com.yanzhikai:TextPathView:0.2.1'
//时间选择器
implementation 'com.contrarywind:Android-PickerView:4.1.9'
//解决65535库
implementation 'androidx.multidex:multidex:2.0.0'
//圆角图标库
implementation 'de.hdodenhof:circleimageview:3.1.0'
//gson库
implementation 'com.google.code.gson:gson:2.7'
//图片选择框架
implementation 'com.github.goweii:PictureSelector:v2.3.1'
//图片加载框架
implementation 'com.github.bumptech.glide:glide:4.6.1'
//Bmob集成所需
implementation 'cn.bmob.android:bmob-sdk:3.7.7'
implementation "io.reactivex.rxjava2:rxjava:2.2.2"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.squareup.okio:okio:2.1.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
//防止键盘遮挡按钮的库
implementation 'com.github.yoyoyaobin:PreventKeyboardBlockUtil:1.0.8'
代码有点多其他不在贴出
项目地址:
https://github.com/cuiwenju2017/Love_LHH
应用下载:
http://d.6short.com/dfyr