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"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="TextView" /> <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" />
<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" /> <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" /> <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" /> <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" /> <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 是一个竖直滚动条,水平方向上的滚动条为 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>
|
事件
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);
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
| <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);
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); } }); } }
|
NavigationView
这个视图相当于一个封装好的菜单,在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
| 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<>();
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;
notifyDataSetChanged(); } });
holder.detailEdit.setVisibility(visibilityState1); 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;
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);
View drawerMenu1 = findViewById(R.id.drawerMenu1);
CardView drawerMenuPoint = drawerMenu1.findViewById(R.id.drawerMenuPoint); CardView drawerMenuAvatar = drawerMenu1.findViewById(R.id.drawerMenuAvatar);
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错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。
实践效果
导航跳转页面
下面封装了代码,关键代码在自定义方法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
| 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(); } }
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 client = new OkHttpClient(); 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); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); }
|
接下来首先要把网络请求放在子线程里,本来是使用写一个类继承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() { 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 { JSONObject jsonResponse = new JSONObject(responseBody); JSONArray data = jsonResponse.getJSONArray("data");
tagList.clear();
for (int i = 0; i < data.length(); i++) { JSONObject item = data.getJSONObject(i); String tagName = item.getString("tagName"); String tagDescribe = item.getString("tagDescribe");
tagList.add(new TagItem(tagName, tagDescribe)); }
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;
notifyDataSetChanged(); } });
holder.detailEdit.setVisibility(visibilityState1); 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;
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 client = new OkHttpClient(); String url = "https://tengenchang.top/target/delete"; System.out.println("targetId:" + targetItem.getTargetId()); 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()); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); 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); } }); targetWithTimeRecyclerView = findViewById(R.id.targetWithTimeRecyclerView); targetNoTimeRecyclerView = findViewById(R.id.targetNoTimeRecyclerView); targetWithTimeRecyclerView.setLayoutManager(new LinearLayoutManager(this)); targetNoTimeRecyclerView.setLayoutManager(new LinearLayoutManager(this));
targetWithTimeList = new ArrayList<>(); targetNoTimeList = new ArrayList<>();
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());
holder.targetNoTimePointCardView.setVisibility(targetNoTimeVisibilityState1); holder.targetNoTimeDayDifference.setVisibility(targetNoTimeVisibilityState1); 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;
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(); calendar.add(Calendar.DAY_OF_MONTH, index); Date date = calendar.getTime(); 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 = dayOfMonthTextView; System.out.println("lastSelectedDayTextView:" + lastSelectedDayTextView);
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();
String[] deadlineParts = deadline.split("T"); String deadlineDate = deadlineParts[0]; String deadlineTime = deadlineParts[1].substring(0, 5);
long dayDifference = calculateDayDifference(selectedDate, deadlineDate);
if (dayDifference > 0) { targetItem.setDayDifference(dayDifference + "天"); } else if (dayDifference == 0) { targetItem.setDayDifference(deadlineTime); } else { 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; } else { return 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; } 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你的目标"));
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); } };
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(); 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 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 = builder.create(); alertDialog.show(); } }
|
提示框实现
注意这里的context可以取消
1
| Toast.makeText(context.getApplicationContext(), "完成目标", Toast.LENGTH_SHORT).show();
|