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 属性只提供样式,不提供颜色

属性名属性值效果
tabModescrollableTab 可滚动
tabGravityfill内容尽可能充满 TabLayout
tabIndicatorGravitystretch指示器拉伸填满 Tab
tabIndicator@drawable/bg_rrc_filled_white指示器样式
tabIndicatorColor@color/background_color_white指示器颜色
tabTextColor@color/fc_whiteTab 文字颜色
tabSelectedTextColor@color/fc_light_blueTab 文字选中颜色

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进阶练习之——类的方法与变量❤️

python进阶练习之——作用域类的方法与变量❤️

Android Studio——常用组件练习之注册信息

python练习之银行模拟系统

算法设计与分析入门学习练习之二

深度强化学习之:模仿学习(imitation learning)