3.UI开发
常用属性:
- fill_parent/match_parent:让当前控件的大小和父布局的大小一样
- wrap_parent:让当前控件的大小刚好能够包含住里面的内容
- android:background 用于为布局或控件指定一个背景
- android:layout_margin 指定控件上下左右的偏移的距离
3.1 常用控件
为每一个组件在定义时都制定id,width,height
- gravity: 对齐方式
- visibility: visible(控件可见) invisible(控件不可见,但仍占据位置) gone(不可见且不占空间)
3.1.1 TextView文本框
定义:
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="">
</TextView>
简单使用示例:
TextView textView = (TextView)findViewById(R.id.textView);
textView.setText(""); //给textVIew设置值
textView.getText();//获取textView的值
注意:在为显示聊天信息的文本框设置背景时,需要使用9-Patch图片,这样可以实现自动拉伸。
3.1.2 Button按钮
-
系统会对button中所有的英文字母自动进行大写转换可以使用textAllCaps属性禁用
-
只有一个按钮点击事件推荐使用匿名内部类
-
多个按钮使用点击事件实现View.OnClickListener接口并重写Onclick方法,然后使用switch进行分流
-
两种为按钮添加点击事件的方法:
- 一种是在代码中直接绑定
- 一种是在Activity中编写包含View类型的参数的方法,并通过布局文件android:onClick属性指定方法名
定义:
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:textAllCaps="false">
</Button>
简单使用:
Button btn = (Button)findViewById(R.id.button);
//为按钮设置监听事件 单按钮
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "你点击了这个按钮", Toast.LENGTH_SHORT).show();
}
});
//-----------------------------------------------------------------------------------------
//多个按钮设置点击事件
//方法1:创建 View.OnClickListener实例
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()){
//使用switch进行分流
case R.id.btn:
break;
default:
break;
}
}
};
//为按钮设置点击事件
btn.setOnClickListener(listener);
//-----------------------------------------------------------------------------------------
//方法2:重写onClick()方法
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn:
break;
default:
break;
}
}
//为按钮设置点击事件
btn.setOnClickListener(this)
//-----------------------------------------------------------------------------------------
//通过自定义点击事件函数,通过xml文件绑定
public void myOnClick(View v){
//编写要执行的代码
}
android:onClick="myOnClick"
3.1.3 EditText编辑框
- hint: 指定一段提示文本
- maxLines: 指定editText的最大行数
- inputType: 控制输入的显示类型
定义:
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint=""
android:maxLines="1">
</EditText>
简单使用:
EditText editText = (EditText)findViewById(R.id.editText);
//获取输入框中的内容
editText.getText().toString()
3.1.4 ImageView
用于在屏幕中显示任何Drawable对象,通常用来显示图片。
说明:使用ImageView组件显示图像时,通常需要将要显示的图片放在res/drawable或res/mipmap目录中
- src: 图片路径
动态更改ImageView中的图片 使用 setImageResource()方法修改显示的图片路径
定义:
<ImageView
android:id="@+id/imgView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/img">
</ImageView>
简单使用:
ImageView imageView = (ImageView)findViewById(R.id.imgView);
//动态更改ImageView中的图片
imageView.setImageResource(R.drawable.img2);
3.1.5 ProgressBar进度条
显示一个正在加载数据的进度条,不指定visibility属性控件都可见
- style: 指定进度条的样式
- max: 指定进度条的最大值
- getVisibility(): 获取组价可见性
- setVisibility(): 设置组件的可见性
可见性:VISIBLE可见 GONE不可见不占位置 INVISIBLE不可见仍占据大小
常用方法:
- setProgress(int progress):用于设置进度完成的百分比
- incrementProgressBy(int diff):用于设置进度条的进度的增加或减少。当参数值为正数时,表示增加,反之表示减少。
定义:
<!-- style指定进度条样式 圆形进度条和水平进度条-->
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="100">
</ProgressBar>
简单使用:
progressBar = (ProgressBar)findViewById(R.id.progress_bar);
//动态的更新进度条
//因为在子线程中不能进行UI操作,所以使用 runOnUiThread()方法将线程切回到主线程中
runOnUiThread(new Runnable() {
@Override
public void run() {
//获取当前进度值
int progress = progressBar.getProgress();
while(progress!=100){
//更新进度
progress += 10;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//将进度重新设置
progressBar.setProgress(progress);
}
}
});
3.1.6 AlertDialog对话框
对话框:置顶于所有界面之上,能屏蔽掉其他控件的交互能力,通过点击事件触发一个alert对话框
创建对话框示例:
//创建对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("This is Dialog"); //设置对话框标题
dialog.setMessage("Something important"); //设置对话框的内容
dialog.setCancelable(false); //可否用返回键关闭
//设置确定按钮的点击事件
dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//设置取消按钮的点击事件
dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show(); //显示对话框
3.1.7 ProgressDialog 进度条对话框
置顶于所有界面之上,能屏蔽掉其他控件的交互能力 会显示进度条
简单使用示例:
ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("");//设置标题
progressDialog.setMessage(""); //设置提示消息
progressDialog.setCancelable(true); //设置可否用返回键关闭
progressDialog.show(); //显示
// progressDialog.dismiss(); 关闭对话框
3.2 4种基本布局
3.2.1 线性布局管理器(LinearLayout)
线性布局管理器是将放入其中的组件按照垂直或水平方向来布局。在垂直线性布局管理器中每一行只能放一个组件,在水平线性布局管理器中,每一列只能放一个组件。线性布局管理器组件不会换行,当组件排列到窗体的边缘后,剩下的组件将不会被显示。
- 通过 orientation属性 指定 vertical垂直 horizontal 水平排列(默认)
- 通过 horizontal 排列时内部控件不能将宽度指定为 match_parent ; 只有垂直方向上的对齐方式才会生效
- 通过 vertical 排列时不能将高度指定为 match_parent ; 只有水平方向上的对齐方式才会生效
属性:
- android:gravity: 指定文字在控件中的对齐方式
- android:layout_gravity: 指定控件在布局中的对齐方式
- 使用 layout_weight属性时,默认将组件宽度指定为0dp
指定weight值时,系统将所有控件weight值相加除以总值就是每个控件显示的宽度大小
定义:
<!-- android:orientation:排列方式 -->
<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:orientation="horizontal"
tools:context=".MainActivity">
</LinearLayout>
3.2.2 相对布局管理器(RelativeLayout)
- alignParent 相对于父布局进行定位
- 控件相对于控件进行定位 above上方 below 下方 toLeftOf左侧 toRightOf右侧
- alignLeft:一个控件的左边缘和另一个控件的左边缘对齐
- alignRight:一个控件的右边缘和另一个控件的右边缘对齐
- alignTop: 一个控件的上边缘和另一个控件的上边缘对齐
- alignBottom:一个控件的下边缘和另一个控件的下边缘对齐
定义:
<RelativeLayout 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"
tools:context=".RelativeLayoutActivity">
</RelativeLayout>
3.2.3 帧布局管理器(FrameLayout)
帧布局管理器中每加入一个组件,都将创建一个空白的区域,通常称为一帧,从屏幕的左上角(0,0)坐标点开始布局,多个组件层叠排序,后面的会覆盖前面的组件,即所有的控件都会放在布局左上角。
FrameLayout支持的常用XML属性:
- android:foreground:设置该帧布局容器的前景图像
- android:foregroundGravity:定义前景图像显示的位置
定义:
<FrameLayout 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"
tools:context=".FrameLayoutActivity">
</FrameLayout>
3.2.4 百分比布局(PercentFrameLayout)
使用前需要在app下的build.gradle文件中引入 implementation ‘androidx.percentlayout:percentlayout:1.0.0’
定义:
<!-- 需要多定义一个app的命名空间 -->
<androidx.percentlayout.widget.PercentFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".PercentFrameLayoutActivity">
</androidx.percentlayout.widget.PercentFrameLayout>
<!--给每个组件设置宽高时使用 以百分比的形式-->
app:layout_heightPercent=""
app:layout_widthPercent=""
3.3 自定义控件
所有控件都是直接或间接继承自View,所有的布局都是直接或间接继承自ViewGroup
ViewGroup: 可以包含很多子View和子ViewGroup,用于放置控件和布局的容器
3.3.1 引入其他布局文件
<include layout="@layout/布局文件名称"/>
3.3.2 创建自定义控件
- 创建一个继承于android.view.View类的View类,并且重写构造方法(自定义的view类中,至少需要重写一个构造方法),在布局文件中引入该控件时就会调用构造函数,在构造函数中需要对标题栏布局进行动态加载
- 根据需要重写其他的方法
- 在项目的活动中,创建并实例化自定义View类,并将其添加到布局管理器中,或者在布局文件中添加自定义控件
LayoutInflater: 布局服务
加载布局的方法: inflate (int resource(对应的布局资源id), ViewGroup root(为该布局的外部在嵌套一层父布局), boolean attachToRoot(是否为加载的布局添加一个root的外层容器))
动态加载布局示例:
LayoutInflater.from(context).inflate(R.layout.布局文件,this,false);
在布局文件中添加自定义控件:
<!-- 标签名为自定义控件的全类名-->
<com.example.全类名
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.全类名>
3.4 ListView列表
数组中的数据无法直接传递给ListView 需要借助适配器来完成
ListView的简单使用: ArrayAdapter的构造函数参数 当前上下文、ListView子项布局的id、需要适配的数据
ListView子项布局id 详细说明:
- android.R.layout.simple_list_item_1 一行text
- android.R.layout.simple_list_item_2 一行title,一行text
- android.R.layout.simple_list_item_single_choice 单选按钮
- android.R.layout.simple_list_item_multiple_choice 多选按钮
- android.R.layout.simple_list_item_checked checkbox 复选框按钮
定制ListView的界面:
- 定义一个实体类 作为ListView适配器的适配类型
- 自定义一个ListView子项的布局文件
- 创建一个自定义的适配器继承自ArrayAdapter,并将泛型指定为实体类的类型
- 重写getView()方法(该方法在每个子项被滚动到屏幕内的时候会被调用,每次都会重新加载布局),获取当前项的实体类的实例, 并使用LayoutInflater 加载传入的布局
- 获取布局文件中组件的实例,设置组件要显示的内容 并返回 view
- 在主活动中将数据初始化,并创建自定义适配器的实例传入上下文对象、布局文件id、数据
- 给listView设置适配器
优化ListView的运行效率
- 运用convertView将加载好的布局缓存,如果为null,则使用 LayoutInflater加载布局,如果不为null则直接复用
- 借助一个内部类将控件的实例进行缓存
如果 convertView为null,使用LayoutInflater加载布局
同时创建内部类实例将其中的控件进行获取,然后使用setTag()将内部类加入到view中
如果 convertView不为null,使用getTag()从view中获取内部类实例 - 给内部类中的控件设置要显示的内容
ListView的点击事件:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//position 当前点击的定位
}
});
定义:
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
简单使用示例(基本固定写法):
//首先创建一个实体类作为适配器的适配类型
//TODO:
//定义子项布局文件
//TODO:
//创建一个Adapter类
public class DemoAdapter extends ArrayAdapter<实体类名> {
private int resourceId;
public DemoAdapter(@NonNull Context context, int resource, @NonNull List<实体类名> objects) {
super(context, resource, objects);
resourceId = resource;
}
class ViewHolder{
//定义子项布局文件中的控件
}
/**
* 该方法在每个子项被滚动到屏幕内的时候会被调用,每次都会重新加载布局
* @param position
* @param convertView 用于将之前加载好的布局进行缓存
* @param parent
* @return
*/
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
getItem(position); // 通过getItem()方法获取当前项的实例
View view;
ViewHolder viewHolder;
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
//实例化ViewHolder中定义的控件
//TODO:
//将ViewHolder存储在View中
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder)view.getTag(); //重新获取ViewHolder
}
//给ViewHolder中的控件设置值
//TODO:
return view;
}
}
//在主活动中
//初始化list数据
//TODO:
//获取adapter实例,并给ListView设置adapter
DemoAdapter adapter = new DemoAdapter(MainActivity.this,R.layout.布局文件,list);
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
3.5 RecyclerView 滚动控件
使用RecyclerView滚动控件
-
在app目录下 build.gradle文件中dependencies中 引入 implementation ‘com.android.support:recyclerview-v7:27.1.1’
在布局xml文件中 添加 androidx.recyclerview.widget.RecyclerView -
为RecyclerView准备一个适配器继承自RecyclerView.Adapter,定义成员变量 数据源list,并指定泛型为 适配器中的内部类 ViewHolder(需要自定义并继承自RecyclerView.ViewHolder)
-
ViewHolder类中具体:
成员变量为要显示的控件
在构造函数中传入View参数,通常为RecyclerView子项的最外层布局 将控件实例化 -
Adapter适配器类中的构造函数中将全局数据源list赋值,并重写父类的3个方法
- onCreateViewHolder(ViewGroup parent, int viewType): 用于创建ViewHolder的实例,并将加载出来的布局传入到构造函数中,返回viewHolder实例
- onBindViewHolder(ViewHolder holder, int position): 对RecyclerView中的子项进行赋值,根据position从数据源list中获取对象实例进行赋值
- getItemCount():返回数据源的长度
-
在主活动中定义数据源list,将list赋值或者初始化,创建布局管理器对象,线性布局,瀑布流布局,网格布局选其一,然后通过setLayoutManager()方法为RecyclerView设置布局方式,实例化适配器对象,并通过setAdapter()方法设置适配器
布局管理器:
- LinearLayoutManager 线性布局:
默认是纵向滚动的 通过setOrientation()方法设置 滚动方式- 纵向排列: 布局文件设置水平排列 宽为 match_parent 高度为 wrap_content 让组件垂直居中
- 横向排列: 布局文件设置为垂直排列 明确设置宽度大小 高度为 wrap_content 让组件水平居中
- GridLayoutManager 网络布局
两个参数:第一个参数用于指定上下文,第二个参数指定列数 - StaggeredGridLayoutManager 瀑布流布局
宽度根据列数自动适配
两个参数:第一个参数用于指定布局列数 第二个参数用于指定布局的排列方向
制作9patch图片,在AS中png图片右键
定义:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
简单使用示例(基本固定写法):
//定义一个实体类
//TODO:
//创建一个适配器类继承自RecyclerView.Adapter
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.ViewHolder> {
private List<实体类名> mlist;
public DemoAdapter(List<实体类名> mlist) {
this.mlist = mlist; //赋值
}
/**
* 用于创建ViewHolder实例
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.子项布局文件名,parent,false);
//将加载出来的布局传入到 ViewHolder 的构造函数
final ViewHolder holder = new ViewHolder(view);
/**
* RecyclerView的点击事件
* 可以给每个控件分开设置
*/
//控件的点击事件
holder.控件名.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取用户点击的position
int position = holder.getAdapterPosition();
//通过position获取点击项实例
mlist.get(position);
}
});
return holder;
}
/**
* 用于对RecyclerView子项数据进行赋值,会在每个子项滚动到屏幕内的时候执行
*/
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
mlist.get(position); //根据position在数据源中获取对应实例
//给ViewHolder中的控件赋值
//TODO:
}
@Override
public int getItemCount() {
return mlist.size(); //返回数据源的长度
}
static class ViewHolder extends RecyclerView.ViewHolder{
View view;
//定义子项布局文件中的控件
//TODO:
public ViewHolder(@NonNull View itemView) {
super(itemView);
view = itemView;
//实例化控件
//TODO:
}
}
}
//在主活动中
//初始化 数据源list数据
//TOOD:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
//以下布局使用其一即可
//使用LinearLayout水平布局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager); //指定为线性布局的方式 默认是纵向排列的
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(linearLayoutManager); // 指定为横向布局的方式
//使用瀑布布局 参数含义为一行3列且垂直排列
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//使用网格布局 参数含义为一行4列
GridLayoutManager layoutManager = new GridLayoutManager(MainActivity.this,4);
recyclerView.setLayoutManager(layoutManager);
//给recyclerView设置adapter
DemoAdapter adapter = new DemoAdapter(list);
recyclerView.setAdapter(adapter);