纪念日+小游戏+工具小软件

闲暇时间写了个小软件,一是留作一个纪念,二是作为碎的知识点学习一下,后期还会加一些小功能进行维护升级,欢迎大家使用并提出宝贵意见。

应用主要截图:

启动页:

纪念日+小游戏+工具小软件_第1张图片

注册登录:

纪念日+小游戏+工具小软件_第2张图片  纪念日+小游戏+工具小软件_第3张图片

日期设置与修改:

纪念日+小游戏+工具小软件_第4张图片

首页:

纪念日+小游戏+工具小软件_第5张图片

辅页:

  纪念日+小游戏+工具小软件_第6张图片  纪念日+小游戏+工具小软件_第7张图片

关于:

纪念日+小游戏+工具小软件_第8张图片

主要代码:

启动页动画视图:

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

应用下载:

纪念日+小游戏+工具小软件_第9张图片

http://d.6short.com/dfyr

你可能感兴趣的:(Android)