Days40 MaterialDesign(一)

本文详细介绍了MaterialDesign中的Snackbar、FloatingActionButton、TextInputLayout、DrawerLayout及NavigationView等组件的使用方法及注意事项。

Material Design不再让像素处于同一个平面,而是让它们按照规则处于空间当中,具备不同的维度。
使用时要导入design支持包。
备注:如果需要使用控件的自定义属性,需要添加(如果有很多控件需要添加自定义属性,可以在父布局中添加):
xmlns:app=”http://schemas.android.com/apk/res-auto”
(一)Snackbar:
简介:
一个类似Toast的控件,可以添加点击事件,并且可以支持手势滑动消除。
SnackBar最好是CoordinatorLayout一起使用,否则滑动删除的功能将失效
要想支持Swipe手势的话,这个view需要是一个CoordinatorLayout

注意:
make()方法的第一个参数的view,不能是有一个ScrollView.因为SnackBar的实现逻辑是往这个View去addView.而ScrollView我们知道,是只能有一个Child的.否则会Exception

//        i am snack info 为snackbar显示的信息
//        submit相当于一个可点击的TextView上的提示信息
//        setAction的第二个参数为点击事件
        Snackbar.make(view,"i am snack info",Snackbar.LENGTH_LONG).setAction("submit", new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("test","we are info");
            }
        }).show();
    }

(二)FloatingActionButton
可以增加onClick属性,当点击后可实现回到顶部或提示导航等功能。

<!--
    app:elevation,控件悬浮的高度
    app:layout_anchorGravity,控件显示的位置
    app:layout_anchor:悬浮参考位置
    app:fabSize:设置控件的大小
    -->
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:elevation="5dp"
        app:fabSize="mini"
        app:layout_anchorGravity="bottom|right"
        app:layout_anchor="@id/app_bar"
        android:src="@android:drawable/ic_input_add" />

(三)TextInputLayout
extends LinearLayout
一个文本输入框的容器,输入的时候提示信息hint不会消失,而是会收起到上方
这里写图片描述
布局文件:

 <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:hint="行百里者半九十"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.design.widget.TextInputLayout>

备注:
返回当前获得焦点的View和关闭软键盘的方法

 //getCurrentFocus()是获取当前activity中获得焦点的view
        View v = getCurrentFocus();
        if(v != null){
            //隐藏软键盘
            ((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
        }

(四)DrawerLayout 抽屉 对比SlidingMenu
extends ViewGroup
这里写图片描述
布局文件

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_draw_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.sign.days39md01.draw.DrawLayoutActivity">

    <!--最外层节点为DrawerLayout,要把margin属性删掉,不然边距的空白部分会覆盖住内容-->
    <!--通过设置内层节点的android:layout_gravity="left"或"right",设置左边/右边抽屉-->
    <!--抽屉的width属性即使为match_parent,也会留出一小块区域显示主界面-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textColor="#0fc"
            android:textSize="26sp"
            android:onClick="content"
            android:text="this is content"/>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#07e7e7"
        android:layout_gravity="left">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textColor="#df1a1a"
            android:onClick="left"
            android:textSize="26sp"
            android:text="this is left menu"/>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="right"
        android:background="#fac21a">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="this is right menu"
            android:textSize="26sp"
            android:onClick="right"
            android:textColor="#bc07c2"/>
    </RelativeLayout>

</android.support.v4.widget.DrawerLayout>

代码:

public class DrawLayoutActivity extends AppCompatActivity {

    private DrawerLayout drawLayout = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_draw_layout);

        initView();
    }

    private void initView() {
        drawLayout = (DrawerLayout) findViewById(R.id.activity_draw_layout);
    }

    public void right(View view) {
//      关闭抽屉;左边抽屉(Gravity.LEFT),右边抽屉(Gravity.RIGHT)
        drawLayout.closeDrawer(Gravity.RIGHT);
    }

    public void left(View view) {
        drawLayout.closeDrawer(Gravity.LEFT);
    }

    public void content(View view) {
//        打开抽屉
        drawLayout.openDrawer(Gravity.LEFT);
    }
}

(五)NavigationView 导航栏
功能:打开左边抽屉,里面包含导航栏,点击导航栏的菜单条目后,根据所点击条目进行相应操作
这里写图片描述
布局文件

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_navdraw_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.sign.days39md01.navdraw.NavigationDrawerLayoutActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="this is content"/>
    </RelativeLayout>

    <!--打开抽屉为一个NavigationView   导航栏-->
    <!--注意两个属性:app:headerLayout="@layout/my_header"代表导航栏的头部-->
    <!--app:menu="@menu/my_menu"代表导航栏的菜单条目部分-->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvShow"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        app:headerLayout="@layout/my_header"
        app:menu="@menu/my_menu"
        android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>

menu:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/itSetting"
        android:title="setting"/>
    <item android:id="@+id/itAbout"
        android:title="about us"/>
    <item android:id="@+id/itClear"
        android:title="clear cache"/>
</menu>

代码:

public class NavigationDrawerLayoutActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout = null;
    private NavigationView nvShow = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_navigation_drawer_layout);

        initView();

//        获得头部视图************************重要
        View v = nvShow.getHeaderView(0);
        TextView txt = (TextView) v.findViewById(R.id.txtHeader);
        txt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(NavigationDrawerLayoutActivity.this,"行百里者半九十",Toast.LENGTH_LONG).show();
            }
        });


//        设置NavigationView的菜单条目点击监听
        nvShow.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem item) {
                switch (item.getItemId()){
                    case R.id.itAbout:
                        Toast.makeText(NavigationDrawerLayoutActivity.this,"i am about us",Toast.LENGTH_LONG).show();
                        break;
                    case R.id.itClear:
                        Toast.makeText(NavigationDrawerLayoutActivity.this,"i am clear cache",Toast.LENGTH_LONG).show();
                        break;
                    case R.id.itSetting:
                        Toast.makeText(NavigationDrawerLayoutActivity.this,"i am setting",Toast.LENGTH_LONG).show();
                        break;
                }

//                点击菜单条目后关闭抽屉
                drawerLayout.closeDrawer(Gravity.LEFT);
                return false;
            }
        });
    }
    private void initView() {
        drawerLayout = (DrawerLayout) findViewById(R.id.activity_navdraw_layout);
        nvShow = (NavigationView) findViewById(R.id.nvShow);
    }
}
好的,下面我将步地指导你如何在 Android Studio 中开发个基于 MIUI 13.0.6 的倒计时桌面组件。我会尽量使用简单易懂的语言,并列出所有需要的步骤和库。 --- ### **步骤 1:准备工作** #### 所需软件和工具: - **Android Studio 2024.3.2.15**(确保已安装) - **Java 或 Kotlin**(我们选择 Kotlin,因为它更现代化且简洁) #### 创建项目: 1. 打开 Android Studio。 2. 点击 `File -> New -> New Project`。 3. 选择 `Empty Activity` 模板,点击 `Next`。 4. 输入项目名称(如 `CountdownWidget`),选择语言为 `Kotlin`。 5. 设置最低 SDK 版本为 `API 21: Android 5.0 (Lollipop)`,以兼容更多设备。 6. 点击 `Finish`,等待项目加载完成。 --- ### **步骤 2:添加必要的库** 我们需要以下库来实现功能: 1. **Material Design 库**:用于美化界面。 2. **DateTime 库**:用于日期和时间操作。 #### 添加依赖: 打开 `build.gradle (Module: app)` 文件,在 `dependencies` 部分添加以下内容: ```gradle implementation 'com.google.android.material:material:1.9.0' // Material Design implementation 'org.threeten:threetenbp:1.5.1' // DateTime 处理库 ``` 然后点击右上角的 `Sync Now` 按钮同步项目。 --- ### **步骤 3:设计布局** 我们需要个简单的布局来显示倒计时信息。 #### 修改 `res/layout/activity_main.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <!-- 目标事件 --> <TextView android:id="@+id/tv_event_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="目标事件" android:textSize="24sp" android:textStyle="bold" /> <!-- 倒计时显示 --> <TextView android:id="@+id/tv_countdown" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00天00时00分00秒" android:textSize="36sp" android:textStyle="bold" /> <!-- 设置目标日期 --> <Button android:id="@+id/btn_set_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="设置目标日期" /> </LinearLayout> ``` --- ### **步骤 4:编写逻辑代码** 我们需要实现以下功能: 1. 计算当前时间与目标时间之间的差值。 2. 根据剩余时间动态显示年月日或时分秒。 #### 修改 `MainActivity.kt`: ```kotlin package com.example.countdownwidget import android.os.Bundle import android.widget.Button import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import org.threeten.bp.LocalDate import org.threeten.bp.LocalDateTime import org.threeten.bp.format.DateTimeFormatter import java.util.* class MainActivity : AppCompatActivity() { private lateinit var tvEventName: TextView private lateinit var tvCountdown: TextView private lateinit var btnSetDate: Button private var targetDate: LocalDateTime = LocalDateTime.now() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 绑定控件 tvEventName = findViewById(R.id.tv_event_name) tvCountdown = findViewById(R.id.tv_countdown) btnSetDate = findViewById(R.id.btn_set_date) // 初始化目标事件 tvEventName.text = "流浪地球2倒计时" // 更新倒计时 updateCountdown() // 设置按钮点击事件 btnSetDate.setOnClickListener { showDatePickerDialog() } } private fun updateCountdown() { val now = LocalDateTime.now() val duration = Duration.between(now, targetDate) if (duration.toDays() <= 0) { // 如果只剩天,显示时分秒 val hours = duration.toHours() % 24 val minutes = duration.toMinutes() % 60 val seconds = duration.seconds % 60 tvCountdown.text = String.format( Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds ) } else { // 如果超过天,显示年月日 val days = duration.toDays() val months = days / 30 val years = months / 12 tvCountdown.text = String.format( Locale.getDefault(), "%d年%d个月%d天", years, months % 12, days % 30 ) } // 每秒更新次 Handler(Looper.getMainLooper()).postDelayed({ updateCountdown() }, 1000) } private fun showDatePickerDialog() { val calendar = Calendar.getInstance() val year = calendar.get(Calendar.YEAR) val month = calendar.get(Calendar.MONTH) val day = calendar.get(Calendar.DAY_OF_MONTH) val datePickerDialog = DatePickerDialog( this, { _, selectedYear, selectedMonth, selectedDay -> val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") targetDate = LocalDate.of(selectedYear, selectedMonth + 1, selectedDay).atStartOfDay() updateCountdown() }, year, month, day ) datePickerDialog.show() } } ``` --- ### **步骤 5:运行和测试** 1. 连接你的 Android 设备或启动模拟器。 2. 点击 Android Studio 工具栏中的绿色运行按钮。 3. 在应用中点击“设置目标日期”按钮,选择个日期。 4. 查看倒计时是否正确显示。 --- ### **解释代码** 1. **布局文件**:定义了三个主要元素:目标事件名称、倒计时显示和设置目标日期的按钮。 2. **逻辑代码**: - 使用 `LocalDateTime` 和 `Duration` 类计算时间差。 - 根据剩余时间动态切换显示格式。 - 使用 `Handler` 每秒更新次倒计时。 - 提供了个日期选择器对话框,允许用户自由设置目标日期。 --- ### 没有还原流浪地球中倒计时的风格,步骤2不知为何出错
06-10
<think>我们正在开发个日程打卡APP,需要推荐技术框架和实现方法。 根据引用[1]和引用[2]的内容,我们可以借鉴些功能设计和实现思路。引用[1]提到日程管理APP开发在普通日程基础上增加多种实用功能,提供相互沟通、共享资源的平台。引用[2]则详细描述了个环保生活小助手APP的功能,包括智能识别、记录模块、活动日历、地图导航、积分体系等,这些功能设计思路可以借鉴到我们的日程打卡APP中。 我们的日程打卡APP核心功能可能包括: 1. 日程创建与管理:用户可以创建日程,设置提醒时间,重复周期等。 2. 打卡功能:用户完成日程后可以进行打卡,记录完成情况。 3. 数据统计:统计用户的打卡情况,生成报表(如连续打卡天数、完成率等)。 4. 社交分享:用户可以将打卡成就分享到社交平台,或者与好友互动。 5. 提醒功能:在设定的时间提醒用户完成日程。 此外,我们可以考虑增加些创新功能: - 成就系统:连续打卡获得积分或勋章,激励用户。 - 社区功能:用户可以加入兴趣小组,分享打卡心得。 - 数据分析:根据用户的打卡数据,提供时间管理建议。 技术框架推荐: 1. 移动端:采用跨平台框架,如Flutter或React Native,以便同时支持iOS和Android。 - Flutter:性能接近原生,丰富的UI组件,适合快速开发美观的界面。 - React Native:有庞大的社区支持,可以使用JavaScript开发。 2. 后端:可以选择Node.js(Express或NestJS)或Python(Django或Flask)等。 - 如果需要实时通信(如消息提醒),可以考虑使用WebSocket。 - 数据库:关系型数据库(如PostgreSQL)或非关系型数据库(如MongoDB)均可。考虑到日程数据有固定结构,关系型数据库可能更合适。 3. 云服务:可以使用阿里云、腾讯云或AWS等云服务提供商,部署后端服务,存储数据。 4. 推送服务:使用Firebase Cloud Messaging(FCM)和苹果推送通知服务(APNs)来实现推送提醒。 5. 地图服务(如果需要位置相关提醒):可以使用高德地图或百度地图的API。 6. 用户认证:使用OAuth2.0协议,可以集成第三方登录(微信、QQ、微博等)。 7. 数据同步:使用WebSocket或定期轮询实现多设备同步。 实现步骤建议: 1. 需求分析:明确APP的核心功能和扩展功能,绘制原型图。 2. 技术选型:根据团队熟悉的技术栈选择框架。 3. 数据库设计:设计用户、日程、打卡记录等表结构。 4. 后端开发:实现用户认证、日程管理、打卡记录、数据统计等API。 5. 前端开发:实现界面,调用后端API。 6. 测试:进行功能测试、性能测试、安全测试等。 7. 部署上线:将后端部署到云服务器,APP上架到应用商店。 注意事项: - 数据安全:用户数据加密存储,传输使用HTTPS。 - 用户体验:提醒功能要稳定可靠,避免错过提醒。 - 性能优化:打卡记录可能越来越多,需要考虑分页加载和数据库索引优化。 以上是个基本的开发框架,具体实现细节需要根据实际需求调整。 相关问题: 1. 日程打卡APP如何设计用户激励体系? 2. 如何实现多设备间的日程同步? 3. 日程提醒功能有哪些实现方案? 4. 如何设计个高效的打卡记录数据库? 5. 如何保证APP的推送提醒及时送达? 6. 在日程管理APP中,社交功能应该如何设计?</think>### 如何开发个日程打卡APP #### 、核心功能设计(参考引用[1][2]优化) 1. **基础日程管理** - 日程创建/编辑/删除(支持重复周期、提醒设置) - 可视化日历视图(日/周/月模式) - 智能提醒(推送通知+铃声震动)[^1] 2. **打卡激励体系** - 连续打卡日历(标记完成状态) - 成就徽章系统(如"7天坚持者") - 积分兑换商城(参考环保APP积分设计)[^2] 3. **社交化扩展** - 好友监督组队(共享打卡进度) - 成就分享(生成海报分享至社交平台) - 社区动态(用户分享打卡心得) 4. **数据分析** - 打卡热力图(可视化坚持情况) - 习惯养成报告(周/月完成率分析) - 个性化建议(基于失败记录优化提醒策略) --- #### 二、技术栈推荐 | 模块 | 推荐方案 | 优势说明 | |---------------|-----------------------------------|----------------------------| | **跨平台框架** | Flutter 3.x | 高性能渲染,iOS/Android双端致体验 | | **后端服务** | Node.js + Express + MongoDB | JSON数据天然适配移动端,灵活扩展 | | **推送服务** | Firebase Cloud Messaging (FCM) | 免费高抵达率,支持iOS/Android | | **数据库** | MongoDB Atlas(云托管) | 动态模式适配打卡记录变化 | | **存储服务** | AWS S3 / 阿里云OSS | 安全存储用户分享图片/海报 | | **实时同步** | Socket.IO / Firebase Realtime DB | 实现组队打卡实时进度同步 | --- #### 三、关键实现步骤 1. **数据模型设计** ```dart // Flutter 数据模型示例 class Habit { String id; String title; TimeOfDay reminderTime; List<bool> repeatDays; // [true,false,true,...] 周到周日 List<DateTime> completeDates; // 打卡日期记录 } ``` 2. **提醒功能实现** ```dart // Flutter 本地通知插件 await flutterLocalNotificationsPlugin.zonedSchedule( id, '打卡提醒', '${habit.title}时间到啦!', _nextInstanceOfTime(habit.reminderTime), NotificationDetails(...), androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime ); ``` 3. **打卡统计逻辑** $$ \text{连续打卡天数} = \max\{ n \mid \forall k \in [0,n-1], (today - k \ days) \in \text{completeDates} \} $$ 4. **性能优化建议** - 使用 `Isolate` 处理数据统计计算 - `ListView.builder` 懒加载历史记录 - Hive 本地缓存降低数据库查询压力 --- #### 四、安全与扩展 1. **安全措施** - JWT 身份验证 - 日程数据 AES-256 端到端加密 - 隐私合规:遵守 GDPR/《个人信息保护法》 2. **增值功能扩展** - AI日程规划(遗传算法优化时间安排) - 语音打卡(集成科大讯飞SDK) - 健康数据同步(接入Apple Health/Google Fit) --- #### 五、开发资源推荐 1. [Flutter官方文档](https://flutter.dev) 2. [Firebase控制台](https://console.firebase.google.com) 3. 设计素材:Material Design 3组件库 4. 测试工具:Flutter Driver集成测试
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值