第1章 Android Studio运行第一个程序
1.1 Android Studio下载:
1.1.1 Android开发者官网:
https://developer.android.google.cn
1.1.2 下载Android Studio开发者工具:
- 进入Android开发者官网;
- 找到Android Studio工具下载页面;
- 点击“Download option”按钮选择与自己系统对应的版本(本人使用的是Windows版本);
- 同意“条款和条件”并点击下载;
1.2 Android Studio安装:
- 双击下载好的Android Studio工具;
- 直接点击“Next”到安装处点击“Install”安装;(中途有:欢迎安装界面-->选择上虚拟设备-->安装路径)
- 等待安装完成后点“Next”如下图所示表示安装完成;
- 运行Android Studio;(由于是第一次安装,没有配置文件,直接点击“OK”)
- 点击“Cancel”;
- 会弹出欢迎界面,提示会提醒是否将使用情况统计信息发送到“Google”;(单纯让谷歌知道多了一位Android开发者)
- 来到欢迎配置Android Studio开发环境,点击”Next“直接到License Agreement[许可协议];(【Install Type:安装类型】[Standard:标准]-->【Select Ul Theme:选择UI主题】-->【Verify Settings:验证配置】)
- 阅读并同意安装组件的许可协议,点击“Finish”;([License Agreement:许可协议]、[Accept:接受])
- 下载完成后,点击“Finish”;
1.3 Android Studio卸载:
- 卸载Android Studio软件;(默认安装路径:C:Program FilesAndroidAndroid Studio)
- 删除SDK文件夹;(默认路径:C:用户Administrator[用户名]AppDataLocalAndroidsdk)
- 删除相关文件夹;(默认路径:C:用户Administrator[用户名])
删除.android 文件夹
删除 .AndroidStudioX.X 文件夹
删除.gradle 文件夹
具体详见:https://blog.csdn.net/weixin_45048331/article/details/111868109
1.4 第一个Android应用程序:
1.4.1 创建Android工程项目:
- 将Android Studio工具的快捷启动放到桌面;
- 点击“New Project”;
- 选择Phone and Table-->选择“Empty Activity”-->点击“Next”;
- 【Name:app的名字】-->【Package name:包名】-->【Save location:保存路径】-->【Language:语言】-->【点击“Finish”】;
- 然后等待下载完成Gradle配置工具;(某些用户可能会弹出防火墙的“安全中心警告”,点击“允许访问”)
- 打开“activity_main.xml”文件,视图效果和模拟器上运行的效果差不多;
1.4.2 运行第一个Android应用程序:
- 添加手机模拟器
- 点击下图右边第4个像手机一样的图标;
- 点击“Create virtual device”添加手机模拟器;
- 选上自己喜欢的设备,然后点击“Next”;(可点击“New Hardware Profile”自定义模拟设备)
- 点击“Download”下载模拟设备的系统镜像;
- 选择已下载的模拟设备系统镜像,点击“Next”;
- 给模拟设备命名,点击“”Finish;
- 点击“播放按钮”给模拟设备开机;
- 手机仿真器视图显示设置;
- 点击运行按钮后的运行结果;
- 编码方式与视图设计方式切换在编辑框的右上角,仿真器隐藏后在编辑框的右边栏最下方(【Code、Split、Design】、【Emulator:仿真器】);
1.4.3 仿真程序进程终止问题
- 问题描述:Android Studio运行自带模拟器报“The emulator process for AVD Pixel_4a_Edited_API_28 has terminated.”
- 原因分析:虚拟机安装路径存在中文符号导致乱码。因为Android Studio会默认安装虚拟机在用户目录下,如果你的电脑用户名带中文,比如C:Users 张三,因为当前用户路径有中文符号,导致一些配置文件中的路径会乱码,这时虚拟机就会运行失败。
- 解决方法:解决Android Studio运行模拟器报:The emulator process for AVD Pixel_4a_Edited_API_28 has terminated._晨曦的博客-CSDN博客
1.5 Android工程结构介绍
修改工程结构显示方式,将Android修改成Project[以目录结构显示];为不影响显示效果此部分内容请使用PC端查看
├── .gradle # AS自动生成的文件,AS会自动修改它的,项目打包时也会删除;
├── .idea # AS自动生成的文件,AS会自动修改它的,项目打包时也会删除;
├── app # 应用相关的东西都在里面,工作的核心目录
│ ├── build # 编译的产物。某些情况下,可以手动把它整个删掉。
│ ├── libs # 依赖包可以放这里,比如一些jar文件。
│ ├── src # 代码在这。非常重要。
│ │ ├── main
│ │ │ ├── java # 放Java代码的地方
│ │ │ ├── res
│ │ │ │ ├── drawable # 应用图标
│ │ │ │ ├── layout # Android布局文件夹
│ │ │ │ ├── mipmap # 桌面图标
│ │ │ │ ├── layout # Android布局文件夹
│ │ │ │ └── values # 颜色、样式、字符集配置文件夹
│ ├── .gitignore # 版本控制
│ ├── build.gradle # 非常重要,app的构建配置。俗称“app的gradle文件”。
│ └── proguard-rules.pro # 先不管。这个是混淆配置。
├── gradle # 它是一个构建起配置文件
├── .gitignore # 整个工程的版本控制
├── build.gradle # 很重要。项目级的配置。俗称“项目gradle文件”。
├── gradle.properties # 全局的gradle配置文件
├── gradlew # Linux/mac上执行gradle命令
├── gradlew.bat # Windows上执行gradle命令
├── local.properties # 本地配置文件,一般不上传
└── settings.gradle # gralde的项目级配置
第2章 Android的UI控件
注:控件的属性值可以【Ctrl+鼠标左键】进行查看
2.1 TextView(文本框)
本节前言:
dp(dip): device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。
pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;
sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。
2.1.1 基础属性详解:
- id:为TextView设置一个组件id,根据id,我们可以在Java代码中通过findViewById(R.id.tv_one)的方法获取到该对象,然后进行相关属性的设置,又或者使用RelativeLayout时,参考组件用的也是id!
- layout_width:组件的宽度,一般写:**wrap_content**或者**match_parent(fill_parent)**,前者是控件显示的内容多大,控件就多大,而后者会填满该控件所在的父容器;当然也可以设置成特定的大小,比如我这里为了显示效果,设置成了200dp。
- layout_height:组件的高度,内容同上。
- gravity:设置控件中内容的对齐方向,TextView中是文字,ImageView中是图片等等。
- text:设置显示的文本内容,一般我们是把字符串写到src/main/res/values/string.xml文件中,然后通过@String/xxx取得对应的字符串内容的,这里为了方便我直接就写到""里,不建议这样写!!!
- textColor:设置字体颜色,同上,通过src/main/res/values/colors.xml资源来引用,然后通过@String/xxx进行引用,别直接这样写!
- textStyle:设置字体风格,三个可选值:**normal**(无效果),**bold**(加粗),**italic**(斜体)
- textSize:字体大小,单位一般是用sp!
- background:控件的背景颜色,可以理解为填充整个控件的颜色,可以是图片哦!
XML代码:
2.1.2 带阴影的TextView
- android:shadowColor:设置阴影颜色,需要与shadowRadius一起使用哦!
- android:shadowRadius:设置阴影的模糊程度,设为0.1就变成字体颜色了,建议使用3.0
- android:shadowDx:设置阴影在水平方向的偏移,就是水平方向阴影开始的横坐标位置
- android:shadowDy:设置阴影在竖直方向的偏移,就是竖直方向阴影开始的纵坐标位置
XML代码:
2.1.3 实际开发例子——跑马灯效果
- android:singleLine:内容单行显示
- android:focusable:是否可以获取焦点
- android:focusablelnTouchMode:用于控制视图在触摸模式下是否可以聚焦
- android:ellipsize:在哪里省略文本
- android:marqueeRepeatLimit:字幕动画重复的次数
更多详见:2.3.1 TextView(文本框)详解 | 菜鸟教程 (runoob.com)
XML代码:
默认情况下没有效果:
- 开启可点击:android:clickable="true"
- 添加一个请求标签:
- 写一个类继承TextView实现其方法,并重写isFocused()方法返回true,然后将TextView标签给出自定义的全类名
2.2 Button(按钮)
本节前言:
Button是TextView的子类,所以TextView上很多属性也可以应用到Button 上!我们实际开发中对于Button的,无非是对按钮的几个状态做相应的操作,比如:按钮按下的时候 用一种颜色,弹起又一种颜色,或者按钮不可用的时候一种颜色这样!上述实现无非是通过 StateListDrawable这种Drawable资源来实现,即编写一个drawable的资源文件。
2.2.1 StateListDrawable介绍:
StateListDrawable是Drawable资源的一种,可以根据不同的状态,设置不同的图片效果,关键节点 ,我们只需要将Button的background属性设置为该drawable资源即可轻松实现,按下 按钮时不同的按钮颜色或背景!
- drawable:引用的Drawable位图,我们可以把他放到最前面,就表示组件的正常状态~
- state_focused:是否获得焦点
- state_window_focused:是否获得窗口焦点
- state_enabled:控件是否可用
- state_checkable:控件可否被勾选,eg:checkbox
- state_checked:控件是否被勾选
- state_selected:控件是否被选择,针对有滚轮的情况
- state_pressed:控件是否被按下
- state_active:控件是否处于活动状态,eg:slidingTab
- state_single:控件包含多个子控件时,确定是否只显示一个子控件
- state_first:控件包含多个子控件时,确定第一个子控件是否处于显示状态
- state_middle:控件包含多个子控件时,确定中间一个子控件是否处于显示状态
- state_last:控件包含多个子控件时,确定最后一个子控件是否处于显示状态
更多详见:3.3.3 Button(按钮)与ImageButton(图像按钮) | 菜鸟教程 (runoob.com)
- 在src/main/res/drawable中添加位矢图和new按钮选择器【btn_selecter.xml】
- 默认情况下新版本的AS设置背景颜色是无效果的,需要修改themes.xml文件
- 在src/main/res中new按钮的颜色选择器【color/btn_color_selecter.xml】
XML代码:
运行效果图:
2.2.2 常见的Button事件:
- 单击事件:setOnClickListener()
- 长按事件:setOnLongClickListener()
- 触摸/移动事件:setOnTouchListener()
代码中TAG的值为“leo”
运行结果1(长按+移动):setOnLongClickListener()与setOnTouchListener()的返回值为false
运行结果2(长按+移动):setOnLongClickListener()返回值为false,setOnTouchListener()的返回值为true,会将setOnLongClickListener()取代(即触摸/移动也会触发)
运行结果3(长按+移动):setOnLongClickListener()返回值为true,setOnTouchListener()的返回值为false,setOnClickListener()不会执行
onClickListener()的另一种写法:在activity_main.xml的Button标签中写android:onClick="leoClick",再按住Alt+Enter创建leoClick方法,优先级低于setOnClickListener()
2.3 EditText(输入框)
2.3.1 主要属性
- android:hint输入提示
- android:textColorHint输入提示文字的颜色
- android:inputType输入类型
- android:drawableXxoxx在输入框的指定方位添加图片
- android:drawablePadding设置图片与输入内容的间距
- android:paddingXxxx设置内容与边框的间距
- android:background背景色
更多详见:2.3.2 EditText(输入框)详解 | 菜鸟教程 (runoob.com)
XML代码:
运行效果图:
2.3.2 实际开发例子——用户登录
运行效果图:edit_background.xml是文本框
2.4 ImageView(图像视图)
2.4.1 主要属性
- android:src设置图片资源
- android:scaleType设置图片缩放类型
- android:maxHeight最大高度
- android:maxWidth最大宽度
- android:adjustViewBounds是否调整View的界限
XML代码:
运行效果图:
2.4.2 scaleType的属性
- fitStart:保持宽高比缩放图片,直到较长的边与lmage的边长相等缩放完成后将图片放在ImageView的左上角
- fitCenter:默认值,同上,缩放后放于中间
- fitEnd:同上,缩放后放于右下角
- fitXY:对图像的横纵方向进行独立缩放,使得该图片完全适应lmageView,但是图片的宽高比可能会发生改变
- center:保持原图的大小,显示在ImageView的中心。 当原图的size大于ImageView的size,超过部分裁剪处理。
- centerCrop:保持宽高比缩放图片,直到完全覆盖ImageView,可能会出现图片的显示不完全
- centerInside:保持宽高比缩放图片,直到ImageView能够完全地显示图片
- matrix:不改变原图的大小,从ImageView的左上角开始绘制原图,原图超过ImageView的部分作裁剪处理
更多详见:2.3.4 ImageView(图像视图) | 菜鸟教程 (runoob.com)
2.5 ProgressBar(进度条)
2.5.1 常用属性详解
- android:max:进度条的最大值
- android:progress:进度条已完成进度值
- android:indeterminate:如果设置成true,则进度条不精确显示进度
- style="?android:attr/progressBarStyleHorizontal"水平进度条
更多详见:ProgressBar(进度条) | 菜鸟教程 (runoob.com)
XML代码:
Java代码:
public class MainActivity extends AppCompatActivity {
private ProgressBar pb1;
private ProgressBar pb2;
private Integer speed;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb1 = findViewById(R.id.pb1);
pb2 = findViewById(R.id.pb2);
speed = (int)(pb2.getMax() * 0.1);
}
public void leoClick(View view) {
if (pb1.getVisibility() == View.GONE) {
pb1.setVisibility(View.VISIBLE); //显示
} else {
pb1.setVisibility(View.GONE);
}
}
public void loadClick(View view) {
int progress = pb2.getProgress();
pb2.setProgress(progress + speed);
}
}
运行效果图:
2.6 Notification(状态栏通知)
2.6.1 Notification的基本使用流程
状态通知栏主要涉及到2个类:Notification 和NotificationManager
Notification:通知信息类,它里面对应了通知栏的各个属性
NotificationManager:是状态栏通知的管理类,负责发通知、清除通知等操作。
使用的基本流程:
- Step 1. 获得NotificationManager对象: NotificationManager nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- Step 2. 创建一个通知栏的Builder构造类: Notification.Builder nBuilder = new Notification.Builder(this);
在老的版本中是使用Notification()
新的版本是使用Notification.Builder()
为了兼容性现在使用NotificationCompat.Builder() - Step 3. 对Builder进行相关的设置,比如标题,内容,图标,动作等!
- Step 4. 调用Builder的build()方法为notification赋值
- Step 5. 调用NotificationManager的notify()方法发送通知!
- PS:另外我们还可以调用NotificationManager的cancel()方法取消通知
2.6.2 NotificationChannel(通知渠道)
通知渠道,Android 8.0引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。
通知重要程度设置,NotificationManager类中
- IMPORTANCE_NONE:关闭通知
- IMPORTANCE_MIN:开启通知,不会弹出,但没有提示音,状态栏中无显示
- IMPORTANCE_LOW:开启通知,不会弹出,不发出提示音,状态栏中显示
- IMPORTANCE_DEFAULT(默认):开启通知,不会弹出,发出提示音,状态栏中显示
- IMPORTANCE_HIGH:开启通知,会弹出,发出提示音,状态栏中显示
2.6.3 Notification的常见方法
- setContentTitle(String string):设置标题
- setContentText(String string):设置文本内容
- setSmalllcon(int icon):设置小图标
- setLargelcon(Bitmap icon):设置通知的大图标
- setColor(int argb):设置小图标的颜色
- setContentlntent(Pendinglntent intent):设置点击通知后的跳转意图
- setAutoCancel(boolean boolean):设置点击通知后自动清除通知
- setWhen(long when):设置通知被创建的时间
注意:Android从5.0系统开始,对于通知栏图标的设计进行了修改。现在Google要求,所有应用程序的通知栏图标,应该只使用alpha图层来进行绘制,而不应该包括RGB图层。
2.6.3 实际例子——模拟通知
XML代码:
Java代码:
运行效果:
更多详见:2.5.8 Notification(状态栏通知)详解 | 菜鸟教程 (runoob.com)
2.7 Toolbar(工具栏目)
2.7.1 常用属性详解
取消系统默认的标题栏目:src/main/res/values/themes.xml --> style标签 --> parent="Theme.MaterialComponents.DayNight.NoActionBar"
- android:layout_width="match_parent"
- android:layout_height=""?attr/actionBarSize":使用的是ActionBar的高度
- android:background="#FFFF00"
- app:navigationlcon=" @drawable/ic_baseline_arrow_back_24""
- app:title="主标题"
- app:titleTextColor="#FF0000"
- app:titleMarginStart="90dp":左间距
- app:subtitle="子标题"
- app:subtitleTextColor="#00FFFFF"
- app:logo="@mipmap/ic_launcher"
使用ToolBar替换系统默认的DarkActionBar:
XML代码:
Java代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.tb); //androidx.appcompat.widget.Toolbar
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e("leo","ToolBar被点击了!");
}
});
}
}
运行效果图:
2.7.2 标题居中显示
代码及运行效果:
2.8 AlertDialog(对话框)
2.8.1 实现方式/使用流程
- AlertDialog.Builder builder = new AlertDialog.Builder(context); 构建Dialog的各种参数
- Builder.setlcon(int iconld); 添加ICON
- Builder.setTitle(CharSequence title); 添加标题
- Builder.setMessage(CharSequence message);添加消息
- Builder.setView(View view); 设置自定义布局
- setPositiveButton:确定按钮
- setNegativeButton:取消按钮
- setNeutralButton:中间按钮
- Builder.create(); 创建Dialog
- Builder.show(); 显示对话框
使用流程:
- Step 1:创建AlertDialog.Builder对象;
- Step 2:调用setIcon()设置图标,setTitle()或setCustomTitle()设置标题;
- Step 3:设置对话框的内容:setMessage()还有其他方法来指定显示的内容;
- Step 4:调用setPositive/Negative/NeutralButton()设置:确定,取消,中立按钮;
- Step 5:调用create()方法创建这个对象,再调用show()方法将对话框显示出来;
更多详见:2.5.9 AlertDialog(对话框)详解 | 菜鸟教程 (runoob.com)
XML代码:
Java代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "leo";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void leoClick(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); //androidx.appcompat.app.AlertDialog;
builder.setIcon(R.mipmap.ic_launcher)
.setTitle("对话框")
.setMessage("今天天气怎么样?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.e(TAG, "点击了确定按钮");
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.e(TAG, "点击了取消按钮");
}
})
.setNeutralButton("中间/其他", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.e(TAG, "点击了中间/其他按钮");
}
})
.create()
.show(); //最后两个位置不能交换
}
}
运行效果图1(无setView()):
dialog_view.xml代码:
运行效果2:
2.9 PopupWindow(悬浮框)
2.9.1 常用方法
- setContentView(View contentView):设置PopupWindow显示的view
- showAsDropDown(View anchor):相对某个控件的位置〈正左下方),无偏移showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移
- setFocusable(boolean focusable):设置是否获取焦点
- setBackgroundDrawable(Drawable background):设置背景dismiss()关闭弹窗
- setAnimationStyle(int animationStyle):设置加载动画
- setTouchable(boolean touchable):设置触摸使能
- setOutsideTouchable(boolean touchable):设置PopupWindow外面的触摸使能
更多详见:2.6.1 PopupWindow(悬浮框)的基本使用 | 菜鸟教程 (runoob.com)
XML代码:
popup_view.xml代码:
Java代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "leo";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void leoClick(View view) {
View popupView = getLayoutInflater().inflate(R.layout.popup_view, null);
//视图、宽、高、是否获取焦点(点击空白处取消)
PopupWindow popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
popupWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.ceshi)); //设置背景
popupWindow.showAsDropDown(view, 50,0);
Log.e(TAG, "leoClick: 111111");
Button btn1 = popupView.findViewById(R.id.btn1);
Button btn2 = popupView.findViewById(R.id.btn2);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e(TAG, "onClick: 按钮1被点击");
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.e(TAG, "onClick: 按钮2被点击");
}
});
}
}
运行结果图:
ListView(列表条目)
使用步骤:
- Step 1:在布局文件中添加一个LisetView
- Step 2:新建一个条目布局文件,这里为list_item.xml,在此文件中写好每一个条目的布局内容
- Step 3:创建item对应的JavaBean
- Step 4:创建待填充的数据data集合并添加数据
- Step 5:创建辅助类Adapter(适配器)需继承BaseAdapter并实现其方法,这里命名为MyAdapter,此类中包含适配的数据和Context。实现getView的时候,需要创建View并将其返回,然后需要在此方法中通过view的findById获取list_item中的组件并填充内容
- Step 6: 创建并初始化MyAdpter适配器,然后获取布局中的ListView,通过set方法将创建好的MyAdpter适配器复值给ListView的Adapter方法就可以完成了
inflate()方法解析:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
- 第一个参数:要获取的布局文件,传入R.layout.xxx
- 第二个参数:这个参数也是一个布局,是为第一个参数指定的父布局。
- 第三个参数(如果第二个参数为null这个参数将失去作用)
true:将第一个参数表示的布局添加到第二参数的布局中。
false:不将第一个参数表示的布局添加到第二参数的布局中。
既然不添加,那么为什么第二个参数不设置为null呢。
不添加的话,这个函数就只剩下一个作用了,那就是获取布局,为了使第一个参数的宽高属性不失效,所以要为他指定一个父布局
更多详见:2.4.5 ListView简单实用 | 菜鸟教程 (runoob.com)
MyActivity.java:
public class MainActivity extends AppCompatActivity {
private ArrayList data = new ArrayList();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < 20; i++) {
Bean bean = new Bean("享学"+i);
data.add(bean);
}
ListView listView = findViewById(R.id.lv);
listView.setAdapter(new MyAdapter(data, this));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView adapterView, View view, int i, long l) {
Log.e("TAG", "onItemClick: " + i);
}
});
}
}
Bean.java:
package com.example.mylistview;
public class Bean {
private String name;
public Bean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MyAdapter.java:
public class MyAdapter extends BaseAdapter {
private ArrayList data;
private Context context;
public MyAdapter(ArrayList data, Context context) {
this.data = data;
this.context = context;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int i) {
return data.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
}
TextView tv = view.findViewById(R.id.tv);
tv.setText(data.get(i).getName());
Log.e("TAG", "getView: " + i);
return view;
}
}
activity_main.xml:
list_item.xml:
运行效果图:
优化后的MyAdapter.java:即用viewHolder存储传过来的view,并为view绑定绑定上viewHolder对象
public class MyAdapter extends BaseAdapter {
private ArrayList data;
private Context context;
public MyAdapter(ArrayList data, Context context) {
this.data = data;
this.context = context;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int i) {
return data.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
view = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
viewHolder.textView = view.findViewById(R.id.tv);
view.setTag(viewHolder);
} else{
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.textView.setText(data.get(i).getName());
Log.e("TAG", "getView: " + i);
return view;
}
public final class ViewHolder{
TextView textView;
}
}
RecyclerView(大量数据集合的列表)
1 什么是RecycylerView:
RecycylerView是support-v7包中的新组件,是一个强大的滑动组件,是一个增强版的ListView,与经典的ListView相比,同样拥
不仅可以实现和ListView同样的效果,还优化了ListView中存在的各种不足之处,这一点从它的名字Recyclerview即回收view也可以看出。ResyslerView 能够实现横向滚动,这是ListView所不能实现的
2 如何使用:
Step 1:添加RecycylerView的依赖:
Step 2:创建布局:
- activity_main.xml
- item.xml
Step 3:创建适配器:
- 创建适配器类继承RecyclerView.Adapter类,传入泛型为RecyclerView.ViewHolder的子类(第2点)。
- 创建内部类即RecyclerView.ViewHolder类的子类,并为其绑定item控件。
- 实现RecyclerView.Adapter类的相关方法(快捷键为Alt+Enter)。
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType); public void onBindViewHolder(@NonNull MyViewHolder holder, int position);//为MyViewHolder绑定上数据 public int getItemCount();//返回数据的条目数
Step 4:在MainActivity.java中使用RecyclerView:
- 获取RecyclerView对象 。
- 准备适配的数据 。
- 适配器实例化 。
- 设置LayoutManager布局方式。
- 设置Adapter 。
MyActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList data = new ArrayList();
for (int i = 0; i < 20; i++) {
Bean bean = new Bean("享学"+i);
data.add(bean);
}
RecyclerView recyclerView = findViewById(R.id.rv);
MyAdapter myAdapter = new MyAdapter(data, this);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(myAdapter);
}
}
Bean.java:
public class Bean {
private String name;
public Bean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter {
private ArrayList data;
private Context context;
public MyAdapter(ArrayList data, Context context) {
this.data = data;
this.context = context;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = View.inflate(context, R.layout.recycler_item, null);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.textView.setText(data.get(position).getName());
}
@Override
public int getItemCount() {
return data==null?0:data.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
}
}
}
activity_main.xml:
recycler_item.xml:
运行效果图:
RecyclerView的点击事件
RecyclerView并没有像ListView一样暴露出Item点击事件或者长按事件处理的api,也就是说使用RecyclerView时候,需要我们自己来实现Item的点击和长按等事件的处理。实现方法有多种:
- 可以监听RecyclerView的Touch事件然后判断手势做相应的处理
- 也可以在通过在绑定ViewHolder的时候设置监听,然后通过Apater回调出去
这里选择第二种方法,更加直观和简单。
思路为:
通过在适配器中设置一个包含onItemClick函数的接口,然后添加setOnItemClickListener方法用于接收用户创建的该接口的实现类对象,最后在onBindViewHolder函数中调用onItemClick方法执行用于传入的对象的onItemClick方法。设置步骤:
- Step 1:创建OnItemClickListener接口
public interface OnItemClickListener{ void onItemClick(View view, int position); void onItemLongClick(View view, int position); }
- Step 2:添加setOnItemClickListener方法()
public void setOnItemClickListener(OnItemClickListener listenser) { this.onItemClickListener = listener; }
- Step 3:在onBindViewHolder中调用实现类对象的onItemClick函数,也可在MyAdapter.MyViewHolder中调用实现类对象的onItemClick函数
//第1种实现方法 @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { holder.textView.setText(data.get(position).getName()); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if(onItemClickListener != null) { int pos = holder.getLayoutPosition(); onItemClickListener.onItemClick(holder.itemView, pos); } } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if(onItemClickListener != null) { int pos = holder.getLayoutPosition(); onItemClickListener.onItemLongClick(holder.itemView, pos); } //表示此事件已经消费,不会触发单击事件 return true; } }); }
//第2种实现方法 public class MyViewHolder extends RecyclerView.ViewHolder{ TextView textView; public MyViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(onItemClickListener != null) { onItemClickListener.onItemClick(itemView, getLayoutPosition()); } } }); itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { if(onItemClickListener != null) { onItemClickListener.onItemLongClick(itemView, getLayoutPosition()); } return false; } }); } }
- Step 5:调用setOnItemClickListener方法传入OnItemClickListener的实现类对象
myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Log.e("TAG", "onItemClick: " + position); } @Override public void onItemLongClick(View view, int position) { Log.e("TAG", "onLongItemClick: " + position); } });
MyAdapter.java的完整代码:
public class MyAdapter extends RecyclerView.Adapter {
private ArrayList data;
private Context context;
public MyAdapter(ArrayList data, Context context) {
this.data = data;
this.context = context;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = View.inflate(context, R.layout.recycler_item, null);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.textView.setText(data.get(position).getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, pos);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, pos);
}
//表示此事件已经消费,不会触发单击事件
return true;
}
});
}
@Override
public int getItemCount() {
return data==null?0:data.size();
}
public interface OnItemClickListener{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(MyAdapter.OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
// itemView.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// if(onItemClickListener != null) {
// onItemClickListener.onItemClick(itemView, getLayoutPosition());
// }
// }
// });
// itemView.setOnLongClickListener(new View.OnLongClickListener() {
// @Override
// public boolean onLongClick(View view) {
// if(onItemClickListener != null) {
// onItemClickListener.onItemLongClick(itemView, getLayoutPosition());
// }
// return false;
// }
// });
}
}
}
recycler_itme.xml:
运行效果图:
第3章 Android的布局方式
3.1 LinearLayout(线性布局)
3.1.1 常用属性
- orientation:布局中组件的排列方式
- gravity:控制组件所包含的子元素的对齐方式,可多个组合
- layout_gravity:控制该组件在父容器里的对其方式
- background:为该组件设置一个背景图片,或者是直接用颜色覆盖
- divider:分割线(可以使用View来设置分割线的效果)
- showDividers:设置分割线所在的位置,none(无),beginning(开始),end(结束),middle(每两个组件间)
- dividerPadding:设置分割线的padding
- layout_weight(权重):该属性是用来按比例划分剩余的区域,按控件的对齐方式
更多详见:2.2.1 LinearLayout(线性布局) | 菜鸟教程 (runoob.com)
XML代码:
运行效果图:
3.2 RelativeLayout(相对布局)
3.2.1 相对父容器定位
- layout_alignParentLeft:左对齐
- layout_alignParentRight:右对齐
- layout_alignParentTop:顶部对齐
- layout_alignParentBottom:底部对齐
- layout_centerHorizontal:水平居中
- layout_centerVertical:垂直居中
- layout_centerInParent:中间位置
3.2.2 相对兄弟容器定位
- layout_toLeftOf:放置于参考组件的左边
- layout_toRightOf:放置于参考组件的右边
- layout_above:放置于参考组件的上方
- layout_below:放置于参考组件的下方
- layout_alignTop:对齐参考组件的上边界
- layout_alignBottom:对齐参考组件的下边界
- layout_alignLeft:对齐参考组件的左边界
- layout_alignRight:对齐参考组件的右边界
3.2.3 通用属性
margin设置组件与父容器的边距
- layout_margin:上下左右偏移
- layout_marginLeft:左边距
- layout_marginRight:右边距
- layout_marginTop:上边距
- layout_margiBottom:下边距
padding设置组件与子容器的边距
- layout_padding:上下左右偏移
- layout_paddingLeft:左边距
- layout_paddingRight:右边距
- layout_paddingTop:上边距
- layout_paddingBottom:下边距
更多