紀念日+小遊戲+工具小軟件

閒暇時間寫了個小軟件,一是留作一個紀念,二是作爲碎的知識點學習一下,後期還會加一些小功能進行維護升級,歡迎大家使用並提出寶貴意見。

應用主要截圖:

啓動頁:

註冊登錄:

  

日期設置與修改:

首頁:

輔頁:

    

關於:

主要代碼:

啓動頁動畫視圖:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章