Android学习 UI模仿练习之“巴士管家”选取车票
Posted Nicholas_hzf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android学习 UI模仿练习之“巴士管家”选取车票相关的知识,希望对你有一定的参考价值。
模拟一些优秀的APP的界面绘制,实现类似功能。绘制“简陋版界面”,哈哈哈。
主要控件:TabLayout+RecyclerView+自定义CalendarView
模拟重点:TabLayout
一、界面效果
二、设计实现
(一)文件列表
(二)0积分获取代码
https://download.csdn.net/download/Nicholas1hzf/12363547
(三)关键代码讲解
1. 顶部日期选择器(30天)的实现
难点: 最右侧阴影效果的实现,Tab的标题获取,自定义日历选择器以及日历选择器与选项卡布局的交互
思路介绍:
外层布局使用 FrameLayout 是因为帧布局可以实现控件遮挡的效果,前面的控件可以被后面的控件遮挡。TabLayout 占满整个布局的宽度,再把“全部日期”的布局(LinearLayout)布置在父布局(FrameLayout)的最右侧(layout_gravity=“end”),成功将 Tab 挡住,这样当 TabLayout 的 Tab 向左滑动时,有一种“火车出洞”的感觉,为了增强此效果,最右侧的布局没有简单使用一个 TextView 而是采用线性布局,内含一个 View(背景使用深色20%透明【#33000000】,产生阴影遮挡效果)和一个底部带有向下箭头(drawableBottom="@drawable/ic_arrow_drop_down")的 TextView(文字提示,提供点击)
A.)最右侧阴影效果的实现
关键代码
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_color_shade">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="2dp"
android:layout_gravity="start|center_vertical"
app:tabMode="scrollable"
app:tabGravity="fill"
app:tabIndicatorGravity="stretch"
app:tabIndicator="@drawable/bg_rrc_filled_white"
app:tabIndicatorColor="@color/background_color_white"
app:tabTextColor="@color/fc_white"
app:tabSelectedTextColor="@color/fc_light_blue"
app:tabTextAppearance="@style/AppTheme.TabLayout.TextAppearance.Date"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end">
<View
android:layout_width="10dp"
android:layout_height="match_parent"
android:background="@color/background_color_shade3"/>
<TextView
android:id="@+id/all_date_selector_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/background_color_light_blue"
android:text="@string/all_date"
android:textColor="@color/fc_white"
android:textSize="?attr/SmallText"
android:gravity="center"
android:drawableBottom="@drawable/ic_arrow_drop_down"/>
</LinearLayout>
</FrameLayout>
B.)Tab的标题获取
TabLayout-标签布局
注意点:tabIndicator 属性只提供样式,不提供颜色
属性名 | 属性值 | 效果 |
---|---|---|
tabMode | scrollable | Tab 可滚动 |
tabGravity | fill | 内容尽可能充满 TabLayout |
tabIndicatorGravity | stretch | 指示器拉伸填满 Tab |
tabIndicator | @drawable/bg_rrc_filled_white | 指示器样式 |
tabIndicatorColor | @color/background_color_white | 指示器颜色 |
tabTextColor | @color/fc_white | Tab 文字颜色 |
tabSelectedTextColor | @color/fc_light_blue | Tab 文字选中颜色 |
Java关键代码
/**********DateSelectorActivity**********/
//控件获取
TabLayout mTabLayout = findViewById(R.id.tab_layout);
//Tab 标题的获取
List<String> mResults = new ArrayList<>();
mResults.addAll(DateUtils.get30DD(Calendar.getInstance()));
mResults.add("敬请\\n期待");
//TabLayout 标题绑定
for (String s : mResults)
TabLayout.Tab tab = mTabLayout.newTab();
tab.setText(s);
mTabLayout.addTab(tab);
//给 TabLayout 的 item 添加分割线
LinearLayout linearLayout = (LinearLayout) mTabLayout.getChildAt(0);
linearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
linearLayout.setDividerDrawable(ContextCompat.getDrawable(this, R.drawable.tab_layout_cut_off_line));
linearLayout.setDividerPadding(12);
/**********DateUtils**********/
/**
* 获取某天之后30天的星期与日期 MM-dd\\n周几
* @param calendar 某天
* @return List<String>
*/
public static List<String> get30DD(Calendar calendar)
List<String> dd = new ArrayList<>();
for (int i = 0; i < 30; i++)
String data = "";
switch (i)
case 0:
data = getFutureDate(i,calendar)+"\\n今天";
break;
case 1:
data = getFutureDate(i,calendar)+"\\n明天";
break;
case 2:
data = getFutureDate(i,calendar)+"\\n后天";
break;
default:
data = getFutureDate(i,calendar)+"\\n"+getFutureDay(i,calendar);
dd.add(data);
return dd;
//获取 calendar 未来某天的月日
public static String getFutureDate(int future,Calendar calendar)
Calendar calendar1 = new GregorianCalendar();
calendar1.setTime(calendar.getTime());
calendar1.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + future);
Date today = calendar1.getTime();
SimpleDateFormat format = new SimpleDateFormat("MM-dd", Locale.CHINA);
return format.format(today);
//获取 calendar 未来某天的星期
public static String getFutureDay(int future,Calendar calendar)
Calendar calendar1 = new GregorianCalendar();
calendar1.setTime(calendar.getTime());
calendar1.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + future);
Date today = calendar1.getTime();
SimpleDateFormat format = new SimpleDateFormat("EEEE", Locale.CHINA);
return format.format(today);
C.)自定义日历选择器
学习改编自 android自定义控件之日历控件的实现
/**********CalendarView**********/
private void initial()
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); //获取今天周几 周日为1 周六为7
int monthStart = -1;
if(dayOfWeek >= 2 && dayOfWeek <= 7) //如果周一到周六
monthStart = dayOfWeek - 2; //本月第一天为0-5
else if(dayOfWeek == 1) //如果是周日
monthStart = 6; //本月第一天为6
curStartIndex = monthStart; //本月第一天的索引位置为monthStart(0-6)
date[monthStart] = 1;//将本月第一天以1号存入
int daysOfMonth = daysOfCurrentMonth();//获取本月天数
for (int i = 1; i < daysOfMonth; i++) //将这个月的日期存入
date[monthStart + i] = i + 1;
curEndIndex = monthStart + daysOfMonth;//本月最后一天的索引位置为monthStart(0-6)+天数
if(calendar.get(Calendar.YEAR) == Calendar.getInstance().get(Calendar.YEAR)
&& calendar.get(Calendar.MONTH) == Calendar.getInstance().get(Calendar.MONTH))
todayIndex = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + monthStart - 1;
else
todayIndex = -1;
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
float lineSX = cellWidth/2;
float lineEX = cellWidth*6+cellWidth/2;
float lineY = cellHeight*0.9f;
Paint linePaint = RenderUtil.getPaint(Color.parseColor("#92A7C2"));
linePaint.setStrokeWidth(4);
//绘制星期
float baseline = RenderUtil.getBaseline(0, cellHeight, weekTextPaint);
for (int i = 0; i < 7; i++)
float weekTextX = RenderUtil.getStartX(cellWidth * i + cellWidth * 0.5f, weekTextPaint, weekText[i]);
canvas.drawText(weekText[i], weekTextX, baseline, weekTextPaint);
//添加 星期下面的线
canvas.drawLine(lineSX,lineY,lineEX,lineY,linePaint);
//绘制日期
for (int i = curStartIndex; i < curEndIndex; i++)
if (i == todayIndex && i == selectedIndex)
drawCircle(canvas, i, selectedDayBgPaint, cellHeight * 0.48f);
drawText(canvas, i, selectedDayTextPaint, "" + date[i]);
else if(i == todayIndex)
drawText(canvas, i, todayTextPaint, "" + date[i]);
else if(i == selectedIndex && i > todayIndex)
drawCircle(canvas, i, selectedDayBgPaint, cellHeight * 0.48f);
drawText(canvas, i, selectedDayTextPaint, "" + date[i]);
else if (i < todayIndex)
drawText(canvas, i, beforeTodayTextPaint, "" + date[i]);
else
drawText(canvas, i, textPaint, "" + date[i]);
//绘制文字函数
private void drawText(Canvas canvas, int index, Paint paint, String text)
if(isIllegalIndex(index))
return;
int x = getXByIndex(index);
int y = getYByIndex(index);
float top = cellHeight + (y - 1) * cellHeight;
float bottom = top + cellHeight;
float baseline = RenderUtil.getBaseline(top, bottom, paint);
float startX = RenderUtil.getStartX(cellWidth * (x - 1) + cellWidth * 0.5f, paint, text);
canvas.drawText(text, startX, baseline, paint);
//绘制选中圆样式
private void drawCircle(Canvas canvas, int index, Paint paint, float radius)
if(isIllegalIndex(index))
return;
int x = getXByIndex(index);
int y = getYByIndex(index);
float centreY = cellHeight + (y - 1) * cellHeight + cellHeight * 0.5f;
float centreX = cellWidth * (x - 1) + cellWidth * 0.5f;
canvas.drawCircle(centreX, centreY, radius, paint);
/**********DateSelectorActivity**********/
//点击全部日期弹出日历选择器界面
//TextView 点击事件监听
mTextView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
showPop(v);
);
//弹窗函数 showPop(View v);
private void showPop(View view)
//获取弹窗布局
View popView = View.inflate(this, R.layout.pw_select_date, null);
//实例化布局中的控件
mCalendarView = popView.findViewById(R.id.calendar_view);
final TextView thisMonthTV = popView.findViewById(R.id.this_month_text_view);
final TextView nextMonthTV = popView.findViewById(R.id.next_month_text_view);
ImageView ensureIV = popView.findViewById(R.id.select_date_image_view);
//获得弹窗,并进行基础设置
final PopupWindow popupWindow = new PopupWindow(getResources().getDisplayMetrics().widthPixels-120,getResources().getDisplayMetrics().heightPixels/2);
popupWindow.setContentView(popView);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
addBackground(popupWindow);
popupWindow.showAtLocation(view, Gravity.CENTER,0,0);
//初始化数据(获取当前月份和下一个月的月份)
final int year = Calendar.getInstance().get(Calendar.YEAR);
final int month = Calendar.getInstance().get(Calendar.MONTH)+1;
int nextMonth;
int nextYear = year;
//考虑下一个月跨年的情况
if (month == 12)
nextMonth = 1;
nextYear++;
else
nextMonth = month+1;
//月份显示格式为:04月,11月,小于10的月份加0
String thisMonthString = "";
if (month < 10)
thisMonthString = "0"+month+"月";
else
thisMonthString = month+"月";
String nextMonthString = "";
if (nextMonth < 10)
nextMonthString = "0"+nextMonth+"月";
else
nextMonthString = nextMonth+"月";
thisMonthTV.setText(thisMonthString);
nextMonthTV.setText(nextMonthString);
//日历控件点击事件监听,下文会详细介绍
mCalendarView.setOnItemClickListener(new ICalendarView.OnItemClickListener()
@Override
public void onItemClick(int day) python进阶练习之——类的方法与变量❤️