Android

目前安卓开发有两种语言可以选择,java或者Kotlin,Kotlin比起Java来说更加简洁,且优化了不少写法
安卓开发的框架呢有Jetpack Compose,使用声明式开发,基本语法方面与SwiftUI大相径庭,但是只适用于Kotlin语言
使用xml去布局activity(页面),使用java去编写逻辑(相当于javascript)
要编写逻辑时,需要用id(XXXX xxxx=findViewById(R.id.xxxx))去找到该控件,实例化之后再去进行后续操作

简单控件

text

单位有px,dp(常用),sp(字体专用,会根据系统文字大小进行调整,一般情况下与dp大小一致)
一般是在@String里面(values包下面)去写文字,这样方便一次修改,以及后续调整语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?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:layout_margin="@dimen/dimen_20"
android:orientation="vertical">
<!--在Design中表示可从左侧控件展示处拖拽至布局文件上,创建简单一个TextView。-->
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<!--修改颜色、大小-->
<!--设置颜色 @color/color_ff0000位置:app/values/colors-->
<!--设置大小 @dimen/text_size_18位置:app/values/dimens-->
<!--设置内容 @string/str_setting_color_size位置:app/values/strings-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/str_setting_color_size"
android:layout_marginTop="@dimen/dimen_10"
android:textColor="@color/color_ff0000"
android:textSize="@dimen/text_size_20" />
<!--添加图片和使用阴影-->
<!--添加图片:drawableTop、drawableBottom、drawableLeft(drawableStart)、drawableRight(drawableEnd)-->
<!--使用阴影:shadowColor(阴影颜色)、shadowDx(tv_2位置为基准,数字越大越往右)、
shadowDy(tv_2位置为基准,数字越大越往下)、shadowRadius(数字越大越模糊)-->
<!--图片 @mipmap/ic_launcher 位置:app/mipmap/任意一个目录能找到即可-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@mipmap/ic_launcher"
android:layout_marginTop="@dimen/dimen_10"
android:gravity="center_vertical"
android:shadowColor="@color/color_FF773D"
android:shadowDx="30"
android:shadowDy="-20"
android:shadowRadius="2"
android:text="右侧添加图片和使用阴影"
android:textColor="@color/color_188FFF"
android:textSize="@dimen/text_size_20" />
<!--对电话和邮件增加链接-->
<!--autoLink对文本内容自动添加E-mail地址、电话号码添加超级链接-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="email|phone"
android:gravity="center_vertical"
android:layout_marginTop="@dimen/dimen_10"
android:text="可点击跳转邮件:SCC5201314@qq.com\n可点击跳转电话:0215201314"
android:textColor="@color/color_188FFF"
android:textSize="@dimen/text_size_14" />
<!--内容过多-->
<!--maxLength最多显示几行,单行也可用android:singleline="true"-->
<!--ellipsize,内容显示不下时,显示...(位置最前、中间、最后都可以),这里要加行数限制才行-->
<!--lineSpacingMultiplier,行距-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
android:lineSpacingMultiplier="1.2"
android:layout_marginTop="@dimen/dimen_10"
android:maxLength="2"
android:text="TxtView继承了View,它还是Button、EditText两个UI组件类的父类。它的作用是在用户界面上显示文本素。从功能上来看TextView就是个文本编辑器,只不过Android关闭的它的可编辑功能。如果需要一个可编辑的文本框,就要使用到它的子类Editext了,Editext允许用户编辑文本框中的内容。TextView和Editext它俩最大的区别就在于TextView不允许用户编辑文本内容,Editext允许用户编辑文本内容。
下面咱写几个实例来详细了解一下TextView的。"
android:textColor="@color/color_188FFF"
android:textSize="@dimen/text_size_14" />
<!--background设置背景色-->
<!--padding内边距(边到可用范围的距离)-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_ff0000"
android:layout_marginTop="@dimen/dimen_10"
android:padding="10dp"
android:text="背景色红色的文本"
android:textColor="@color/white" />

<!--带边框的文本-->
<!--layout_margin外边距(TextView到其他控件的距离)-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10"
android:background="@drawable/bg_tv_frame_red"
android:padding="10dp"
android:text="带着红色边框的文本" />
<!--带边框的文本背景色渐变-->
<!--代码可实现文本的渐变-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10"
android:background="@drawable/bg_tv_frame_gradient"
android:padding="10dp"
android:textColor="@color/white"
android:text="带着边框和背景色渐变的文本" />
</LinearLayout>

LinearLayout

线性布局

使用orientation属性控制排列方式,默认水平排列
水平:orientation:horizontal

垂直:orientation:vertical
使用layout_weight属性控制权重,相当于各自拥有多大比例

Layout_width为0dp时,layout_weight表示水平方向的宽度比例

Layout_height为0dp时,layout_weight表示垂直方向的宽度比例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="300dp"
android:background="@color/teal_200"
android:orientation="horizontal"
tools:context=".MainActivity">

<Button
android:id="@+id/button1"
android:layout_width="100dp"
android:layout_weight="3"
android:layout_height="wrap_content"
android:text="Button" />

<Button
android:id="@+id/button2"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />

</LinearLayout>

RelativeLayout

相对布局,感觉比较像css里的绝对定位

布局方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//左上
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"

//右上
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"//不设置基于右侧,会自动设置为基于左侧

//左下
android:layout_alignParentBottom="true" //不设置基于底部,会自动设置为基于顶部
android:layout_alignParentLeft="true"

//右下
android:layout_alignParentBottom="true" //不设置基于底部,会自动设置为基于顶部
android:layout_alignParentRight="true" //不设置基于右侧,会自动设置为基于左侧

android:layout_above="@id/button3" //居中控件顶部
android:layout_below="@id/button3" //居中控件底部
android:layout_toLeftOf="@id/button3" //居中控件左侧
android:layout_toRightOf="@id/button3" //居中控件右侧

GridLayout

设置几行几列

属性 说明
android:rowCount 设置网格布局有几行
android:columnCount 设置网格布局有几列

设置组件所在的行或列

注意: 行列从 0 开始计算,比如第一行是 0 ,第二行是 1

属性 说明
android:layout_row 设置组件位于第几行
android:layout_column 设置组件位于第几列

设置组件跨几行几列

属性 说明
android:layout_rowSpan 设置组件跨几行
android:layout_columnSpan 设置组件跨几列

ScrollView

ScrollView 是一个竖直滚动条,水平方向上的滚动条为 HorizontalScrollView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:orientation="vertical" >

<ScrollView
<!--注意ScrollView为竖直滚动所以layout_width必须match_parent
<!--同理HorizontalScrollView为水平滚动所以layout_height必须match_parent-->
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="500dp">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/chuliuxiang" />
</ScrollView>
</LinearLayout>

Button

事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MainActivity extends AppCompatActivity {

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

// 获取控件id
Button btn1=findViewById(R.id.btn1);

// 监听点击事件
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在这里写要执行的操作
}
});

// 长按事件
btn1.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// 在这里写要执行的操作
return false;
}
});

// 触摸事件
btn1.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 在这里写要执行的操作
return false;
}
});

}
}

ImageView

1
2
3
4
5
6
7
8
9
<!--src设置前景,而background设置背景,scaleType为缩放形式-->
<ImageView
android:id="@+id/iv"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:scaleType="fitCenter"
android:src="@drawable/developer"
android:background="@drawable/developer" />

include

相当于引入组件,优化了代码

1
<include layout="@layout/activity_drawer_menu" android:id="@+id/drawerMenu1"/>

DrawerLayout

安卓自带的抽屉式菜单

使用方法为分为两部分,一个是正文部分(必须放在最上面),一个是菜单部分,其中如果是左侧拉出的菜单就必须要加上android:layout_gravity=”start”,同理右侧拉出则改为end,注意下面的代码我进行了封装,而使用后发现只有NavigationView才能加上layout_gravity这个属性

xml部分的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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"
tools:context=".MainActivity"
android:id="@+id/drawerLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<!--正文部分-->
</LinearLayout>
<!--左侧菜单部分-->
<com.google.android.material.navigation.NavigationView
android:layout_width="210dp"
android:layout_height="match_parent"
android:layout_gravity="start">
<include
android:id="@+id/drawerMenu"
layout="@layout/activity_drawer_menu"/>
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>

java部分的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 获取activity_nav.xml中的根布局
View Nav = findViewById(R.id.Nav);

ImageView navMenuImageView = Nav.findViewById(R.id.navMenu);
navMenuImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在这里处理点击事件,打开抽屉
Log.v("drawerMenu","点击成功");
DrawerLayout drawerLayout = findViewById(R.id.drawerLayout);
drawerLayout.openDrawer(GravityCompat.START); // 打开左侧抽屉
}
});
}
}

这个视图相当于一个封装好的菜单,在menu属性部分存入主体菜单的布局,在headerLayout属性部分存入头部菜单的布局(比如头像姓名)

RecyclerView

这个控件是用来展示大量数据,它比ListView的优势在于能够回收利用数据(名字的由来)同时可以选择单列展示,双列展示,瀑布流展示

xml部分:

1
2
3
4
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

java部分:

TagActivity部分:

主要在于新增tagAdapter,以及数据tagList的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 这里省略网络请求方法performNetworkRequest()部分,在此方法中主要用于获取tagList的值
public class TagActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private TagAdapter tagAdapter;
private List<TagItem> tagList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tag);

// 初始化数据列表
tagList = new ArrayList<>(); // 初始化 tagList

// 初始化适配器并将其与RecyclerView关联
tagAdapter = new TagAdapter(tagList);
recyclerView.setAdapter(tagAdapter);

// 启动异步任务来执行网络请求
performNetworkRequest();
}
// 执行网络请求的方法
}
TagItem部分:

该部分就是提前把需要展示的数据封装为类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TagItem {
private String tagName;
private String tagDescription;

private boolean isDetailsVisible; // 新增的字段
// 构造函数和其他方法...
public boolean isDetailsVisible() {
return isDetailsVisible;
}
public void setDetailsVisible(boolean visible) {
isDetailsVisible = visible;
}
public TagItem(String tagName, String tagDescription) {
this.tagName = tagName;
this.tagDescription = tagDescription;
}
public String getTagName() {
return tagName;
}
public String getTagDescription() {
return tagDescription;
}
}
TagAdapter部分:

该部分的重写方法是内部封装好去重写的方法

首先最开始是去定义在TagAdapter中的全局变量

然后onCreateViewHolder()方法去新增view(注意要另外建立好)

接着是onBindViewHolder是绑定数据(也就是set各种属性)注意这里设定属性有可能会只设置到最后一个元素,原因一般有两种:1、没加notifyDataSetChanged()通知适配器数据已更改 2、试图在方法里去改变属性,因为方法最后只会绑定到最后一个view,应该在外围进行更改

还有getItemCount来确定一共生成多少view

数据来源则是ViewHolder,这里去find各种需要的元素id,然后在onBindViewHolder进行相应操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class TagAdapter extends RecyclerView.Adapter<TagAdapter.ViewHolder> {

private List<TagItem> tagItemList;
private int visibilityState1 = View.VISIBLE;
private int visibilityState2 = View.GONE;



public TagAdapter(List<TagItem> tagItemList) {
this.tagItemList = tagItemList;
// 初始化可见性状态
this.visibilityState1 = View.VISIBLE;
this.visibilityState2 = View.GONE;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
TagItem tagItem = tagItemList.get(position);
System.out.println(tagItem.getTagName());
holder.tagNameTextView.setText(tagItem.getTagName());
holder.tagDescriptionTextView.setText(tagItem.getTagDescription());



holder.navDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 切换可见性状态
visibilityState1 = (visibilityState1 == View.GONE) ? View.VISIBLE : View.GONE;
visibilityState2 = (visibilityState2 == View.GONE) ? View.VISIBLE : View.GONE;

// 通知适配器数据已更改,以便刷新所有itemView
notifyDataSetChanged();
}
});

// 设置detailEdit的可见性状态
holder.detailEdit.setVisibility(visibilityState1);
// 设置detailDelete的可见性状态
holder.detailDelete.setVisibility(visibilityState2);

// 添加渐变动画效果
if (visibilityState1 == View.VISIBLE) {
animateView(holder.detailEdit, true);
} else {
animateView(holder.detailEdit, false);
}

if (visibilityState2 == View.VISIBLE) {
animateView(holder.detailDelete, true);
} else {
animateView(holder.detailDelete, false);
}

}

// 辅助方法来执行渐变动画
private void animateView(View view, boolean show) {
float startAlpha = show ? 0f : 1f;
float endAlpha = show ? 1f : 0f;

ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
alphaAnimator.setDuration(300); // 设置动画持续时间

alphaAnimator.start();
}



@Override
public int getItemCount() {
return tagItemList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView tagNameTextView;
TextView tagDescriptionTextView;

// 获取activity_nav.xml中的根布局
View Nav;
ImageView navDelete;

CardView detailEdit;
CardView detailDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tagNameTextView = itemView.findViewById(R.id.tagName);
tagDescriptionTextView = itemView.findViewById(R.id.tagDescription);

Nav= ((Activity)itemView.getContext()).findViewById(R.id.Nav);
navDelete = Nav.findViewById(R.id.navDelete);
detailEdit = itemView.findViewById(R.id.detailEdit);
detailDelete = itemView.findViewById(R.id.detailDelete);
}
}
}

ConstraintLayout

相当于融合了LinearLayout和RelativeLayout

下表列出了可用约束的属性列表

属性
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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"
tools:context=".TagActivity"
android:id="@+id/drawerLayout">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<include android:id="@+id/Nav" layout="@layout/activity_nav"/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/Nav"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="70dp"
android:layout_height="70dp"
app:cardCornerRadius="40dp"
app:cardElevation="0dp"
android:layout_marginTop="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1.0">

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="+"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:textColor="@color/white"
android:background="@color/zi" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/drawerMenu"
android:layout_width="210dp"
android:layout_height="match_parent"
android:layout_gravity="start">
<include
layout="@layout/activity_drawer_menu"/>
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>

EditView

用于文字的输入

hint用于提示文字,maxLength为最长允许输入长度,

background=”@null”很关键,可以去除掉hint的下划线

1
2
3
4
5
6
7
8
9
10
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入名称"
android:maxLength="6"
android:textStyle="bold"
android:textColor="@color/balck"
android:hitColor="@color/hui"
android:textSize="15sp"
android:background="@null"/>

在java中获取文字

注意赋值给String类型的变量是要toString()

1
userEmail=userEmailEditText.getText().toString();

基本属性

视图宽高

match_parent:与父级视图保持一致

wrap_content:表示与内容自适应

以dp为单位的具体尺寸

间距

外边距:layout_margin

内边距:padding

对齐方式

指定当前视图相对于上级视图的对齐方式:layout_gravity

指定下级视图相对于当前视图的对齐方式:gravity

固定左上角:left|top

排列方式

使用orientation属性控制排列方式,默认水平排列
水平:orientation:horizontal

垂直:orientation:vertical

权重

使用layout_weight属性控制权重,相当于各自拥有多大比例

Layout_width为0dp时,layout_weight表示水平方向的宽度比例

Layout_height为0dp时,layout_weight表示垂直方向的宽度比例

对可见性的处理

使用Visibility来到达使用同一个xml达成不同布局的效果,能够优化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 获取activity_drawer_menu.xml中的根布局
View drawerMenu1 = findViewById(R.id.drawerMenu1);

// 获取drawerMenuPoint和drawerMenuAvatar控件
CardView drawerMenuPoint = drawerMenu1.findViewById(R.id.drawerMenuPoint);
CardView drawerMenuAvatar = drawerMenu1.findViewById(R.id.drawerMenuAvatar);

// 设置它们的visibility为GONE
drawerMenuPoint.setVisibility(View.GONE);
drawerMenuAvatar.setVisibility(View.GONE);
}
}

基础操作

调试

在Logcat里查看调试信息

Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v(“”,””);
Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择.
Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息
Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。
Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。

1
Log.v("你好","点击成功");

实践效果

导航跳转页面

下面封装了代码,关键代码在自定义方法startActivity中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//DrawerMenuHelper.java
public class DrawerMenuHelper {
private AppCompatActivity activity;

public DrawerMenuHelper(AppCompatActivity activity) {
this.activity = activity;
}

public void setupDrawerMenu(int num) {
//先找到按钮
LinearLayout drawerMenuTo1=navigationView.findViewById(drawerMenuToBIds[0]);

drawerMenuTo1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(MainActivity.class);
Log.v("drawerMenu","点击抽屉的第一个按钮");
}
});
}
//关键代码
private void startActivity(Class<?> cls) {
Intent intent = new Intent(activity, cls);
activity.startActivity(intent);
activity.finish(); // 关闭当前活动
}
}
//MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

DrawerMenuHelper drawerMenuHelper = new DrawerMenuHelper(this);
drawerMenuHelper.setupDrawerMenu(0);
}
}

网络请求

使用okhttp3框架

首先要在build.gradle进行引入implementation(“com.squareup.okhttp3:okhttp:4.9.1”)

其次是在AndroidManifest.xml加入权限

1
2
3
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

然后编写请求的逻辑这里要注意数据的格式、请求的方法、返回数据的格式(具体去查看swiftui笔记里的网络请求部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void performNetworkRequest() {
// 创建 OkHttpClient 实例
OkHttpClient client = new OkHttpClient();

// 请求URL
String url = "https://tengenchang.top/tag/get";

// 请求数据
String requestData = "3489044730@qq.com";

// 设置请求体
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(JSON, requestData);

// 创建 POST 请求
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();

// 下面使用 'request' 对象来执行网络请求,我会在下方再补充代码
// 例如,使用 'client' 执行请求:client.newCall(request).enqueue(callback);
}

接下来首先要把网络请求放在子线程里,本来是使用写一个类继承AsyncTask,然后在主线程中使用execute()方法来调用子线程,但是execute()方法,以及在主线程更新ui的onPostExecute()方法已经在API30中被弃用(卡了我一整节习概QAQ)解决方法就是使用okhttp3的onResponse()方法内的runOnUiThread()方法去在更新ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private void performNetworkRequest() {
// 上面为配置 'request' 对象的各种细节来执行网络请求,我已经在笔记上方给出代码
// 使用OkHttp3执行异步网络请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
// 获取响应数据
String responseBody = response.body().string();
try {
// 解析JSON响应
JSONObject jsonResponse = new JSONObject(responseBody);
JSONArray data = jsonResponse.getJSONArray("data");

// 清空已有数据
tagList.clear();

// 遍历JSON数据并添加到tagList中
for (int i = 0; i < data.length(); i++) {
JSONObject item = data.getJSONObject(i);
String tagName = item.getString("tagName");
String tagDescribe = item.getString("tagDescribe");

// 创建TagItem对象并添加到tagList中
tagList.add(new TagItem(tagName, tagDescribe));
}

// 更新 UI,确保在主线程中执行
runOnUiThread(new Runnable() {
@Override
public void run() {
// 通知适配器数据已更新
tagAdapter.notifyDataSetChanged();
}
});

} catch (JSONException e) {
e.printStackTrace();
}
} else {
// 请求失败,输出错误信息
Log.e("TagActivity", "请求失败,状态码: " + response.code());
Log.e("TagActivity", response.body().string());
}
}

@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
}

RelativeLayout内的适配器只能修改单一View的问题

原因一般有两种:1、没加notifyDataSetChanged()通知适配器数据已更改 2、试图在方法里去改变属性,因为方法最后只会绑定到最后一个view,应该在外围进行更改(具体源代码可以去看简单空间部分的RelativeLayout介绍,这个问题也卡了挺久QAQ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class TagAdapter extends RecyclerView.Adapter<TagAdapter.ViewHolder> {

private List<TagItem> tagItemList;
private int visibilityState1 = View.VISIBLE;
private int visibilityState2 = View.GONE;



public TagAdapter(List<TagItem> tagItemList) {
this.tagItemList = tagItemList;
// 初始化可见性状态
this.visibilityState1 = View.VISIBLE;
this.visibilityState2 = View.GONE;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
TagItem tagItem = tagItemList.get(position);
System.out.println(tagItem.getTagName());
holder.tagNameTextView.setText(tagItem.getTagName());
holder.tagDescriptionTextView.setText(tagItem.getTagDescription());



holder.navDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 切换可见性状态
visibilityState1 = (visibilityState1 == View.GONE) ? View.VISIBLE : View.GONE;
visibilityState2 = (visibilityState2 == View.GONE) ? View.VISIBLE : View.GONE;

// 通知适配器数据已更改,以便刷新所有itemView
notifyDataSetChanged();
}
});

// 设置detailEdit的可见性状态
holder.detailEdit.setVisibility(visibilityState1);
// 设置detailDelete的可见性状态
holder.detailDelete.setVisibility(visibilityState2);

// 添加渐变动画效果
if (visibilityState1 == View.VISIBLE) {
animateView(holder.detailEdit, true);
} else {
animateView(holder.detailEdit, false);
}

if (visibilityState2 == View.VISIBLE) {
animateView(holder.detailDelete, true);
} else {
animateView(holder.detailDelete, false);
}

}

// 辅助方法来执行渐变动画
private void animateView(View view, boolean show) {
float startAlpha = show ? 0f : 1f;
float endAlpha = show ? 1f : 0f;

ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
alphaAnimator.setDuration(300); // 设置动画持续时间

alphaAnimator.start();
}



@Override
public int getItemCount() {
return tagItemList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView tagNameTextView;
TextView tagDescriptionTextView;

// 获取activity_nav.xml中的根布局
View Nav;
ImageView navDelete;

CardView detailEdit;
CardView detailDelete;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tagNameTextView = itemView.findViewById(R.id.tagName);
tagDescriptionTextView = itemView.findViewById(R.id.tagDescription);

Nav= ((Activity)itemView.getContext()).findViewById(R.id.Nav);
navDelete = Nav.findViewById(R.id.navDelete);
detailEdit = itemView.findViewById(R.id.detailEdit);
detailDelete = itemView.findViewById(R.id.detailDelete);
}
}
}

删除指定页面

最大的问题在于如何删除,以及如何通知主线程去更改ui,以及使用getcontext来找到父activity

注意一下getcontext的用法

View类中提供的方法,在继承了View的类中才可以调用。

返回的是当前View运行在哪个Activity Contex中,获取当前context的实例。

如果使用场景是Activity则相当于 this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
private void deleteTarget(TargetItem targetItem, int position, Context context) {
// 创建 OkHttpClient 实例
OkHttpClient client = new OkHttpClient();

// 请求URL
String url = "https://tengenchang.top/target/delete";
System.out.println("targetId:" + targetItem.getTargetId());

// 创建 JSON 对象
JSONObject requestData = new JSONObject();
try {
requestData.put("userEmail", "3489044730@qq.com");
requestData.put("targetName", targetItem.getTargetName());
requestData.put("ifPoints", 0);
requestData.put("targetId", Long.valueOf(targetItem.getTargetId()));
} catch (JSONException e) {
e.printStackTrace();
}

// 设置请求体
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(JSON, requestData.toString());

// 创建 POST 请求
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();

// 使用 OkHttp3 执行异步网络请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
// 从数据源中删除项
targetItemList.remove(position);

// 通知适配器删除了特定位置的项
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
notifyItemRemoved(position);
}
});

// 通知适配器更新从删除位置到列表末尾的所有项
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
notifyItemRangeChanged(position, targetItemList.size());
}
});
} else {
// 请求失败,输出错误信息
Log.e("TargetActivity", "请求失败,状态码: " + response.code());
Log.e("TargetActivity", response.body().string());
}
}

@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
}

edit和delete转化

第一种情况

页面里只有一个RelativeLayout,无论在adapter里实现点击事件还是在activity中实现点击事件都可以,详细内容可见RelativeLayout内的适配器只能修改单一View的问题

第二种情况(又卡住QAQ)

页面中有两个RelativeLayout,那就不能在adapter中实现事件,因为最后只会实现最后一个点击事件,这时就需要在activity中实现

TargetActivity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class TargetActivity extends AppCompatActivity {
private RecyclerView targetWithTimeRecyclerView;
private RecyclerView targetNoTimeRecyclerView;
private TargetWithTimeAdapter targetWithTimeAdapter;
private TargetNoTimeAdapter targetNoTimeAdapter;
private List<TargetItem> targetWithTimeList;
private List<TargetItem> targetNoTimeList;

private int targetVisibilityState1 = View.VISIBLE;
private int targetVisibilityState2 = View.GONE;


private View Nav;
ImageView navDelete;


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

Nav = findViewById(R.id.Nav);
navDelete = Nav.findViewById(R.id.navDelete);

navDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 切换可见性状态
targetVisibilityState1 = (targetVisibilityState1 == View.GONE) ? View.VISIBLE : View.GONE;
targetVisibilityState2 = (targetVisibilityState2 == View.GONE) ? View.VISIBLE : View.GONE;

// 更新两个适配器的可见性状态
targetNoTimeAdapter.updateVisibility(targetVisibilityState1, targetVisibilityState2);
targetWithTimeAdapter.updateVisibility(targetVisibilityState1, targetVisibilityState2);
}
});
// 初始化RecyclerView
targetWithTimeRecyclerView = findViewById(R.id.targetWithTimeRecyclerView);
targetNoTimeRecyclerView = findViewById(R.id.targetNoTimeRecyclerView);
targetWithTimeRecyclerView.setLayoutManager(new LinearLayoutManager(this));
targetNoTimeRecyclerView.setLayoutManager(new LinearLayoutManager(this));

// 初始化数据列表
targetWithTimeList = new ArrayList<>(); // 初始化 targetWithTimeList
targetNoTimeList = new ArrayList<>(); // 初始化 targetNoTimeList

// 初始化适配器并将其与RecyclerView关联
targetWithTimeAdapter = new TargetWithTimeAdapter(targetWithTimeList,targetVisibilityState1, targetVisibilityState2);
targetNoTimeAdapter = new TargetNoTimeAdapter(targetNoTimeList,targetVisibilityState1, targetVisibilityState2);

targetWithTimeRecyclerView.setAdapter(targetWithTimeAdapter);
targetNoTimeRecyclerView.setAdapter(targetNoTimeAdapter);

}
}
targetNoTimeAdapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class TargetNoTimeAdapter extends RecyclerView.Adapter<TargetNoTimeAdapter.ViewHolder>{
private List<TargetItem> targetItemList;
private int targetNoTimeVisibilityState1 = View.VISIBLE;
private int targetNoTimeVisibilityState2= View.GONE;

public TargetNoTimeAdapter(List<TargetItem> targetItemList, int visibilityState1, int visibilityState2) {
this.targetItemList = targetItemList;
// 初始化可见性状态
this.targetNoTimeVisibilityState1 = visibilityState1;
this.targetNoTimeVisibilityState2 = visibilityState2;
}

@NonNull
@Override
public TargetNoTimeAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_target_notime, parent, false);
return new TargetNoTimeAdapter.ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull TargetNoTimeAdapter.ViewHolder holder, int position) {
TargetItem targetItem = targetItemList.get(position);
System.out.println(targetItem.getTargetName());
holder.targetNameTextView.setText(targetItem.getTargetName());
holder.targetDescriptionTextView.setText(targetItem.getTargetDescribe());
holder.targetNoTimePointTextView.setText("X"+targetItem.getTargetPoint());


// 设置detailEdit的可见性状态
holder.targetNoTimePointCardView.setVisibility(targetNoTimeVisibilityState1);
holder.targetNoTimeDayDifference.setVisibility(targetNoTimeVisibilityState1);
// 设置detailDelete的可见性状态
holder.targetNoTimeDelete.setVisibility(targetNoTimeVisibilityState2);
}
public void updateVisibility(int visibilityState1, int visibilityState2) {
targetNoTimeVisibilityState1 = visibilityState1;
targetNoTimeVisibilityState2 = visibilityState2;
notifyDataSetChanged();
}

@Override
public int getItemCount() {
return targetItemList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView targetNameTextView;
TextView targetDescriptionTextView;
TextView targetNoTimePointTextView;

// 获取activity_nav.xml中的根布局
View Nav;
ImageView navDelete;

CardView targetNoTimePointCardView;
CardView targetNoTimeDelete;
TextView targetNoTimeDayDifference;

public ViewHolder(@NonNull View itemView) {
super(itemView);
targetNameTextView = itemView.findViewById(R.id.targetNoTimeName);
targetDescriptionTextView = itemView.findViewById(R.id.targetNoTimeDescribe);
targetNoTimePointTextView = itemView.findViewById(R.id.targetNoTimePoint);

Nav = ((Activity) itemView.getContext()).findViewById(R.id.Nav);
navDelete = Nav.findViewById(R.id.navDelete);
targetNoTimePointCardView = itemView.findViewById(R.id.targetNoTimePointCardView);
targetNoTimeDelete = itemView.findViewById(R.id.targetNoTimeDelete);
targetNoTimeDayDifference = itemView.findViewById(R.id.targetNoTimeDayDifference);
}
}
}

日期创建

这块涉及的比较多,有时间再仔细看一下

知识点不只是日期的相应处理,还有使用java去构建布局,这一点我都使用的都比较简单,但是下面的代码是比较复杂布局构建,但是其实复杂布局就是去记住更多的布局对象的相应属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
private View createDateItemView(final int index, final List<TargetItem> targetWithTimeList) {
// 获取当前日期
Date currentDate = calendar.getTime();
// 将日期向前或向后移动 index 天
calendar.add(Calendar.DAY_OF_MONTH, index);
// 获取新的日期
Date date = calendar.getTime();
// 恢复 calendar 到当前日期
calendar.setTime(currentDate);

// 创建日期的格式化工具,用于显示星期几和日期天数
SimpleDateFormat dayOfWeekFormat = new SimpleDateFormat("E");
SimpleDateFormat dayOfMonthFormat = new SimpleDateFormat("d");

// 创建日期项的布局参数,设置边距
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
layoutParams.setMargins(18, 0, 18, 0);

// 创建日期项的外层布局
RelativeLayout dateItemLayout = new RelativeLayout(this);
dateItemLayout.setLayoutParams(layoutParams);

// 创建显示星期几的文本视图
TextView dayOfWeekTextView = new TextView(this);
dayOfWeekTextView.setText(dayOfWeekFormat.format(date));
dayOfWeekTextView.setId(View.generateViewId());
dayOfWeekTextView.setTextColor(Color.GRAY);
dayOfWeekTextView.setTextSize(12);

// 设置星期几文本视图的布局参数,水平居中显示
RelativeLayout.LayoutParams dayOfWeekParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
);
dayOfWeekParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
dayOfWeekTextView.setLayoutParams(dayOfWeekParams);

// 创建显示日期天数的文本视图
TextView dayOfMonthTextView = new TextView(this);
dayOfMonthTextView.setText(dayOfMonthFormat.format(date));
dayOfMonthTextView.setWidth(getResources().getDimensionPixelSize(R.dimen.date_item_width));
dayOfMonthTextView.setHeight(getResources().getDimensionPixelSize(R.dimen.date_item_height));
dayOfMonthTextView.setGravity(Gravity.CENTER);

// 检查是否为当前日期,设置初始状态
if (index == 0) {
dayOfMonthTextView.setTextColor(Color.parseColor("#CFC8FF"));
dayOfMonthTextView.setBackgroundResource(R.drawable.selected_date_background);
lastSelectedDayTextView = dayOfMonthTextView;
} else {
dayOfMonthTextView.setTextColor(Color.parseColor("#CFC8FF"));
dayOfMonthTextView.setBackgroundResource(R.drawable.unselected_date_background);
}

// 设置日期天数文本视图的布局参数,水平居中显示,位于星期几文本视图下方
RelativeLayout.LayoutParams dayOfMonthParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
);
dayOfMonthParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
dayOfMonthParams.addRule(RelativeLayout.BELOW, dayOfWeekTextView.getId());
dayOfMonthTextView.setLayoutParams(dayOfMonthParams);

// 允许日期天数文本视图响应点击事件
dayOfMonthTextView.setClickable(true);

// 设置日期天数文本视图的点击事件监听器
// 在日期项点击事件的回调中添加以下代码
dayOfMonthTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (lastSelectedDayTextView != null) {
// 取消上一个选中日期的背景和文本颜色
lastSelectedDayTextView.setBackgroundResource(R.drawable.unselected_date_background);
lastSelectedDayTextView.setTextColor(Color.parseColor("#CFC8FF"));
}

// 设置当前选中日期的背景和文本颜色
dayOfMonthTextView.setBackgroundResource(R.drawable.selected_date_background);
dayOfMonthTextView.setTextColor(Color.parseColor("#CFC8FF"));

// 更新 lastSelectedDayTextView 为当前选中的日期
lastSelectedDayTextView = dayOfMonthTextView;
System.out.println("lastSelectedDayTextView:" + lastSelectedDayTextView);

// 获取选中日期的字符串形式,例如 "2023-07-19"
String selectedDate = new SimpleDateFormat("yyyy-MM-dd").format(date);

// 遍历目标列表
for (int i = 0; i < targetWithTimeList.size(); i++) {
TargetItem targetItem = targetWithTimeList.get(i);
String deadline = targetItem.getDeadline(); // 获取deadline,例如 "2023-07-19T19:14:00"

// 解析deadline中的日期部分和时间部分
String[] deadlineParts = deadline.split("T");
String deadlineDate = deadlineParts[0]; // 日期部分,例如 "2023-07-19"
String deadlineTime = deadlineParts[1].substring(0, 5); // 时间部分,例如 "19:14"

// 计算日期差
long dayDifference = calculateDayDifference(selectedDate, deadlineDate);

// 根据日期差的规则设置不同的值
if (dayDifference > 0) {
// 相差日期大于0,获得相差的天数
targetItem.setDayDifference(dayDifference + "天");
} else if (dayDifference == 0) {
// 相差日期等于0,获得deadline的小时以及分钟
targetItem.setDayDifference(deadlineTime);
} else {
// 相差日期小于0,获得deadline的月份和日子
targetItem.setDayDifference(deadlineDate.substring(5));
}
System.out.println("deadlineTime:" + targetItem.getDayDifference());
// 通知适配器数据已更改
targetWithTimeAdapter.notifyDataSetChanged();
}
}
});

// 将星期几和日期天数文本视图添加到日期项的布局中
dateItemLayout.addView(dayOfWeekTextView);
dateItemLayout.addView(dayOfMonthTextView);

// 返回日期项的布局
return dateItemLayout;
}

RecyclerView适配器根据条件转成不同的页面

这里的实现效果,我之前一直是采用多个RecyclerView来达成条件转成的效果,然后可以直接根据一定的条件进行不同的页面,不过下面的例子只实现了list为空时的转化

Adapter部分

注意getItemViewType判断list的size

onCreateViewHolder的if语句来转化不同页面

onBindViewHolder的if语句进行不同处理

getItemCount也是去判断list的size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class TagAdapter extends RecyclerView.Adapter<TagAdapter.ViewHolder> {
public TagAdapter(List<TagItem> tagItemList) {
this.tagItemList = tagItemList;
}

@Override
public int getItemViewType(int position) {
if (tagItemList.size() == 0) {
return 1; // 返回1表示空页面
} else {
return 0; // 返回0表示正常数据项
}
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag, parent, false);
return new TagAdapter.ViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_null, parent, false);
return new TagAdapter.ViewHolder(view);
}
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (tagItemList.size() == 0) {
// 当数据为空时,可以设置空页面的提示信息或样式

// 可以设置其他空页面的样式或操作
} else {

}
}
@Override
public int getItemCount() {
if (tagItemList.size() == 0) {
return 1; // 返回1项以显示空页面
} else {
return tagItemList.size();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}

轮播图实现

使用ViewPager2来实现轮播图,ViewPager2实际上也是RecyclerView

使用handler.postDelayed(runnable, 1800);开始轮播任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class LoginNavActivity extends AppCompatActivity {
private TextView userAgreement;
private TextView privacyPolicy;

private ViewPager2 viewPager;
private List<CarouselItem> carouselItems = new ArrayList<>();
private int currentItem = 0; // 当前轮播图项的索引
private Handler handler = new Handler();
private Runnable runnable;

private LinearLayout loginNavLoginLinearLayout;

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

userAgreement=findViewById(R.id.userAgreement);
privacyPolicy=findViewById(R.id.privacyPolicy);

userAgreement.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(UserAgreementActivity.class);
}
});
privacyPolicy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(PrivacyPolicyActivity.class);
}
});

viewPager = findViewById(R.id.viewPager);

// 添加轮播图项数据
carouselItems.add(new CarouselItem(R.drawable.loginnavlogo1, "兑换\n商店积分"));
carouselItems.add(new CarouselItem(R.drawable.loginnavlogo2, "发现\n自我进步"));
carouselItems.add(new CarouselItem(R.drawable.loginnavlogo3, "建立\n计时标签"));
carouselItems.add(new CarouselItem(R.drawable.loginnavlogo4, "建立\n你的目标"));

// 创建适配器并设置到ViewPager2
CarouselAdapter carouselAdapter = new CarouselAdapter(carouselItems);
viewPager.setAdapter(carouselAdapter);

// 创建轮播任务
runnable = new Runnable() {
@Override
public void run() {
if (currentItem == carouselItems.size() - 1) {
currentItem = 0;
} else {
currentItem++;
}
viewPager.setCurrentItem(currentItem);
handler.postDelayed(this, 1800); // 1.8秒后再次执行
}
};

// 开始轮播任务
handler.postDelayed(runnable, 1800);

loginNavLoginLinearLayout=findViewById(R.id.loginNavLogin);
loginNavLoginLinearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//监听按钮,如果点击,就跳转
startActivity(HomeActivity.class);
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
// 在Activity销毁时停止轮播任务,防止内存泄漏
handler.removeCallbacks(runnable);
}
private void startActivity(Class<?> cls) {
Intent intent = new Intent(this, cls);
this.startActivity(intent);
this.finish(); // 关闭当前活动
}
}

跳转时传递信息

通过putExtra转递

1
2
3
4
5
6
7
private void startActivity(Class<?> cls) {
Intent intent = new Intent(this, cls);
//为跳转放入信息
intent.putExtra("sourceActivity", "Tag");
startActivity(intent);
finish(); // 关闭当前活动
}

获取信息

1
2
3
4
5
6
7
8
9
10
// 获取传递的标志
String sourceActivity = getIntent().getStringExtra("sourceActivity");
// 根据不同的标志设置不同的标题
if ("Tag".equals(sourceActivity)) {
createNavTitle.setText("建立标签");
} else if ("Store".equals(sourceActivity)) {
createNavTitle.setText("建立商品");
}else if ("Target".equals(sourceActivity)) {
createNavTitle.setText("建立目标");
}

计时页面实现

弹窗实现

先创建AlertDialog对象

再通过setTitle以及setMessage以及setPositiveButton以及setNegativeButton来设置相应的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void cancelTimer() {
if (countDownTimer != null) {
// 创建一个 AlertDialog.Builder 对象
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置弹窗标题
builder.setTitle("确定要放弃吗?");
// 设置弹窗正文
builder.setMessage("本次计时将不会得到任何分数");
// 设置确定按钮,并指定点击事件
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户点击了确定按钮,执行放弃计时的操作
countDownTimer.cancel();
timerTextView.setText(timer);
isTimerRunning = false;
timeButtonTextView.setText("开始");
}
});
// 设置取消按钮,并指定点击事件
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户点击了取消按钮,不执行任何操作,只关闭弹窗
dialog.dismiss();
}
});
// 创建并显示 AlertDialog
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}

提示框实现

注意这里的context可以取消

1
Toast.makeText(context.getApplicationContext(), "完成目标", Toast.LENGTH_SHORT).show();