《第一行代码》第三章内容的整理。
第三章主要是控件的开发,布局的理解,是安卓的基础部分。
目录
注册监听器
一种是声明为内部类,但是Java规定,内部类只能访问外部类中的成员变量,不能访问外部方法中的变量。解决方法是将方法中的变量声明为final全局变量。但是内部类虽然可以达到效果但是看起来界面不好看。
另一种是实现接口的方式来注册,让此类实现View.OnClickListener接口,然后在类中重写OnClick方法。
- 变量的声明:
在类中
private Button button;
private EditText edit;
在onCreate方法中
button=(Button) findViewById(R.id.button);
edit=(EditText) findViewById(R.id.edit_text);
点击事件
switch (v.getId())
{
case R.id.button:
String data=edit.getText().toString();
Toast.makeText(this,data,Toast.LENGTH_SHORT).show();
break;
default:
break;
}
- Android的ID都是数值,因此可以用数组存储。
4种基本布局:
布局是一种可以放置很多控件的容器,它可以按照一定的规律调整内部控件的位置,从而可以编写出精美的页面。布局还可以放置布局,形成布局的嵌套。
线性布局 LinearLayout
以下两个属性不可缺少
- android:layout_width
- android:layout_height
使用了android:layout_weight之后,在
android:orientation="vertical"中,android:layout_height就不再管用了。
android:orientation="horizontal中,android:layout_width就不再管用了。
-
只有线性布局支持layout_weight。指定成0dp是一种规范的写法。dp是Android中用来指定空间大小,间距等属性的单位。
-
Button的大小一般都声明为wrap_content这样有更好的适配特性。
相对布局RelativeLayout,它可以通过相对定位的方式让控件出现在布局的任何位置,该布局虽然繁琐,但都是有规律可循的。
帧布局FrameLayout
这种布局没有方便的定位方式,所有的空间都会默认摆放到布局的左上角,由于定位方式的欠缺(与LinearLayout的layout_gravity相同),所以它的应用场景比较少。
百分比布局
由于LinearLayout本身已经支持按比例指定控件的大小了,因此百分比布局职位FrameLayout和RelativeLayout进行了功能拓展。提供了PercentFrameLayout和PercentRelativeLayout这两个全新的布局。
百分比布局是新增布局,需要在build.gradle中添加百分比布局库的依赖。
添加依赖的时候代码是
implementation 'com.android.support:percent:28.0.0'
书上的代码已经过时了,另外这个28.0.0应该和 implementation 'com.android.support:appcompat-v7:28.0.0'的号码一致。(因为电脑问题,未实现)
创建自定义控件
我们所用的所有的控件都是直接或者间接继承自view的,所用的所有布局直接或间接继承自ViewGroup的。
自定义的控件,也就是layout的文件,写完了之后可以被到处应用。比如在主界面,那么主界面就可以用
<include layout="@layout/name"/>引用完成。
另外一种添加的方法是新建一个类继承LinearLayout,随后在这个类中调用父类构造函数,参数分别是Context和Attribute类型的,随后需要对这个类进行动态加载,
LayoutInflater.from(context).inflate(R.layout.文件名,this);
【动态加载借助LayoutInflater来实现,通过调用其from方法(参数为context)可以构建出一个LayoutInflater的对象,然后在调用inflate方法就可以动态加载布局了,它有多个参数,一是要加载的文件的id,二是给加载好的布局再添加的一个父布局】,有时候会有第三个参数,例如后面讲的ListVIew和RecyclerView
随后便可以在这个类中为控件编写事件了。
ListView控件(已被RecyclerView取代)
声明控件首先正常声明,不过在表示的时候,需要在Activity中声明数组来储存一项一项的数据(把数据提供好),数组中的数据是无法直接传给ListView的,安卓提供了许多适配器实现类,最好用的是ArrayAdapter,泛型需要你指定要传的是什么数据类型。它的构造函数
- Context
- android.R.layout.simple_list_item_1
- 数组名
其中android.R.layout.simple_list_item_1作为ListView的子项布局id,这是一个Android内置的布局文件,里面只有一个TextView,可以简单地显示一段文本。
最后实例化ListView,对象调用setAdapter(adapter);
定制ListView文件
这是本章的难点和终点,是Android的重中之重!必须牢牢掌握。现在官方不推荐ListView了,转而推荐RecycleView。
首先ListView在布局文件中的声明是很简单的,注意使它的
android:layout_width="match_parent"
android:layout_height="match_parent”均为match_parent就可以,这样ListView便可以充满整个屏幕。
两种方法使用ListView
- 仅仅展示文字
声明一个String类型的数组,这里要引入适配器的概念
适配器是一个连接数据和视图的桥梁,适配器就像显示器,把复杂的东西按人可以接受的方式来展现。
不懂没有关系,因为声明是死的。ArrayAdapter是泛型结构。
ArrayAdapter<String> adapter=new ArrayAdapter<String>(context,android.R.layout.simple_list_item1,字符串数组);
其中android.R.layout.simple_list_item1是系统提供的内置布局。我们待会自定义的时候会把它改成我们所需要的。
实例化ListView,最后通过ListView的setAdapter()方法绑定ArrayAdapter。(listview.setAdapter(adapter);)
- 定制ListView页面
这一节就开始有难度了,对ListView进行定制,展示更丰富的内容,例如图片+文字。(跳过)
RecyclerView
RecyclerView比ListView更加的强大,Android推荐使用这项技术。
- 引入
Android希望更多的设备都可以用上,所以讲RecyclerView定义在了build.gradle中,打开app目录下的build.gradle,在dependciew闭包中添加
implementation 'com.android.support:recyclerview-v7:28.0.0'
随后同步更新build.gradle(需要联网)
- 声明
在布局中加入(所有通过build.grade添加的功能据需如此声明)
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
- 步骤
①首先声明一个普通类,这个类包含着文字和图片,接收器等等。
package com.example.recyclerview;
import android.widget.FrameLayout;
public class Fruit {
private String ImageName;
private int ImageId;
public Fruit(String Imagename,int ImageId)
{
this.ImageId=ImageId;
this.ImageName=Imagename;
}
public String getImageName()
{
return ImageName;
}
public int getImageId()
{
return ImageId;
}
}
②为RecyclerView准备一个适配器
package com.example.recyclerview;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> data;
static class ViewHolder extends RecyclerView.ViewHolder
{
View fruitView;
ImageView imageView;
TextView textView;
public ViewHolder(View view)
{
super(view);
fruitView=view;
imageView=(ImageView) view.findViewById(R.id.image);
textView=(TextView) view.findViewById(R.id.text);
}
}
public FruitAdapter(List<Fruit> list)
{
data=list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_list,parent,false);
final ViewHolder holder=new ViewHolder(view);
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
Fruit fruit=data.get(position);
Toast.makeText(v.getContext()," 点击了"+fruit.getImageName(),Toast.LENGTH_SHORT).show();
}
});
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
Fruit fruit=data.get(position);
Toast.makeText(v.getContext()," 点击了图片"+fruit.getImageName(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
Fruit fruit=data.get(i);
viewHolder.imageView.setImageResource(fruit.getImageId());
viewHolder.textView.setText(fruit.getImageName());
}
@Override
public int getItemCount() {
return data.size();
}
}
该类继承自RecyclerView.Adapter,泛型为FruitAdapter.ViewHolder,其中ViewHolder为我们声明的一个静态内部类,继承自RecyclerView.ViewHolder。另外需重写以下三种方法
onCreateViewHolder,
onBindViewHolder
getItemCount
先看静态内部类在这个类中首先先声明ImageView和TextView对象,并声明一个View fruitView为点击事件做铺垫。它的构造函数的参数为View类型的,构造函数必须首先super(view),随后实例化刚才声明的两个对象并把参数view传给fruitView。
内部类结束后,需要忙活主类的构造函数,他的参数是一个List类型为我们刚声明的类。把参数传给主类的私有List。
接下来重写三种方法,最简单的是重写getItemCount方法,只要返回私有List的size即可。
重些onCreatViewHolder方法:这个类的意思是要创建视图。用于创建VIewHolder实例,我们把子项的布局加载进来,
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_list,parent,false);
并把加载后的布局传到构造函数中,
final ViewHolder holder=new ViewHolder(view);
随后就可以返回holder了,但是为了点击事件,还需要接着做。
当我们点击图片的时候,我们希望他能给我们一个交互,这个图片在holder中,所以我们为ImageView的对象添加监听事件。
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
Fruit fruit=data.get(position);
Toast.makeText(v.getContext()," 点击了"+fruit.getImageName(),Toast.LENGTH_SHORT).show();
}
});
敲到new View.OnClickListener时,AS会自动补齐,接下来在监听事件里写就可以了。怎么确认点击的是哪个选项呢?利用VIewHolder的getAdapterPosition拿到用户点击的position(这个子项的position是死的,跟List里的位置一一对应),然后通过position拿到相应的Fruit的实例。
重写onBindViewHolder这个方法是用来对RecyclerView子项的数据进行赋值,会在子项滚动到屏幕内的时候执行,它的参数一个是VIewHolder,一个是position。我们用position可以拿到Fruit的实例,随后将数据设置到ViewHolder中的ImageView和TextView中即可。
③在MainActivity中实现。
首先实例化RecyclerView自不必说,然后传给适配器相应的List文件,并且用RecyclerView的实例去
recyclerView.setAdapter(adapter);
RecyclerView的好处是可以随便更改滑动的样式,所以需要LayoutManager来制定RecyclerView的布局方式。线性布局声明如下
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
瀑布方式声明如下
StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);//3代表3列
recyclerView.setLayoutManager(layoutManager);