《第一行代码》核心知识点:Android的脸面之UI控件
创始人
2024-02-06 16:59:49

《第一行代码》核心知识点:Android的脸面之UI控件

  • 前言
  • 三、Android的脸面之UI控件
    • 3.1 常用控件介绍
      • 3.1.1 TextView
      • 3.1.2 Button
      • 3.1.3 EditText
      • 3.1.4 ImageView
      • 3.1.5 ProgressBar
      • 3.1.6 AlertDialog
      • 3.1.7 ProgressDialog
    • 3.2 四种基本布局介绍
      • 3.2.1 线性布局
      • 3.2.2 相对布局
      • 3.2.3 帧布局
      • 3.2.4 百分比布局
      • 3.2.5 布局使用小技巧
    • 3.3 创建自定义控件
      • 3.3.1 常用控件与布局的继承结构
      • 3.3.2 自定义标题栏控件
    • 3.4 玩转ListView控件
      • 3.4.1 ListView控件简单案例(每项仅仅展示字符串)
      • 3.4.1 定制ListView子项数据的展示样式
    • 3.5 玩转RecyclerView控件
      • 3.5.1 由于RecyclerView属于新增控件,我们需要引用这个控件
      • 3.5.2 设计每个Item的布局
      • 3.5.3 设计列表中每个Item所包含要素的类
      • 3.5.4 定义适配器
      • 3.5.5 RecyclerView使用该适配器
      • 3.5.6 RecyclerView点击事件
  • 参考书籍:第一行代码

前言

本文讲解Android常用控件(包括ListView、RecyclerView)以及Android中常用几种布局。

三、Android的脸面之UI控件

3.1 常用控件介绍

3.1.1 TextView

文本控件,常用于显示文字。
常用xml属性如下:

  • android:id 标识该控件的id
  • android:layout_width 控件宽度,常取值"match_parent(与屏幕一样宽)"、wrap_content(与文本内容一样宽)、或者固定值
  • android:layout_height 控件宽度,取值与高度类似
  • android:text 显示的文本内容
  • android:textSize 文本字体大小
  • android:textColor 文本颜色
  • 等等

3.1.2 Button

按钮控件,一般用于用户点击操作。
常用xml属性与TextView差不多,其中Button控件的英文字母自动转为大写,可以通过android:textAllCaps="false"属性禁止该特性。

3.1.3 EditText

也是文本控件,但是与TextView控件的区别是,EditText控件允许用户在控件中输入和编辑内容。
常用xml属性与TextView差不多,一般编辑框中都会有提示信息,我们可以使用 android:hint=“info” xml属性设置提示信息。我们也可以通过android:maxLines="2"属性设置最大行数。

3.1.4 ImageView

展示图片的控件。
通过android:src 属性设置展示的图片

3.1.5 ProgressBar

用于在界面上显示一个进度条。
常用xml属性:

  • android:visibility 可以设置控件是否可见,主要有三种值,visible表示可见,这也是默认的设置;invisible表示不可见,但是它仍然占据屏幕控件,类似控件透明了;gone表示不可见,并且不再占屏幕空间。
  • style="@style/Widget.AppCompat.ProgressBar.Horizontal"设置进度条的形状
  • android:max=“100” 设置进度条的最大值,我们可以在程序中调用setProgress()方法修改进度值。

3.1.6 AlertDialog

在当前界面弹出一个对话框,这个对话框置于所有界面元素之上,能够屏蔽掉其它控件的交互能力,一般用于展示向用户进行提示的信息。
使用方法示例代码:

AlertDialog.Builder dialog=new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("这是一个提示对话框");
dialog.setMessage("请注意一些信息");
dialog.setCancelable(false);//不能通过back键返回
dialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {//点击确定后处理的事情}});
dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {//点击取消后处理的事情}});
dialog.show();

3.1.7 ProgressDialog

ProgressDialog会在界面上弹出一个带有进度条的窗口,一般用于显示进度。
使用方法示例代码如下:

ProgressDialog  dialog1=new ProgressDialog(MainActivity.this);
dialog1.setTitle("这是一条进度提示对话框");
dialog1.setMessage("进度提示信息");
dialog1.setCancelable(false);//用户不能通过back键关闭该窗口
dialog1.show();

数据处理完成后,我们可以调用 dialog1.dismiss();方法关闭窗口

3.2 四种基本布局介绍

布局是一种可以放置很多控件或布局的容器。布局就像是控件位置的顶层设计者,它可以按照一定的规律调整内部控件的位置,不然控件随意乱放,界面应该很难整洁精美。布局与控件的关系如图所示。
在这里插入图片描述

3.2.1 线性布局

线性布局(LinearLayout)可以将控件按照垂直或者水平方向排列。

  • 垂直方向 xml属性:android:orientation=“vertical”
  • 水平方向 xml属性:android:orientation=“horizontal”
  • android:layout_gravity与android:gravity的区别?
    android:layout_gravity是控件在布局中的对齐方式,而android:gravity是文字在控件中的对齐方式。需要注意的是,当LinearLayout的排列方向是horizontal时,只有垂直方向上的对齐方式才会生效,因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向上的长度都会改变,因而无法指定该方向上的对齐方式。同样的道理,当LinearLayout的排列方向是vertical时,只有水平方向上的对齐方
  • android:layout_weight你真的会了吗?
    系统会首先计算LinearLayout下所有控件的android:layout_weight总值,然后每个控件所占大小的比例就是用该控件的android:layout_weight除以刚计算到的总值。例如,LinearLayout下有一个EditText控件,一个Button控件,如果想让EditText占据屏幕宽度的3/5,Button占据屏幕宽度的2/5,只需要将EditText的 layout_weight改成3,Button的 layout_weight改成2就可以了。

3.2.2 相对布局

相对布局(RelativeLayout)可以通过设置各个控件之间的相对位置让控件出现在布局的任何位置。

相对于父布局进行定位的常用xml属性:

  • android:layout_alignParentLeft 该控件是否在父布局的左侧
  • android:layout_alignParentRight 该控件是否在父布局的右侧
  • android:layout_alignParentTop 该控件是否在父布局的上侧
  • android:layout_alignParentBottom 该控件是否在父布局的下侧

相对于控件进行定位的常用xml属性:

  • android:layout_above 值为另一个控件的id,表明该控件在另一个控件的上方
  • android:layout_below 值为另一个控件的id,表明该控件在另一个控件的下方
  • android:layout_toLeftOf 值为另一个控件的id,表明该控件在另一个控件的左侧
  • android:layout_toRightOf 值为另一个控件的id,表明该控件在另一个控件的右侧
  • android:layout_alignLeft 值为另一个控件的id,表明该控件的左边缘另一个控件的左边缘对齐
  • android:layout_alignRight 值为另一个控件的id,表明该控件的右边缘另一个控件的右边缘对齐
  • android:layout_alignTop 值为另一个控件的id,表明该控件的上边缘另一个控件的上边缘对齐
  • android:layout_alignBottom 值为另一个控件的id,表明该控件的下边缘另一个控件的下边缘对齐

3.2.3 帧布局

帧布局(FrameLayout)所有的控件都默认摆放在布局的左上角。
帧布局也可以使用android:layout_gravity指定控件在布局中的对齐方式。

3.2.4 百分比布局

上面介绍的三种布局,我们发现只有LinearLayout支持使用layout_weight实现按比例指定控件大小的功能,其它两种布局都不支持。

因此Android为了使得相对布局和帧布局也支持按比例指定控件大小的功能,引入了PercentRelativeLayout和PercentFrameLayout两个布局,这两个布局是在RelativeLayout和FrameLayout的基础上去掉了使用wrap_content,match_parent等方式指定控件大小,而是允许直接指定控件在布局中所占的百分比

百分比布局属于新增布局,我们需要在build.grade中添加百分比布局的依赖就可以使用了。

  implementation "androidx.percentlayout:percentlayout:1.0.0"

在这里插入图片描述

百分比布局,常用xml属性:

  • app:layout_widthPercent 值为百分比,通过百分比指定控件宽度
  • -app:layout_heightPercent 值为百分比,通过百分比指定控件高度

3.2.5 布局使用小技巧

如果有个布局(比如标题栏)要在各个界面重复使用,我们就可以通过include将这个布局引入到各个界面。
在这里插入图片描述

3.3 创建自定义控件

3.3.1 常用控件与布局的继承结构

常用控件与布局的继承结构如下图所示。从图中可以看到所有控件都是直接或者间接的继承自View,所有布局都是直接或间接继承自ViewGroup。同理我们也可以利用上面的继承结构自定义我们的控件。
在这里插入图片描述

3.3.2 自定义标题栏控件

  1. 编写标题栏xml


  1. 自定义标题栏类继承自LinearLayout
public class TitleLayout  extends LinearLayout {public TitleLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);//获取并填充布局View view = LayoutInflater.from(context).inflate(R.layout.title, this);Button back = view.findViewById(R.id.title_back);Button edit=view.findViewById(R.id.title_edit);back.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "返回", Toast.LENGTH_SHORT).show();}});edit.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "编辑", Toast.LENGTH_SHORT).show();}});}
}

3.4 玩转ListView控件

ListView数据有众多子项构成,每个子项代表一行数据,用于展示较多的数据,它根据设备屏幕大小展示部分数据,剩余部分的数据通过用户下拉展示。

3.4.1 ListView控件简单案例(每项仅仅展示字符串)

  1. 定义布局


  1. 将ListView与待展示的数据进行绑定,完成数据展示
public class MainActivity extends AppCompatActivity {//1. 设置待展示的数据private String[] data={"苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//通过适配器,将ListView与待展示的数据进行绑定//android.R.layout.simple_list_item_1是Android内置简单xml布局,此处用于展示每项数据的布局ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,data);ListView listView = findViewById(R.id.list_view);listView.setAdapter(adapter);}
}

3.4.1 定制ListView子项数据的展示样式

子项数据的展示样式也是通过布局设定的,因此我们只需要编写Item布局样式,然后将该布局样式与ListView关联即可。

核心点:继承已有的适配器如ArrayAdapter,自定义适配器,并重写getView方法

假设我们ListView中每项包括水果名称及对应图片,定制ListViews过程如下:

  1. 自定义一个包含每项所需属性的实体类
public class Fruit {private String name;//水果名称private int imgID;//图片资源IDpublic Fruit(String name, int imgID) {this.name = name;this.imgID = imgID;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getImgID() {return imgID;}public void setImgID(int imgID) {this.imgID = imgID;}
}
  1. 编写每项数据展示的布局样式


  1. 定义适配器继承自ArrayAdapter,用于将ListView与待展示的每项数据进行绑定
public class FruitAdapter extends ArrayAdapter {/*** 展示每项数据的xml布局ID*/private int resourceID;public FruitAdapter(@NonNull Context context, int resource, @NonNull List objects) {super(context, resource, objects);this.resourceID=resource;}//该方法在每个子项被滚动到屏幕内的时候调用@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {//1. 获取该项数据Fruit fruit = getItem(position);//2. 获取该项用到的控件View view=LayoutInflater.from(getContext()).inflate(resourceID, parent, false);ImageView fruitImg = view.findViewById(R.id.fruit_img);TextView fruitName = view.findViewById(R.id.fruit_name);//3. 为每个控件设置相应的数据fruitImg.setImageResource(fruit.getImgID());fruitName.setText(fruit.getName());return view;}
}
  1. 使用自定义的ListView
public class MainActivity extends AppCompatActivity {private String[] data={"苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果","苹果"};private List fruits=new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化要展示的数据initFruits();//1. 自定义的适配器中传入上下文,子项布局,子项数据集FruitAdapter fruitAdapter = new FruitAdapter(this, R.layout.fruit_item, fruits);ListView listView = findViewById(R.id.list_view);//2. 将适配器添加到ListView中listView.setAdapter(fruitAdapter);//3. 定义点击每个子项时的事件listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView parent, View view, int position, long id) {Fruit fruit = fruits.get(position);Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();}});}private void initFruits(){for (String datum : data) {fruits.add(new Fruit(datum,R.mipmap.apple));}}
}

上面第3步定义的适配器中会重复加载布局,并且重复调用findViewById,这会影响ListView展示数据的效率,下面进阶的适配器类解决了以上问题。

public class FruitAdapter extends ArrayAdapter {/*** 展示每项数据的xml布局ID*/private int resourceID;public FruitAdapter(@NonNull Context context, int resource, @NonNull List objects) {super(context, resource, objects);this.resourceID=resource;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {//获取子项数据Fruit fruit = getItem(position);View view;ViewHolder viewTemp;//如果子项的布局未获取,则获取布局并临时保存布局中的子viewif (convertView==null){//获取布局view = LayoutInflater.from(getContext()).inflate(resourceID, parent, false);//获取布局中的控件并缓存到ViewHolder类中viewTemp=new ViewHolder();viewTemp.fruitImg=view.findViewById(R.id.fruit_img);viewTemp.fruitName=view.findViewById(R.id.fruit_name);//将缓存的布局中的控件通过setTag保存下来view.setTag(viewTemp);}//如果子项的布局已经获取,则直接拿到布局,不要重复获取else{//直接拿到布局,不要重复获取view=convertView;//通过getTag获取保存的布局中的控件viewTemp= (ViewHolder) view.getTag();}ImageView fruitImg = viewTemp.fruitImg;TextView fruitName = viewTemp.fruitName;//为子项中的控件设置相应的数据fruitImg.setImageResource(fruit.getImgID());fruitName.setText(fruit.getName());return view;}//包含子项所有控件的类,用于缓存对象class ViewHolder{ImageView fruitImg;TextView fruitName;}
}

3.5 玩转RecyclerView控件

ListView具有以下缺点:

  1. 需要开发者使用一些技巧提高它的效率,否则性能非常差
  2. 扩展性不好,如它只能实现纵向滚动,无法实现横向滚动

针对ListView的确定,Android提供了RecyclerView,它可以说是ListView的增强版,它本身对性能进行了内部优化,并且可以由用户来设定布局,可以方便实现横向滚动的效果等。

3.5.1 由于RecyclerView属于新增控件,我们需要引用这个控件

dependencies {//...implementation 'androidx.recyclerview:recyclerview:1.2.1'//...
}

3.5.2 设计每个Item的布局

如每个Item展示水果图片及名称



3.5.3 设计列表中每个Item所包含要素的类

如每项展示水果图片及图片名

package com.example.recyclerviewtest;public class Fruit {private String name;private int imgID;public Fruit(String name, int imgID) {this.name = name;this.imgID = imgID;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getImgID() {return imgID;}public void setImgID(int imgID) {this.imgID = imgID;}
}

3.5.4 定义适配器

首先适配器类继承自 RecyclerView.Adapter,其次在适配器类内定义内部类继承自RecyclerView.ViewHolder(这类似我们对ListView优化时的操作),然后在构造函数中传入数据,最后重写onCreateViewHolder(加载布局),onBindViewHolder(绑定数据),getItemCount(获取数据数量)三个方法。

public class FruitAdapter extends RecyclerView.Adapter {//继承RecyclerView.ViewHolder,为其添加列表属性static class ViewHolder extends RecyclerView.ViewHolder{ImageView fruitImage;TextView fruitName;public ViewHolder(@NonNull View itemView) {super(itemView);fruitImage = itemView.findViewById(R.id.fruit_img);fruitName = itemView.findViewById(R.id.fruit_name);}}private List fruitList;//构造函数中传入数据public FruitAdapter(List fruitList){this.fruitList=fruitList;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {//加载Item的布局View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item, viewGroup, false);//临时存储每个Item中的要素ViewHolder viewHolder = new ViewHolder(view);return viewHolder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {//获取指定位置的数据 Fruit fruit = fruitList.get(i);//设置数据viewHolder.fruitImage.setImageResource(fruit.getImgID());viewHolder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return fruitList.size();}
}

3.5.5 RecyclerView使用该适配器

开发者可以通过setLayoutManager控制展示数据的布局类型及方向

 @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits();RecyclerView recyclerView = findViewById(R.id.recycle_view);LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//通过设置RecyclerView的布局,可以控制滚动的方向recyclerView.setLayoutManager(linearLayoutManager);FruitAdapter fruitAdapter = new FruitAdapter(fruits);//通过适配器绑定数据recyclerView.setAdapter(fruitAdapter);}

3.5.6 RecyclerView点击事件

RecyclerView点击事件是在适配器类重写的onCreateViewHolder方法中注册,虽然它写法上比ListView稍复杂,但是它更加灵活,可以很方便的控制子项里的任意要素的点击。对第4步定义的适配器类进行改造,添加点击事件,示例代码如下

public class FruitAdapter extends RecyclerView.Adapter {//继承RecyclerView.ViewHolder,为其添加列表属性static class ViewHolder extends RecyclerView.ViewHolder{ImageView fruitImage;TextView fruitName;public ViewHolder(@NonNull View itemView) {super(itemView);fruitImage = itemView.findViewById(R.id.fruit_img);fruitName = itemView.findViewById(R.id.fruit_name);}}private List fruitList;//构造函数中传入数据public FruitAdapter(List fruitList){this.fruitList=fruitList;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {//加载Item的布局View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item, viewGroup, false);//临时存储每个Item中的要素ViewHolder viewHolder = new ViewHolder(view);//=============================新增点击事件 start=============================//为图片添加点击事件viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//获取当前点击图片的索引int position = viewHolder.getAdapterPosition();//根据索引获取数据Fruit fruit = fruitList.get(position);Toast.makeText(v.getContext(), "你点击了"+fruit.getName(), Toast.LENGTH_SHORT).show();}});//=============================新增点击事件 end=============================return viewHolder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {//获取指定位置的数据 Fruit fruit = fruitList.get(i);//设置数据viewHolder.fruitImage.setImageResource(fruit.getImgID());viewHolder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return fruitList.size();}
}

参考书籍:第一行代码

链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...