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

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

应用主要截图:

启动页:

注册登录:

  

日期设置与修改:

首页:

辅页:

    

关于:

主要代码:

启动页动画视图:

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'

布局内使用:

<yanzhikai.textpath.SyncTextPathView
            android:id="@+id/stpv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            app:duration="3000"
            app:paintStrokeColor="@color/colorYello"
            app:pathStrokeColor="@color/colorAccent"
            app:showPainter="true"
            app:text="我们"
            app:textInCenter="true"
            app:textSize="60sp" />

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<User>() {
            @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位表示日期
     * <p>
     * 以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<Day> query = new BmobQuery<>();
            query.addWhereEqualTo("author", BmobUser.getCurrentUser(User.class));
            query.order("-updatedAt");
            //包含作者信息
            query.include("author");
            query.findObjects(new FindListener<Day>() {

                @RequiresApi(api = Build.VERSION_CODES.O)
                @Override
                public void done(List<Day> 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

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读