最近的專案中遇到了一個日曆,需要在日曆上顯示一些資訊,日曆有點選事件。
設計小姐姐給的效果圖是這樣的:
實際作出來的真機截圖,是這樣的:
實現這個效果還是比較容易的,我是用的是RecyclerView。
比較麻煩的就是日曆的計算,加上農曆。
佈局檔案程式碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroud_dark"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/headerview"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_show_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="日"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="一"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="二"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="三"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="四"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="五"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="六"
android:textColor="@color/text_color2"
android:textSize="@dimen/mini_text" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/date_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"></android.support.v7.widget.RecyclerView>
<LinearLayout
android:id="@+id/ll_toggle"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@color/white"
android:gravity="center">
<ImageView
android:layout_width="30dp"
android:layout_height="25dp"
android:src="@mipmap/liangdaogang" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="@color/divide"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/backgroud_dark"></android.support.v7.widget.RecyclerView>
</LinearLayout>
<LinearLayout
android:id="@+id/headerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/common_more_title" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/white"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/date_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text=""
android:textColor="@color/text_color2"
android:textSize="@dimen/large_text" />
<ImageView
android:id="@+id/before_bt"
android:layout_width="34dp"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/date_text"
android:paddingRight="25dp"
android:src="@mipmap/jt" />
<ImageView
android:id="@+id/next_bt"
android:layout_width="34dp"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/date_text"
android:paddingRight="25dp"
android:rotation="180"
android:src="@mipmap/jt" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>複製程式碼
item的佈局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="4dp"
android:gravity="center"
android:textColor="@color/text_color2"
android:textSize="@dimen/small_text"
android:textStyle="bold" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="4dp"
android:gravity="center"
android:textColor="@color/text_color4"
android:textSize="@dimen/mini_text" />
</LinearLayout>
<com.allrun.dangjianshisanshi.widget.PPBall
android:id="@+id/point"
android:layout_width="5dp"
android:layout_height="5dp"
android:layout_gravity="right|top"
android:layout_marginRight="10dp"
android:layout_marginTop="7dp"
android:visibility="gone"
app:color="@color/colorPrimary" />
</FrameLayout>複製程式碼
PPBall是一個圓形的自定義view,可以填充顏色,可以使用圖片替代,這裡就是顯示那個紅點。使用Grid佈局,一週七天。
date_list.layoutManager = GridLayoutManager(this, 7)複製程式碼
這個RecyclerView的資料來源是一個月份的天,也就是每一天都是一個資料,這個資料是通過Calendar給出的,傳到Adapter裡面的Calendar已經是選好了月份的。當然還有另一部分的資料是哪些天有提示的紅點,今天是幾號,選中了幾號,這些資料在modelHolder中。
直接上Adapter的程式碼,只有100行,稍縱即逝:
package com.greendami.adapter.schedule
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.allrun.dangjianshisanshi.R
import com.allrun.dangjianshisanshi.actvity.schedule.ScheduleActivity
import com.allrun.dangjianshisanshi.ardangjianshisanshi.entity.entity_base_obj_objlist
import com.allrun.dangjianshisanshi.ardangjianshisanshi.entity.entity_null_obj
import com.allrun.dangjianshisanshi.ardangjianshisanshi.entity.scheduleObj
import com.allrun.dangjianshisanshi.model.ModelHolder
import com.allrun.dangjianshisanshi.util.LunarCalendar
import com.allrun.dangjianshisanshi.widget.PPBall
import org.jetbrains.anko.backgroundDrawable
import java.text.SimpleDateFormat
import java.util.*
/**
* Created by greendami on 2017/8/24.
*/
class DateNavigateAdapter(var context: Context, var c: Calendar, var modelHolder: ModelHolder) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var emptyCount = 0
var today = Date()
var sdf = SimpleDateFormat("yyyyMMdd")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.date_navigate_item, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
initNormal(holder, position)
}
private fun initNormal(holder: RecyclerView.ViewHolder, position: Int) {
var myHolder: MyViewHolder = holder as MyViewHolder
if (position < emptyCount) {
myHolder.tv.visibility = View.GONE
} else {
myHolder.tv.text = "${position + 1 - emptyCount}"
c.set(Calendar.DATE, position + 1 - emptyCount)
myHolder.tv2.text = LunarCalendar().getLunarDate(c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DAY_OF_MONTH),false)
var dateString = sdf.format(c.time)
//週日週六的顏色淺
if(position%7 == 0 ||position%7 == 6){
myHolder.tv.setTextColor(context.resources.getColor(R.color.text_color4))
}
//顯示今天
if(sdf.format(today) == dateString){
myHolder.ll_tv.backgroundDrawable = context.resources.getDrawable(R.drawable.red_circle3)
myHolder.tv.setTextColor(context.resources.getColor(R.color.text_color2))
myHolder.tv2.setTextColor(context.resources.getColor(R.color.text_color4))
}
//選中
if(modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value?.obj?.s != null
&&modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value!!.obj.s == dateString){
myHolder.ll_tv.backgroundDrawable = context.resources.getDrawable(R.drawable.red_circle2)
myHolder.tv.setTextColor(context.resources.getColor(R.color.white))
myHolder.tv2.setTextColor(context.resources.getColor(R.color.white))
}
//顯示點點
myHolder.point.visibility = View.GONE
modelHolder.get<entity_base_obj_objlist<entity_null_obj, String>>().value?.objList?.forEach {
if(it.replace("-","") == dateString){
myHolder.point.visibility = View.VISIBLE
return@forEach
}
}
myHolder.ll_tv.setOnClickListener {
context as ScheduleActivity
c.set(Calendar.DATE, position + 1 - emptyCount)
modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value!!.obj = entity_null_obj(dateString)
modelHolder.callUpdate()
}
}
}
override fun getItemViewType(position: Int): Int {
return position
}
//獲得當月天數,並加上前面的空白天數
override fun getItemCount(): Int {
c.set(Calendar.DATE, 1)//把日期設定為當月第一天
emptyCount = c.get(Calendar.DAY_OF_WEEK) - 1//第一天是星期幾,前面要空出來
c.roll(Calendar.DATE, -1)//日期回滾一天,也就是最後一天
val maxDate = c.get(Calendar.DATE)
return maxDate + emptyCount
}
internal inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tv: TextView = itemView.findViewById(R.id.tv)
var tv2: TextView = itemView.findViewById(R.id.tv2)
var ll_tv: View = itemView.findViewById(R.id.ll_tv)
var point = itemView.findViewById<PPBall>(R.id.point)
}
}複製程式碼
其實有的月份的第一天可能不是星期日,所以開始可能有幾天是空白的,如下圖:
2017年9月1日就是星期五,前面就空出來了幾天,這幾天的數量需要算進來,然後在getItemCount()中加上。這裡程式碼上有註釋。然後在onBindViewHolder()的時候,把空白幾天的Item隱藏。
if (position < emptyCount) {
myHolder.tv.visibility = View.GONE
}複製程式碼
這裡使用View.GONE並不會讓後面的item把前面的Item的位置頂掉。因為整個資料集的數量我們已經給定了。這裡是數量決定的。
如果只是顯示日期:myHolder.tv.text = "${position + 1 - emptyCount}" 這樣就可以通過Position和前面的空白Item的數量直接算出。
下面是顯示農曆,具體農曆的算出,我是在網上找的程式碼:
package com.greendami.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
/**
* Created by greendami on 2017/10/13.
*/
public class LunarCalendar {
private int year; // 農曆的年份
private int month;
private int day;
private String lunarMonth; // 農曆的月份
private boolean leap;
public int leapMonth = 0; // 閏的是哪個月
final static String chineseNumber[] = { "正", "二", "三", "四", "五", "六", "七",
"八", "九", "十", "冬", "臘" };
static SimpleDateFormat chineseDateFormat = new SimpleDateFormat(
"yyyy年MM月dd日");
final static long[] lunarInfo = new long[] {
0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af, 0x9ad0, 0x55d2,
0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd255, 0xb54f, 0xd6a0, 0xada2, 0x95b0, 0x4977,
0x497f, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970,
0x6566, 0xd4a0, 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f,
0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2, 0xa950, 0xb557,
0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573, 0x52bf, 0xa9a8, 0xe950, 0x6aa0,
0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0,
0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6,
0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46, 0xab60, 0x9570,
0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58, 0x5ac0, 0xab60, 0x96d5, 0x92e0,
0xc960, 0xd954, 0xd4a0, 0xda50, 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5,
0xa950, 0xb4a0, 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930,
0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530,
0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0, 0xd0b6, 0xd25f, 0xd520, 0xdd45,
0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0,
0x4b63, 0x937f, 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef,
0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0, 0xa6d0, 0x55d4,
0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50, 0x55a0, 0xaba4, 0xa5b0, 0x52b0,
0xb273, 0x6930, 0x7337, 0x6aa0, 0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260,
0xe968, 0xd520, 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252,
0xd520};
// 農曆部分假日
final static String[] lunarHoliday = new String[] {
"0101 春節",
"0115 元宵",
"0505 端午",
"0707 七夕",
"0715 中元",
"0730 地藏",
// "0802 灶君誕",
"0815 中秋",
// "0827 先師誕",
"0909 重陽",
"1208 臘八",
"1223 小年",
"0100 除夕" };
// 公曆部分節假日
final static String[] solarHoliday = new String[] {
"0101 元旦",
// "0110 中國110宣傳日",
"0214 情人",
// "0221 國際母語日",
// "0303 國際愛耳日",
"0308 婦女",
"0312 植樹",
// "0315 消費者權益日",
// "0322 世界水日",
// "0323 世界氣象日",
"0401 愚人",
// "0407 世界衛生日",
"0501 勞動",
"0504 青年",
"0512 護士",
// "0519 全國助殘日",
// "0531 世界無煙日",
"0601 兒童",
// "0626 國際禁毒日",
"0701 建黨", //1921
"0801 建軍", //1933
//"0808 父親節",
// "0909 毛澤東逝世紀念", //1976
"0910 教師",
// "0917 國際和平日",
// "0927 世界旅遊日",
// "0928 孔子誕辰",
"1001 國慶",
// "1006 老人節",
// "1007 國際住房日",
// "1014 世界標準日",
// "1024 聯合國日",
// "1112 孫中山誕辰紀念",
// "1210 世界人權日",
// "1220 澳門迴歸紀念",
"1224 平安",
"1225 聖誕"
// "1226 毛澤東誕辰紀念"
};
//24節氣
final static String[] sTermInfo = new String[]{
// 時節 氣候
"小寒","大寒",
"立春","雨水",
"驚蟄","春分",
"清明","穀雨",
"立夏","小滿",
"芒種","夏至",
"小暑","大暑",
"立秋","處暑",
"白露","秋分",
"寒露","霜降",
"立冬","小雪",
"大雪","冬至"
};
final static String[] constellations = new String[]{
"摩蠍座:12.22—01.19","水瓶座:01.20—02.18","雙魚座:02.19—03.20","白羊座:03.21—04.19",
"金牛座:04.20—05.20","雙子座:05.21—06.20","巨蟹座:06.21—07.21","獅子座:07.22—08.22",
"處女座:08.23—09.22","天秤座:09.23—10.22","天蠍座:10.23—11.21","射手座:11.22—12.21"
};
final static String[] yi_string = new String[]{
"出行.上任.會友.上書.見工","除服.療病.出行.拆卸.入宅",
"祈福.祭祀.結親.開市.交易","祭祀.修填.塗泥.餘事勿取",
"交易.立券.會友.簽約.納畜","祈福.祭祀.求子.結婚.立約",
"求醫.赴考.祭祀.餘事勿取","經營.交易.求官.納畜.動土",
"祈福.入學.開市.求醫.成服","祭祀.求財.簽約.嫁娶.訂盟",
"療病.結婚.交易.入倉.求職","祭祀.交易.收財.安葬"
};
final static String[] ji_string = new String[]{
"動土.開倉.嫁娶.納采","求官.上任.開張.搬家.探病",
"服藥.求醫.栽種.動土.遷移","移徙.入宅.嫁娶.開市.安葬",
"種植.置業.賣田.掘井.造船","開市.交易.搬家.遠行",
"動土.出行.移徙.開市.修造","登高.行船.安床.入宅.博彩",
"詞訟.安門.移徙","開市.安床.安葬.入宅.破土",
"安葬.動土.鍼灸","宴會.安床.出行.嫁娶.移徙"
};
final static String[][] jcName = new String[][]{
{"建","除","滿","平","定","執","破","危","成","收","開","閉"},
{"閉","建","除","滿","平","定","執","破","危","成","收","開"},
{"開","閉","建","除","滿","平","定","執","破","危","成","收"},
{"收","開","閉","建","除","滿","平","定","執","破","危","成"},
{"成","收","開","閉","建","除","滿","平","定","執","破","危"},
{"危","成","收","開","閉","建","除","滿","平","定","執","破"},
{"破","危","成","收","開","閉","建","除","滿","平","定","執"},
{"執","破","危","成","收","開","閉","建","除","滿","平","定"},
{"定","執","破","危","成","收","開","閉","建","除","滿","平"},
{"平","定","執","破","危","成","收","開","閉","建","除","滿"},
{"滿","平","定","執","破","危","成","收","開","閉","建","除"},
{"除","滿","平","定","執","破","危","成","收","開","閉","建"}
};
final static String[] Gan = new String[] { "甲", "乙", "丙", "丁", "戊", "己", "庚",
"辛", "壬", "癸" };
final static String[] Zhi = new String[] { "子", "醜", "寅", "卯", "辰", "巳", "午",
"未", "申", "酉", "戌", "亥" };
// ====== 傳回農曆 y年的總天數 1900--2100
public int yearDays(int y) {
int i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1) {
if ((lunarInfo[y - 1900] & i) != 0)
sum += 1;
}
return (sum + leapDays(y));
}
// ====== 傳回農曆 y年閏月的天數
public int leapDays(int y) {
if (leapMonth(y) != 0) {
if ((lunarInfo[y - 1899] & 0xf) != 0)
return 30;
else
return 29;
} else
return 0;
}
// ====== 傳回農曆 y年閏哪個月 1-12 , 沒閏傳回 0
public int leapMonth(int y) {
long var = lunarInfo[y - 1900] & 0xf;
return (int)(var == 0xf ? 0 : var);
}
// ====== 傳回農曆 y年m月的總天數
public int monthDays(int y, int m) {
if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
return 29;
else
return 30;
}
// ====== 傳回農曆 y年的生肖
public String animalsYear(int year) {
final String[] Animals = new String[] { "鼠", "牛", "虎", "兔", "龍", "蛇",
"馬", "羊", "猴", "雞", "狗", "豬" };
return Animals[(year - 4) % 12]+"年";
}
// ====== 傳入 月日的offset 傳回干支, 0=甲子
final private static String cyclicalm(int num) {
return (Gan[num % 10] + Zhi[num % 12]);
}
// ====== 傳入 offset 傳回干支, 0=甲子
final public String cyclical(int year, int month,int day) {
int num = year - 1900 + 36;
//立春日期
int term2 = sTerm(year, 2);
if(month>2 || (month==2 && day>= term2)){
num = num + 0;
}else{
num = num -1;
}
return (cyclicalm(num));
}
public static String getChinaDayString(int day) {//將農曆day日格式化成農曆表示的字串
String chineseTen[] = { "初", "十", "廿", "卅" };
String chineseDay[] = { "一", "二", "三", "四", "五", "六", "七",
"八", "九", "十"};
String var="";
if(day!=20 && day != 30){
var = chineseTen[(int)((day-1)/10)]+ chineseDay[(int)((day-1)%10)];
}else if(day !=20){
var = chineseTen[(int)(day/10)]+"十";
}else{
var = "二十";
}
return var;
}
/*
* 計算公曆nY年nM月nD日和bY年bM月bD日漸相差多少天
* */
public int getDaysOfTwoDate(int bY,int bM, int bD,int nY, int nM,int nD){
Date baseDate = null;
Date nowaday = null;
try {
baseDate = chineseDateFormat.parse(bY+"年"+bM+"月"+bD+"日");
nowaday = chineseDateFormat.parse(nY+"年"+nM+"月"+nD+"日");
} catch (ParseException e) {
e.printStackTrace();
}
// 求出相差的天數
int offset = (int) ((nowaday.getTime() - baseDate.getTime()) / 86400000L);
return offset;
}
/*農曆lunYear年lunMonth月lunDay日
* isLeap 當前年月是否是閏月
* 從農曆日期轉換成公曆日期
* */
public Calendar getSunDate(int lunYear, int lunMonth, int lunDay, boolean isLeap){
//公曆1900年1月31日為1900年正月初一
int years = lunYear -1900;
int days=0;
for(int i=0;i<years;i++){
days += yearDays(1900+i);//農曆某年總天數
}
for(int i=1;i<lunMonth;i++){
days +=monthDays(lunYear,i);
}
if(leapMonth(lunYear)!=0 && lunMonth>leapMonth(lunYear)){
days += leapDays(lunYear);//lunYear年閏月天數
}
if(isLeap){
days +=monthDays(lunYear,lunMonth);//lunYear年lunMonth月 閏月
}
days +=lunDay;
days = days-1;
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 1900);
cal.set(Calendar.MONTH, 0);
cal.set(Calendar.DAY_OF_MONTH,31);
cal.add(Calendar.DATE, days);
/*
Date date=cal.getTime();
int year_c = cal.get(Calendar.YEAR);
int month_c = cal.get(Calendar.MONTH)+1;
int day_c = cal.get(Calendar.DAY_OF_MONTH);
*/
return cal;
}
public String getLunarString(int year, int month, int day){
int var = getLunarDateINT(year,month,day);
int lYear = (int)var/10000;
int lMonth = (int)(var%10000)/100;
int lDay = var - lYear*10000 - lMonth*100;
String lunY = cyclical(year,month,day)+"年";
String lunM="";
int testMonth = getSunDate(lYear,lMonth,lDay,false).get(Calendar.MONTH)+1;
if(testMonth!=month){
lunM = "閏";
}
lunM += chineseNumber[(lMonth-1)%12]+"月";
//int leap = leapMonth(lYear);
String lunD = getChinaDayString(lDay);
int animalsYear = 0;
int term2 = sTerm(year, 2);//立春
if(month>2 ||(month==2&& day>=term2)){
animalsYear = year;
}else{
animalsYear = year-1;
}
String Festival = getFestival(year,month,day);
//return lunY+"-"+lunM+"-"+lunD+"-"+animalsYear(animalsYear);
return "農曆 "+lunM+lunD+" "+Festival;
}
/*
* 將公曆year年month月day日轉換成農曆
* 返回格式為20140506(int)
* */
public int getLunarDateINT(int year, int month, int day){
int iYear,LYear,LMonth,LDay, daysOfYear = 0;
// 求出和1900年1月31日相差的天數
//year =1908;
//month = 3;
//day =3;
int offset = getDaysOfTwoDate(1900,1,31,year,month,day);
//Log.i("--ss--","公曆:"+year+"-"+month+"-"+day+":"+offset);
// 用offset減去每農曆年的天數
// 計算當天是農曆第幾天
// i最終結果是農曆的年份
// offset是當年的第幾天
for (iYear = 1900; iYear < 2100 && offset >0; iYear++) {
daysOfYear = yearDays(iYear);
offset -= daysOfYear;
//Log.i("--ss--","農曆:"+iYear+":"+daysOfYear+"/"+offset);
}
if (offset < 0) {
offset += daysOfYear;
iYear--;
//Log.i("--ss--","農曆:"+iYear+":"+daysOfYear+"/"+offset);
}
// 農曆年份
LYear = iYear;
leapMonth = leapMonth(iYear); // 閏哪個月,1-12
leap = false;
// 用當年的天數offset,逐個減去每月(農曆)的天數,求出當天是本月的第幾天
int iMonth=1, daysOfMonth = 0;
for (iMonth = 1; iMonth < 13 && offset >0; iMonth++) {
// 閏月
if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
--iMonth;
leap = true;
daysOfMonth = leapDays(iYear);
} else{
daysOfMonth = monthDays(iYear, iMonth);
}
// 解除閏月
if (leap && iMonth == (leapMonth + 1)) leap = false;
offset -= daysOfMonth;
//Log.i("--ss--","農曆:"+iYear+"-"+iMonth+":"+daysOfMonth+"/"+offset);
}
// offset為0時,並且剛才計算的月份是閏月,要校正
if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
if (leap) {
leap = false;
} else {
leap = true;
--iMonth;
}
}
// offset小於0時,也要校正
if (offset < 0) {
offset += daysOfMonth;
--iMonth;
//Log.i("--ss--","農曆:"+iYear+"-"+iMonth+":"+daysOfMonth+"/"+offset);
}
LMonth = iMonth;
LDay = offset + 1;
//Log.i("--ss--","農曆:"+LYear+"-"+LMonth+"-"+LDay);
return LYear * 10000 + LMonth *100 + LDay;
}
public String getFestival(int year, int month, int day){//獲取公曆對應的節假日或二十四節氣
//農曆節假日
int var = getLunarDateINT(year, month, day);
int lun_year = (int)var/10000;
int lun_month = (int)(var%10000)/100;
int lun_Day = (int)(var - lun_year*10000 - lun_month*100);
for (int i = 0; i < lunarHoliday.length; i++) {
// 返回農曆節假日名稱
String ld = lunarHoliday[i].split(" ")[0]; // 節假日的日期
String ldv = lunarHoliday[i].split(" ")[1]; // 節假日的名稱
String lmonth_v = lun_month + "";
String lday_v = lun_Day + "";
String lmd = "";
if (month < 10) {
lmonth_v = "0" + lun_month;
}
if (day < 10) {
lday_v = "0" + lun_Day;
}
lmd = lmonth_v + lday_v;
if (ld.trim().equals(lmd.trim())) {
return ldv;
}
}
//公曆節假日
for (int i = 0; i < solarHoliday.length; i++) {
if(i==solarHoliday.length && year<1893 //1893年前沒有此節日
|| i+3 == solarHoliday.length && year<1999
|| i+6 == solarHoliday.length && year<1942
|| i+10 == solarHoliday.length && year<1949
|| i == 19 && year<1921
|| i == 20 && year<1933
|| i == 22 && year<1976){
break;
}
// 返回公曆節假日名稱
String sd = solarHoliday[i].split(" ")[0]; // 節假日的日期
String sdv = solarHoliday[i].split(" ")[1]; // 節假日的名稱
String smonth_v = month + "";
String sday_v = day + "";
String smd = "";
if (month < 10) {
smonth_v = "0" + month;
}
if (day < 10) {
sday_v = "0" + day;
}
smd = smonth_v + sday_v;
if (sd.trim().equals(smd.trim())) {
return sdv;
}
}
int b = getDateOfSolarTerms(year,month);
if(day==(int)b/100){
return sTermInfo[(month-1)*2];
}else if(day== b%100){
return sTermInfo[(month-1)*2+1];
}
return "";
}
/** */
/**
* 傳出y年m月d日對應的農曆. yearCyl3:農曆年與1864的相差數 ? monCyl4:從1900年1月31日以來,閏月數
* dayCyl5:與1900年1月31日相差的天數,再加40 ?
*
* isday: 這個引數為false---日期為節假日時,陰曆日期就返回節假日 ,true---不管日期是否為節假日依然返回這天對應的陰曆日期
*
* @return
*/
public String getLunarDate(int year_log, int month_log, int day_log,
boolean isday) {
int var = getLunarDateINT(year_log,month_log,day_log);
// 農曆年份
year = (int)var/10000;
setYear(year); // 設定公曆對應的農曆年份
//農曆月份
month = (int)Math.ceil((var%10000)/100);
setLunarMonth(chineseNumber[(month-1)%12] + "月"); // 設定對應的陰曆月份
day = var - year*10000 - month*100;
if (!isday) {
String Festival = getFestival(year_log,month_log,day_log);
if(Festival.length()>1){
return Festival;
}
}
if (day == 1)
return chineseNumber[(month-1)%12] + "月";
else
return getChinaDayString(day);
}
public String toString() {
if (chineseNumber[(month-1)%12] == "正" && getChinaDayString(day) == "初一")
return "農曆" + year + "年";
else if (getChinaDayString(day) == "初一")
return chineseNumber[(month-1)%12] + "月";
else
return getChinaDayString(day);
// return year + "年" + (leap ? "閏" : "") + chineseNumber[month - 1] +
// "月" + getChinaDayString(day);
}
/*
* public static void main(String[] args) { System.out.println(new
* LunarCalendar().getLunarDate(2012, 1, 23)); }
*/
public int getLeapMonth() {
return leapMonth;
}
public void setLeapMonth(int leapMonth) {
this.leapMonth = leapMonth;
}
/**
* 得到當前日期對應的陰曆月份
*
* @return
*/
public String getLunarMonth() {
return lunarMonth;
}
public void setLunarMonth(String lunarMonth) {
this.lunarMonth = lunarMonth;
}
/**
* 得到當前年對應的農曆年份
*
* @return
*/
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getConstellation(int month, int day){//計算星座
int date = month*100 + day;
String constellation="";
if(date>=120 && date <=218){
constellation = constellations[1];
}else if(date>=219 && date<=320){
constellation = constellations[2];
}else if(date>=321 && date<=419){
constellation = constellations[3];
}else if(date>=420 && date<=520){
constellation = constellations[4];
}else if(date>=521 && date<=620){
constellation = constellations[5];
}else if(date>=621 && date<=721){
constellation = constellations[6];
}else if(date>=722 && date<=822){
constellation = constellations[7];
}else if(date>=823 && date<=922){
constellation = constellations[8];
}else if(date>=923 && date<=1022){
constellation = constellations[9];
}else if(date>=1023 && date<=1121){
constellation = constellations[10];
}else if(date>=1122 && date<=1221){
constellation = constellations[11];
}else{
constellation = constellations[0];
}
return constellation;
}
private int sTerm(int y, int n) {
int[] sTermInfo = new int[]{0,21208,42467,63836,85337,107014,
128867,150921,173149,195551,218072,240693,
263343,285989,308563,331033,353350,375494,
397447,419210,440795,462224,483532,504758};
Calendar cal = Calendar.getInstance();
cal.set(1900, 0, 6, 2, 5, 0);
long temp = cal.getTime().getTime();
cal.setTime(new Date( (long) ((31556925974.7 * (y - 1900) + sTermInfo[n] * 60000L) + temp)));
int a=cal.get(Calendar.DAY_OF_MONTH);
return a;
}
public int getDateOfSolarTerms(int year, int month){
int a = sTerm(year,(month-1)*2);
int b = sTerm(year,(month-1)*2 +1);
return a*100+b;
//return 0;
}
public String jcrt(String d) {
String jcrjxt="";
String yj0 = "宜:\t",yj1 = "忌:\t",br = "-";
//String yj0 = "",yj1 = "",br = "-";
if (d == "建") jcrjxt = yj0 + yi_string[0] + br + yj1 + ji_string[0];
if (d == "除") jcrjxt = yj0 + yi_string[1] + br + yj1 + ji_string[1];
if (d == "滿") jcrjxt = yj0 + yi_string[2] + br + yj1 + ji_string[2];
if (d == "平") jcrjxt = yj0 + yi_string[3] + br + yj1 + ji_string[3];
if (d == "定") jcrjxt = yj0 + yi_string[4] + br + yj1 + ji_string[4];
if (d == "執") jcrjxt = yj0 + yi_string[5] + br + yj1 + ji_string[5];
if (d == "破") jcrjxt = yj0 + yi_string[6] + br + yj1 + ji_string[6];
if (d == "危") jcrjxt = yj0 + yi_string[7] + br + yj1 + ji_string[7];
if (d == "成") jcrjxt = yj0 + yi_string[8] + br + yj1 + ji_string[8];
if (d == "收") jcrjxt = yj0 + yi_string[9] + br + yj1 + ji_string[9];
if (d == "開") jcrjxt = yj0 + yi_string[10] + br + yj1 + ji_string[10];
if (d == "閉") jcrjxt = yj0 + yi_string[11] + br + yj1 + ji_string[11];
return jcrjxt;
}
/* num_y%12, num_m%12, num_d%12, num_y%10, num_d%10
* m:農曆月份 1---12
* dt:農曆日
* */
public String CalConv2(int yy, int mm, int dd, int y, int d, int m, int dt) {
String dy = d + "" + dd;
if ((yy == 0 && dd == 6) || (yy == 6 && dd == 0) || (yy == 1 && dd == 7) ||
(yy == 7 && dd == 1) || (yy == 2 && dd == 8) || (yy == 8 && dd == 2) ||
(yy == 3 && dd == 9) || (yy == 9 && dd == 3) || (yy == 4 && dd == 10) ||
(yy == 10 && dd == 4) || (yy == 5 && dd == 11) || (yy == 11 && dd == 5)) {
/*
* 地支共有六對相沖,俗稱“六沖”,即:子午相沖、醜未相沖、寅申相沖、卯酉相沖、辰戌相沖、巳亥相沖。
* 如當年是甲子年,子為太歲,與子相沖者是午,假如當日是午日,則為歲破,其餘的以此類推,即丑年的歲破為未日,
* 寅年的歲破為申日,卯年的歲破為酉日,辰年的歲破為戌日,巳年的歲破為亥日,午年的歲破為子日,未年的歲破為丑日,
* 申年的歲破為寅日,酉年的歲破為卯日,戌年的歲破為辰日,亥年的歲破為巳日。
* */
return "日值歲破 大事不宜";
}
else if ((mm == 0 && dd == 6) || (mm == 6 && dd == 0) || (mm == 1 && dd == 7) || (mm == 7 && dd == 1) ||
(mm == 2 && dd == 8) || (mm == 8 && dd == 2) || (mm == 3 && dd == 9) || (mm == 9 && dd == 3) ||
(mm == 4 && dd == 10) || (mm == 10 && dd == 4) || (mm == 5 && dd == 11) || (mm == 11 && dd == 5)) {
return "日值月破 大事不宜";
}
else if ((y == 0 && dy == "911") || (y == 1 && dy == "55") || (y == 2 && dy == "111") ||
(y == 3 && dy == "75") || (y == 4 && dy == "311") || (y == 5 && dy == "95") ||
(y == 6 && dy == "511")|| (y == 7 && dy == "15") || (y == 8 && dy == "711") || (y == 9 && dy == "35")) {
return "日值上朔 大事不宜";
}
else if ((m == 1 && dt == 13) || (m == 2 && dt == 11) || (m == 3 && dt == 9) || (m == 4 && dt == 7) ||
(m == 5 && dt == 5) || (m == 6 && dt == 3) || (m == 7 && dt == 1) || (m == 7 && dt == 29) ||
(m == 8 && dt == 27) || (m == 9 && dt == 25) || (m == 10 && dt == 23) || (m == 11 && dt == 21) ||
(m == 12 && dt == 19)) {
/*
* 楊公十三忌 以農曆正月十三日始,以後每月提前兩天為百事禁忌日
* */
return "日值楊公十三忌 大事不宜";
//}else if(var == getSiLi(m,dt)){
//return "日值四離 大事勿用";
}
else {
return null;
}
}
public int getDaysOfMonth(int year, int month){//返回公曆year年month月的當月總天數
if(month == 2){
return(((year%4 == 0) && (year%100 != 0) || (year%400 == 0))? 29: 28);
}else if(month == 1 || month == 3 ||month == 5 || month == 7 || month == 8 ||month == 10 || month == 12){
return 31;
}
return 30;
}
/*輸入
* year 公曆年份,大於1900
* month 公曆月份 1--12
* 輸出
* ArrayList<String> year年month月全月宜忌
* */
public ArrayList<String> getyiji(int year, int month) {
int num_y = -1;
int num_m = (year - 1900) * 12 + month-1 +12;
int num_d;
int mLMonth=1,mLDay=1,mLun_x=0;
int days_of_month = getDaysOfMonth(year, month);
ArrayList<String> yiji = new ArrayList<String>();
//年柱 1900年立春後為庚子年(60進位制36)
//cyclical(year,month);
//立春日期
int term2 = sTerm(year, 2);
//月柱 1900年1月小寒以前為 丙子月(60進位制12)
int firstNode = sTerm(year, (month-1)*2);//當月的24節氣中的節開始日
//cyclicalm(num_m);
//Calendar cal = Calendar.getInstance();
//cal.set(year, month, 1, 0, 0, 0);
//1900/1/1與 1970/1/1 相差25567日, 1900/1/1 日柱為甲戌日(60進位制10)
int dayCyclical = (int) (getDaysOfTwoDate(1900,1,1,year,month,1)+10);
for(int i=0;i<days_of_month;i++){
if(mLDay > mLun_x){
//Log.i("","mLDay > mLun_x "+mLDay+":"+mLun_x);
int var = getLunarDateINT(year,month,i+1);
int mLYear =(int)var/10000;
mLMonth = (int)(var%10000)/100;
mLDay = (int)(var - mLYear*10000 - mLMonth*100);
mLun_x = (leapMonth(mLYear)!=0)?leapDays(mLYear):monthDays(mLYear, mLMonth);
//Log.i("","mLDay > mLun_x ?"+mLDay+":"+mLun_x);
}
//依節氣調整二月分的年柱, 以立春為界
if(month==2 && (i+1)==term2){
//cY = cyclicalm(year-1900 + 36);
num_y = year - 1900 + 36;
}
//依節氣 調整月柱, 以「節」為界
if((i+1)==firstNode) {
num_m =(year - 1900) * 12 + month +12 ;
//cM = cyclicalm(num_m);
}
//日柱
//cD = cyclicalm(dayCyclical + i);
num_d = dayCyclical +i;
mLDay++;
//Log.i("","---num_y:"+num_y+","+num_m+","+num_d+",n_m:"+mLMonth+",n_d:"+mLDay+",mLun_x:"+mLun_x);
String str = CalConv2(num_y%12, num_m%12, num_d%12, num_y%10, num_d%10, mLMonth, mLDay-1);
if(str == null){
String var = jcName[num_m%12][num_d%12];
str = jcrt(var);
//Log.i("","---"+month+"-"+(i+1)+","+var+":"+str);
}
//Log.i("","---"+year+"-"+month+"-"+(i+1)+","+str);
yiji.add(str);
}
return yiji;
}
/*
* 公曆某日宜忌
* */
public String getyiji(int year, int month, int day) {
int num_y = -1;
int num_m = (year - 1900) * 12 + month-1 +12;
int num_d;
int mLMonth=1,mLDay=1;
int days_of_month = getDaysOfMonth(year, month);
if(day > days_of_month)
day = days_of_month;
//年柱 1900年立春後為庚子年(60進位制36)
//cyclical(year,month);
//立春日期
int term2 = sTerm(year, 2);
int firstNode = sTerm(year, (month-1)*2);//當月的24節氣中的節開始日
int dayCyclical = (int) (getDaysOfTwoDate(1900,1,1,year,month,1)+10);
if(month==2 && day==term2){
//cY = cyclicalm(year-1900 + 36);//依節氣調整二月分的年柱, 以立春為界
num_y = year - 1900 + 36;
}
if(day==firstNode) {
num_m =(year - 1900) * 12 + month +12 ;//依節氣 調整月柱, 以「節」為界
//cM = cyclicalm(num_m);
}
num_d = dayCyclical +day -1;
int var = getLunarDateINT(year,month,day);
int mLYear =(int)var/10000;
mLMonth = (int)(var%10000)/100;
mLDay = (int)(var - mLYear*10000 - mLMonth*100);
//Log.i("","---num_y:"+num_y+","+num_m+","+num_d+",n_m:"+mLMonth+",n_d:"+mLDay+",mLun_x:"+mLun_x);
String str = CalConv2(num_y%12, num_m%12, num_d%12, num_y%10, num_d%10, mLMonth, mLDay);
if(str == null){
str = jcrt(jcName[num_m%12][num_d%12]);
//Log.i("","---"+month+"-"+(i+1)+","+var+":"+str);
}
//Log.i("","---"+year+"-"+month+"-"+(i+1)+","+str);
return str;
}
/*
* 計算距離1900年12月31日days天后的日期
* */
public int getDateFromBaseDate(int days){
int year = 0,month = 0,day = 0;
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 1900);
cal.set(Calendar.MONTH, 0);
cal.set(Calendar.DAY_OF_MONTH,31);
cal.add(Calendar.DATE, days);
year = cal.get(Calendar.YEAR);
month = cal.get(Calendar.MONTH)+1;
day = cal.get(Calendar.DAY_OF_MONTH);
return 10000*year+100*month+day;
}
}複製程式碼
在getLunarDate方法中我修改了一下,原來的月份計算有點問題,沒有向上取整month = (int)Math.ceil((var%10000)/100);這是我修改的。
請注意,在呼叫的時候
myHolder.tv2.text = LunarCalendar().getLunarDate(c.get(Calendar.YEAR),c.get(Calendar.MONTH)+1,c.get(Calendar.DAY_OF_MONTH),false)複製程式碼
月份 加了1。Calendar的月份是從0開始的。
然後說說點選選中效果:
點選的時候
myHolder.ll_tv.setOnClickListener {
context as ScheduleActivity
c.set(Calendar.DATE, position + 1 - emptyCount)
modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value!!.obj = entity_null_obj(dateString)
modelHolder.callUpdate()
}複製程式碼
我把點選的日期放在了modelHolder中,然後通知modelHolder重新整理,modelHolder就會在外部重新整理adapter,然後modelHolder.get>().value?.obj?.s就是我們點選的日期,就被帶到adapter裡面了。
//選中
if(modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value?.obj?.s != null
&&modelHolder.get<entity_base_obj_objlist<entity_null_obj, scheduleObj>>().value!!.obj.s == dateString){
myHolder.ll_tv.backgroundDrawable = context.resources.getDrawable(R.drawable.red_circle2)
myHolder.tv.setTextColor(context.resources.getColor(R.color.white))
myHolder.tv2.setTextColor(context.resources.getColor(R.color.white))
}複製程式碼
這裡就是顯示被選中的式樣。這樣處理在切換月份後,選中日期不會丟失,在切換回之前的月份後,選中的日期還是選中的式樣。
選中日期的事件處理,比如請求網路資料,我寫在了modelHolder重新整理Adapter的時候,在那裡modelHolder會收到選中的日期。
modelHolder.get<entity_base_objlist<scheduleObj>>().observe(this, android.arch.lifecycle.Observer {
//收到事件,進行處理
})複製程式碼
點選月份的前後箭頭,可以切換月份
before_bt.setOnClickListener {
c?.add(Calendar.MONTH, -1)
date_text.text = sdf.format(c?.time)
loadData()
}
next_bt.setOnClickListener {
c?.add(Calendar.MONTH, 1)
date_text.text = sdf.format(c?.time)
loadData()
}複製程式碼
切換月份後,重新載入adapter即可。
最後,附贈一個收起控制元件的操作,手指按住下面的那兩條橫線,向上移動,控制元件會跟隨手指移動,最終關閉。
ll_toggle.setOnClickListener { ll_show_date.visibility = View.GONE }
ll_toggle.setOnTouchListener { view, event ->
if (event.action == MotionEvent.ACTION_DOWN && downY == 0f) {
downY = (ll_show_date.parent as ViewGroup).y
}
if (event.action == MotionEvent.ACTION_MOVE) {
if (-ll_show_date.height + event.rawY > downY || event.rawY <= downY) {
//限制滑動範圍
true
} else {
(ll_show_date.parent as ViewGroup).y = -ll_show_date.height + event.rawY
//移動過程中,隱藏下面的列表
list.visibility = View.INVISIBLE
}
}
if (event.action == MotionEvent.ACTION_UP) {
if (-ll_show_date.height + event.rawY < downY) {
(ll_show_date.parent as ViewGroup).y = downY
ll_show_date.visibility = View.GONE
}
//移動完了,顯示列表
list.visibility = View.VISIBLE
}
false
}複製程式碼
這部分程式碼沒什麼營養,就是根據手指座標,修改控制元件的座標。
最後的最後,ModelHolder是Livedata中的ViewModel。