직접 제작한 timePicker, CalendarPicker
1. 탭 크기 조절 연동
2. 폴드 작은화면 연동
3. 폴드 큰화면 연동
4. 폴드 접엇다 펴도 데이터 연동
5. 회전시 데이터 연동 및 크기 조절
public class CalendarPickerPopupDialog extends Dialog {
private Context context;
private CalendarPopupDialogClickListener CalendarPopupDialogClickListener;
private CalendarView calendar_alert;
private TextView tvTitle, tvNegative, tvPositive;
private String text;
private String title;
Reserves reserves;
//커스텀
private int setYearValue;
private int setMonthValue;
private int setDayValue;
public CalendarPickerPopupDialog(@NonNull Context context, CalendarPopupDialogClickListener CalendarPopupDialogClickListener) {
super(context);
this.context = context;
this.CalendarPopupDialogClickListener = CalendarPopupDialogClickListener;
}
public void setText(String text) {
this.text = text;
}
public void setYearValue(int setYearValue) { this.setYearValue = setYearValue; }
public void setMonthValue(int setMonthValue) {
this.setMonthValue = setMonthValue;
}
public void setDayValue(int setDayValue) { this.setDayValue = setDayValue; }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.calendar_dialog);
calendar_alert = (CalendarView) findViewById(R.id.calendar_alert);
calendar_alert.setMinDate(System.currentTimeMillis() - 1000); //이전 날짜 선택 막기
int year = setYearValue;
int month = setMonthValue-1;
int day = setDayValue;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
long milliTime = calendar.getTimeInMillis();
calendar_alert.setDate (milliTime, true, true);
calendar_alert.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
@Override
public void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth) {
setYearValue = year;
setMonthValue = month + 1;
setDayValue = dayOfMonth;
reserves = new Reserves(year,month+1, dayOfMonth);
EventBus.getDefault().post(new MessageEvent(reserves));
}
});
tvPositive = findViewById(R.id.btn_calendar_ok);
tvNegative = findViewById(R.id.btn_calendar_cancel);
//
tvPositive.setOnClickListener(v -> {
this.CalendarPopupDialogClickListener.onPositiveClick(setYearValue,setMonthValue,setDayValue);
dismiss();
});
//
tvNegative.setOnClickListener(v -> {
this.CalendarPopupDialogClickListener.onNegativeClick();
dismiss();
});
//
}
}
public interface CalendarPopupDialogClickListener {
void onPositiveClick(int y, int m, int d);
void onNegativeClick();
}
public class TimePickerPopupDialog extends Dialog {
private Context context;
private TimePickerPopupDialogClickListener TimePickerPopupDialogClickListener;
private TimePicker timePicker;
private TextView tvTitle, tvNegative, tvPositive;
private String text;
private String title;
Reserves reserves;
//커스텀
private int setHourValue;
private int setMinuteValue;
public TimePickerPopupDialog(@NonNull Context context, TimePickerPopupDialogClickListener TimePickerPopupDialogClickListener) {
super(context);
this.context = context;
this.TimePickerPopupDialogClickListener = TimePickerPopupDialogClickListener;
}
public void setText(String text) {
this.text = text;
}
public void setHourValue(int setHourValue) { this.setHourValue = setHourValue; }
public void setMinuteValue(int setSetMinuteValue) {
this.setMinuteValue = setSetMinuteValue;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timepicker_dialog);
timePicker = (TimePicker) findViewById(R.id.timepicker_alert);
timePicker.setIs24HourView(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
timePicker.setHour(setHourValue);
timePicker.setMinute(setMinuteValue);
} else{
timePicker.setCurrentHour(setHourValue);
timePicker.setCurrentMinute(setMinuteValue);
}
timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
setHourValue = hourOfDay;
setMinuteValue = minute;
reserves = new Reserves(hourOfDay, minute);
EventBus.getDefault().post(new MessageEvent(reserves));
}
});
tvPositive = findViewById(R.id.btn_time_ok);
tvNegative = findViewById(R.id.btn_time_cancel);
tvPositive.setOnClickListener(v -> {
this.TimePickerPopupDialogClickListener.onPositiveClick(setHourValue,setMinuteValue);
dismiss();
});
tvNegative.setOnClickListener(v -> {
this.TimePickerPopupDialogClickListener.onNegativeClick();
dismiss();
});
}
}
public interface TimePickerPopupDialogClickListener {
void onPositiveClick(int h, int m);
void onNegativeClick();
}
public class Reserves {
private static Integer nowYear;
private static Integer nowMonth;
private static Integer nowDay;
private static Integer nowHour;
private static Integer nowMinute;
private static Boolean timePicker = false;
private static Boolean calendarPicker = false;
public Reserves(Integer nowYear,Integer nowMonth, Integer nowDay){
this.calendarPicker = true;
this.nowYear = nowYear;
this.nowMonth = nowMonth;
this.nowDay = nowDay;
}
public Reserves(Integer nowHour, Integer nowMinute) {
this.timePicker = true;
this.nowHour = nowHour;
this.nowMinute = nowMinute;
}
public void setNowHour(Integer hour){nowHour = hour;}
public Integer getNowHour(){return nowHour;}
public void setNowMinute(Integer minute){nowMinute = minute;}
public Integer getNowMinute(){return nowMinute;}
public void setNowYear(Integer year){nowYear = year;}
public Integer getNowYear(){return nowYear;}
public void setNowMonth(Integer month){nowMonth= month;}
public Integer getNowMonth(){return nowMonth;}
public void setNowDay(Integer day){nowDay = day;}
public Integer getNowDay(){return nowDay;}
public Boolean getCalendarPicker(){return calendarPicker;}
public Boolean getTimePicker(){return timePicker;}
}
public class MyViewModel extends ViewModel {
private MutableLiveData<Reserves> reserves;
public MutableLiveData<Reserves> getReserves() {
if (reserves == null) {
reserves = new MutableLiveData<Reserves>();
}
return reserves;
}
}
public class MyViewModelFactory implements ViewModelProvider.Factory {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
try {
return modelClass.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("팩토리 런타임 에러");
} catch (InstantiationException e) {
e.printStackTrace();
throw new RuntimeException("팩토리 런타임 에러2");
}
}
}
public class MessageEvent {
public final Reserves message;
public MessageEvent(Reserves message) {
this.message = message;
}
}
public class MainActivity extends AppCompatActivity {
private final static String TAG = MainActivity.class.getSimpleName();
TimePickerPopupDialog TimePickerPopupDialog;
CalendarPickerPopupDialog CalendarPickerPopupDialog;
//@Bell 20221208 picker
TextView txtRevDate, txtRevTime, txtRevAmPm;
String finalDateTime = "";
String finalDate = "";
String finalTime = "";
int finalYear, finalMonth, finalDay, finalHour, finalMinute;
int nowYear, nowMonth, nowDay;
int nowHour, nowMinute;
int changeYear, changeMonth, changeDay;
int changeHour, changeMinute;
Reserves reserves;
MyViewModel viewModel;
Boolean changeDate = false;
Boolean changeTime = false;
Boolean changeFinalTime = false;
Boolean changeFinalDate = false;
//모션
CheckBox cb_sendLater;
LinearLayout linRevDateTime;
ImageView imgRevColck;
//@Bell 20221208 picker
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
if(event.message.getCalendarPicker()){
this.changeYear = event.message.getNowYear();
this.changeMonth = event.message.getNowMonth();
this.changeDay = event.message.getNowDay();
this.changeDate = true;
}
if(event.message.getTimePicker()){
this.changeHour = event.message.getNowHour();
this.changeMinute = event.message.getNowMinute();
this.changeTime = true;
}
//
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
//피커
cb_sendLater = (CheckBox) findViewById(R.id.cb_sendLater);
linRevDateTime = (LinearLayout) findViewById(R.id.linRevDateTime);
imgRevColck = (ImageView) findViewById(R.id.imgRevClock);
txtRevDate = (TextView) findViewById(R.id.txtRevDate);
txtRevTime = (TextView) findViewById(R.id.txtRevTime);
txtRevAmPm = (TextView) findViewById(R.id.txtRevAmPm);
handleScheduledMessage();
}
// 가로 / 세로모드
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//안해도됨 (타임피커는 연동됨)
// if (TimePickerPopupDialog != null) {
// TimePickerPopupDialog.dismiss();
// changeFinalTime = false;
// //세로
// if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
// getTimePickerPopupDialog();
//
// //가로
// } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// getTimePickerPopupDialog();
//
// }
// changeFinalTime = true;
// }
if (CalendarPickerPopupDialog != null) {
CalendarPickerPopupDialog.dismiss();
if(changeDate == false){ changeDate = true; }
changeFinalDate = false;
//세로
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
getCalendarPickerPopupDialog();
//가로
} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
getCalendarPickerPopupDialog();
}
changeFinalDate = true;
}
}
//@Bell 20221208 피커 깨지는 현상 수정 및 예약 쪽지 테마변경 및 커스텀
private void handleScheduledMessage() {
cb_sendLater.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
tenMinuteLater();
txtRevDate.setText(nowYear+". "+nowMonth+". "+nowDay);
txtRevTime.setText(getFormattedHour(nowHour)+":"+nowMinute);
txtRevAmPm.setText(getAmPmTen(nowHour));
finalDate = getFinalDate(nowYear, nowMonth, nowDay);
finalTime = getFinalTime(nowHour, nowMinute);
finalDateTime = finalDate + "" + finalTime;
Log.d(TAG+ " @@@@", "finalDateTime: "+ finalDateTime);
// Animation animation = AnimationUtils.loadAnimation(getActivity(),R.anim.anim_shake_clock);
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.anim_shake_clock);
linRevDateTime.setVisibility(View.VISIBLE);
imgRevColck.startAnimation(animation);
//Picker
txtRevDate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { getCalendarPickerPopupDialog(); }
});
txtRevTime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getTimePickerPopupDialog();
}
});
//여기서최종 정보 전송
Log.d(TAG + "@@@@", "Last ! finalDateTime: " + finalDateTime);
} else {
linRevDateTime.setVisibility(View.GONE);
changeFinalDate = false;
changeFinalTime = false;
changeDate = false;
changeTime = false;
//
}
}
});
}
//@Bell Picker
//데이트피커 다이얼로그
//켈린더피커 다이얼로그
private Date currentDate = new Date();
public void getCalendarPickerPopupDialog(){
CalendarPickerPopupDialog = new CalendarPickerPopupDialog(MainActivity.this, new CalendarPopupDialogClickListener() {
@Override
public void onPositiveClick(int y, int m, int d) {
CalendarPickerPopupDialog = null;
txtRevDate.setText(y+". "+m+". "+d);
finalDate = getFinalDate(y,m,d);
finalDateTime = finalDate + "" + finalTime;
finalYear = y;
finalMonth = m;
finalDay = d;
changeFinalDate = true;
changeTime = false;
if (stringToDate(finalDateTime).before(currentDate)) {
txtRevAmPm.setText(getAmPmTen(nowHour));
txtRevTime.setText(getFormattedHour(nowHour)+":"+nowMinute);
finalTime = getFinalTime(nowHour,nowMinute);
finalDateTime = finalDate + "" + finalTime;
Log.d(TAG+ "@@@@", "befor finalDateTime: "+ finalDateTime);
Toast.makeText(MainActivity.this, "시간을 다시 전택해 주세요", Toast.LENGTH_SHORT).show();
changeFinalTime = false;
changeFinalDate = false;
changeTime = false;
changeDate = false;
} else {
Log.d(TAG+ "@@@@", "finalDateTime: "+ finalDateTime);
Toast.makeText(MainActivity.this, y+"년 "+m+"월 "+d+"일 선택되었습니다.", Toast.LENGTH_SHORT).show();
}
Log.d(TAG + "@@@@", "Last calendar finalDateTime: " + finalDateTime);
//여기서 최종 데이터 전송
}
@Override
public void onNegativeClick() {
CalendarPickerPopupDialog = null;
changeDate = false;
changeTime = false;
Toast.makeText(MainActivity.this, "취소되었습니다.", Toast.LENGTH_SHORT).show();
}
});
// getCurrentDate();
tenMinuteLater();
if(changeFinalDate) {
CalendarPickerPopupDialog.setYearValue(finalYear);
CalendarPickerPopupDialog.setMonthValue(finalMonth);
CalendarPickerPopupDialog.setDayValue(finalDay);
}else if(changeDate) {
reserves = new Reserves(changeYear, changeMonth, changeDay);
//뷰모델 객체 생성
viewModel = new ViewModelProvider(this, new MyViewModelFactory())
.get(MyViewModel.class);
//옵저버 정의 - 데이터가 변하는 이벤트 발생시 처리할 핸들러(람다)
Observer<Reserves> PickerObserver = PickerData -> reserves = PickerData;
//뷰모델에 옵저버 등록
viewModel.getReserves().observe(this, PickerObserver);
viewModel.getReserves().setValue(reserves);
CalendarPickerPopupDialog.setYearValue(viewModel.getReserves().getValue().getNowYear());
CalendarPickerPopupDialog.setMonthValue(viewModel.getReserves().getValue().getNowMonth());
CalendarPickerPopupDialog.setDayValue(viewModel.getReserves().getValue().getNowDay());
}else {
CalendarPickerPopupDialog.setYearValue(nowYear);
CalendarPickerPopupDialog.setMonthValue(nowMonth);
CalendarPickerPopupDialog.setDayValue(nowDay);
}
CalendarPickerPopupDialog.setCancelable(false);
CalendarPickerPopupDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
CalendarPickerPopupDialog.show();
}
public void getTimePickerPopupDialog(){
TimePickerPopupDialog = new TimePickerPopupDialog(MainActivity.this, new TimePickerPopupDialogClickListener() {
@Override
public void onPositiveClick(int h, int m) {
TimePickerPopupDialog = null;
finalTime = getFinalTime(h,m);
finalDateTime = finalDate + "" + finalTime;
finalHour = h;
finalMinute = m;
changeFinalTime = true;
changeDate = false;
if (stringToDate(finalDateTime).before(currentDate)) {
txtRevAmPm.setText(getAmPmTen(nowHour));
txtRevTime.setText(getFormattedHour(nowHour)+":"+nowMinute);
finalTime = getFinalTime(nowHour,nowMinute);
finalDateTime = finalDate + "" + finalTime;
Log.d(TAG+ "@@@@", "befor finalDateTime: "+ finalDateTime);
Toast.makeText(MainActivity.this, "시간을 다시 선택해 주세요", Toast.LENGTH_SHORT).show();
changeFinalTime = false;
changeFinalDate = false;
changeTime = false;
changeDate = false;
} else {
txtRevAmPm.setText(getAmPmTen(h));
txtRevTime.setText(getFormattedHour(h)+":"+m);
finalTime = getFinalTime(h,m);
finalDateTime = finalDate + "" + finalTime;
Log.d(TAG+ "@@@@", "finalDateTime: "+ finalDateTime);
Toast.makeText(MainActivity.this, getAmPmTen(h)+" "+getFormattedHour(h)+"시"+m+"분 선택되었습니다.", Toast.LENGTH_SHORT).show();
}
Log.d(TAG + "@@@@", "Last time finalDateTime: " + finalDateTime);
//여기서 최종 데이터 전송
}
@Override
public void onNegativeClick() {
TimePickerPopupDialog = null;
changeDate = false;
changeTime = false;
Toast.makeText(MainActivity.this, "취소되었습니다.", Toast.LENGTH_SHORT).show();
}
});
// getCurrentTime(); //현시간
tenMinuteLater();
if(changeFinalTime) {
TimePickerPopupDialog.setHourValue(finalHour);
TimePickerPopupDialog.setMinuteValue(finalMinute);
}else if(changeTime) {
reserves = new Reserves(changeHour, changeMinute);
//뷰모델 객체 생성
viewModel = new ViewModelProvider(this, new MyViewModelFactory())
.get(MyViewModel.class);
//옵저버 정의 - 데이터가 변하는 이벤트 발생시 처리할 핸들러(람다)
Observer<Reserves> PickerObserver = PickerData -> reserves = PickerData;
//뷰모델에 옵저버 등록
viewModel.getReserves().observe(this, PickerObserver);
viewModel.getReserves().setValue(reserves);
TimePickerPopupDialog.setHourValue(viewModel.getReserves().getValue().getNowHour());
TimePickerPopupDialog.setMinuteValue(viewModel.getReserves().getValue().getNowMinute());
}else {
TimePickerPopupDialog.setHourValue(nowHour);
TimePickerPopupDialog.setMinuteValue(nowMinute);
}
TimePickerPopupDialog.setCancelable(false);
TimePickerPopupDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
TimePickerPopupDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
TimePickerPopupDialog.show();
}
//현재날짜
public void getCurrentDate() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
LocalDate now = LocalDate.now();
this.nowYear = now.getYear();
this.nowMonth = now.getMonth().getValue();
Log.d("@@@@", "nowMonth : "+ nowMonth);
this.nowDay = now.getDayOfMonth();
} else {
Date now = new Date();
SimpleDateFormat formatterYear = new SimpleDateFormat("yyyy");
SimpleDateFormat formatterMonth = new SimpleDateFormat("MM");
SimpleDateFormat formatterDay = new SimpleDateFormat("dd");
int formatedNowYear = Integer.parseInt(formatterYear.format(now));
int formatedNowMonth = Integer.parseInt(formatterMonth.format(now));
int formatedNowDay = Integer.parseInt(formatterDay.format(now));
Log.d("@@@@", formatedNowYear+" "+formatedNowMonth+" "+formatedNowDay);
this.nowYear = formatedNowYear;
this.nowMonth = formatedNowMonth;
this.nowDay = formatedNowDay;
}
}
//현재시간
public void getCurrentTime() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
LocalTime now = LocalTime.now();
this.nowHour = now.getHour();
this.nowMinute = now.getMinute();
} else {
Date now = new Date();
SimpleDateFormat formatterHour = new SimpleDateFormat("HH");
SimpleDateFormat formatterMinute = new SimpleDateFormat("mm");
int formatedNowHour = Integer.parseInt(formatterHour.format(now));
int formatedNowMinute = Integer.parseInt(formatterMinute.format(now));
Log.d("@@@@", formatedNowHour + " " + formatedNowMinute);
this.nowHour = formatedNowHour;
this.nowMinute = formatedNowMinute;
}
}
//메시지 예약 시간 Am Pm
public String getAmPmTen(int hour) {
String amPm;
if(hour >= 12){
amPm = "오후";
} else{
amPm = "오전";
}
return amPm;
}
//메시지 예약 시간 24시간 -> 12시간 formatter
public int getFormattedHour (int hour) {
if(hour >= 13){
hour = hour - 12;
}
return hour;
}
//10분뒤 날짜 시간
public void tenMinuteLater(){
Calendar cal = Calendar.getInstance();
SimpleDateFormat formatterYear = new SimpleDateFormat("yyyy");
SimpleDateFormat formatterMonth = new SimpleDateFormat("MM");
SimpleDateFormat formatterDay = new SimpleDateFormat("dd");
SimpleDateFormat formatterHour = new SimpleDateFormat("HH");
SimpleDateFormat formatterMinute = new SimpleDateFormat("mm");
cal.add(Calendar.MINUTE, 10);
int formatedNowYear = Integer.parseInt(formatterYear.format(cal.getTime()));
int formatedNowMonth = Integer.parseInt(formatterMonth.format(cal.getTime()));
int formatedNowDay = Integer.parseInt(formatterDay.format(cal.getTime()));
int formatedNowHour = Integer.parseInt(formatterHour.format(cal.getTime()));
int formatedNowMinute = Integer.parseInt(formatterMinute.format(cal.getTime()));
this.nowYear = formatedNowYear;
this.nowMonth = formatedNowMonth;
this.nowDay = formatedNowDay;
this.nowHour = formatedNowHour;
this.nowMinute = formatedNowMinute;
}
//finalDate formatter
public String getFinalDate(int mY, int mM, int mD){
finalDate = String.format("%04d", mY)+"년 "+String.format("%02d", mM)+"월 "+String.format("%02d", mD)+"일 ";
Log.d(TAG + "@@@@ getFinalDate ", "finalDate: " + finalDate);
return finalDate;
}
//finalTime formatter
public String getFinalTime(int mH, int mM){
finalTime = String.format("%02d", mH)+"시 "+String.format("%02d", mM)+"분 ";
Log.d(TAG + "@@@@ getFinalTime", "finalTime: " + finalTime);
return finalTime;
}
public Date stringToDate(String mFinalDateTime){
Date dateFinalDateTime = null;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy년 MM월 dd일 HH시 mm분");
try {
dateFinalDateTime = dateFormat.parse(mFinalDateTime);
Log.d(TAG + "@@@@", "dateFinalDateTime: " + dateFinalDateTime );
Log.d(TAG + "@@@@", "mFinalDateTime: " + mFinalDateTime );
} catch (Exception e){ e.printStackTrace(); }
return dateFinalDateTime;
}
//다이얼로그 리사이즈
public void dialogResize(Dialog mDialog, float x, float y){
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
Window window = mDialog.getWindow();
int mX = (int)(size.x * x);
int mY = (int)(size.y * y);
window.setLayout(mX,mY);
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/share_round_popup">
<View
android:id="@+id/calendar_view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
app:layout_constraintBottom_toTopOf="@+id/btn_calendar_cancel" />
<TextView
android:id="@+id/btn_calendar_ok"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="OK"
android:gravity="end"
android:paddingRight="15dp"
android:textSize="16sp"
android:paddingVertical="8dp"
android:textColor="@color/blue"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btn_calendar_cancel" />
<TextView
android:id="@+id/btn_calendar_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Cancel"
android:gravity="start"
android:paddingLeft="15dp"
android:textSize="16sp"
android:paddingVertical="8dp"
android:textColor="@color/blue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toStartOf="@+id/btn_calendar_ok"/>
<CalendarView
android:id="@+id/calendar_alert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/InfoCalendarPickerTheme"
app:layout_constraintBottom_toTopOf="@+id/calendar_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/share_round_popup"
xmlns:app="http://schemas.android.com/apk/res-auto">
<View
android:id="@+id/time_view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#F0F0F0"
app:layout_constraintBottom_toTopOf="@+id/btn_time_cancel" />
<TextView
android:id="@+id/btn_time_ok"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="OK"
android:gravity="end"
android:paddingRight="15dp"
android:textSize="16sp"
android:paddingVertical="8dp"
android:textColor="@color/blue"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/btn_time_cancel" />
<TextView
android:id="@+id/btn_time_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Cancel"
android:gravity="start"
android:paddingLeft="15dp"
android:textSize="16sp"
android:paddingVertical="8dp"
android:textColor="@color/blue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toStartOf="@+id/btn_time_ok"/>
<TimePicker
android:id="@+id/timepicker_alert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/InfoTimePickerTheme"
app:layout_constraintBottom_toTopOf="@+id/time_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:timePickerMode="spinner"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main"
tools:context=".MainActivity">
<CheckBox
android:id="@+id/cb_sendLater"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:buttonTint="@color/black"
android:gravity="center"
android:scaleX="0.9"
android:scaleY="0.9"
android:text="check"
android:textColor="@color/black"
android:textSize="13dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.953"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.023" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cvRevDataTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:background="@color/white"
app:cardBackgroundColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/linRevDateTime"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="@+id/imgRevClock"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginVertical="8dp"
android:layout_marginStart="10dp"
android:src="@drawable/alarm" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/txtRevDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingHorizontal="10dp"
android:paddingVertical="8dp"
android:text="0000. 00. 00"
android:textColor="@color/black"
android:textSize="13dp" />
<TextView
android:id="@+id/txtRevAmPm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingVertical="8dp"
android:text="오후"
android:textColor="@color/black"
android:textSize="13dp" />
<TextView
android:id="@+id/txtRevTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingVertical="8dp"
android:paddingLeft="3dp"
android:paddingEnd="13dp"
android:text="00:00"
android:textColor="@color/black"
android:textSize="13dp" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8L11,8v6l4.75,2.85 0.75,-1.23 -4,-2.37L12.5,8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/back">
<solid android:color="@color/blue" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/shape_popup">
<corners android:radius="15dp" />
<solid android:color="@color/white" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="10"
android:toYDelta="10"
android:duration="500"
android:interpolator="@anim/cycle_4_pf" />
<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="4" />
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout"
탭연동, 가로 세로 회전연동
<color name="blue">#0078FF</color>
<style name="InfoTimePickerTheme">
<item name="android:textSize">17sp</item>
<item name="android:textColorPrimary">@color/black</item>
<item name="colorControlNormal">#F0F0F0</item>
</style>
<style name="InfoCalendarPickerTheme">
<item name="colorControlActivated">@color/blue</item>
<item name="android:textStyle">bold</item>
</style>
implementation 'org.greenrobot:eventbus:3.3.1'
참고
https://www.tutorialsbuzz.com/2019/09/android-datepicker-dialog-styling-kotlin.html
https://dodokwon.tistory.com/6
https://chc3484.tistory.com/101
https://soir1984.tistory.com/31
https://colalove5562.tistory.com/2
https://developer.android.com/develop/ui/views/components/pickers