LinearLayout又称线性布局,这个布局会将它所包含的控件在线性方向上依次排列。android:orientation 属性指定排列方向,vertical是垂直方向,horizontal是水平方向。
注意:如果LinearLayout的排列方向是horizontal。内部控件就绝对不能将宽度指定为math_parent,这样的话单独一个控件就会将水平方向占满,同样,若是vertical,就不能将高度指定为math_parent.
补充: android:gravity用于指定文字在控件中的对齐方式
android:layout_gravity用于指定控件在布局中的对齐方式。
可以通过相对定位的方式让控件出现在布局的任何位置。
1.相对父局:
android:layout_alignParentTop
android:layout_alignParentRight
android:layout_alignParentLeft
android:layout_alignParentBottom
android:layout_centerInParent
2:相对控件:
android:layout_above:让一个控件位于另一个控件上方,需要为这个属性指定相对控件id的引用,其他类似。
android:layout_below
android:layout_toLeftOf
android:layout_toRightOf
3.另外一组相对于控件进行定位的属性:
android:layout_alignLeft:表示让一个控件的左边缘和另一个控件的左边缘对齐。
android:layout_alignRight
android:layout_alignTop
android:layout_alignBottom
这种布局没有方便的定位方式,所有的控件都默认放在布局的左上角
这种布局中我们可以不再使用wrap_content,math_parent,等方式来指定控件的大小,而是允许直接指定控件在布局中占的百分比。由于LinearLayout本身已经支持按比例指定控件的大小,因此百分比布局只为FrameLayout和RelativeLayout进行了功能拓展,提供了PercentFrameLayout和PercentRelativeLayout这两个全新布局。
在dependencies闭包中添加如下内容
implementation 'androidx.percentlayout:percentlayout:1.0.0'
然后修改activity_main.xml中的代码
<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">
<Button
android:id="@+id/button1"
android:text="Button 1"
android:layout_gravity="left|top"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"/>
<Button
android:id="@+id/button2"
android:text="Button 2"
android:layout_gravity="right|top"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"/>
<Button
android:id="@+id/button3"
android:text="Button 3"
android:layout_gravity="left|bottom"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"/>
<Button
android:id="@+id/button4"
android:text="Button 4"
android:layout_gravity="right|bottom"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"/>
androidx.percentlayout.widget.PercentFrameLayout>
最外层使用了PercentFrameLayout,由于百分比布局并不是内置在系统SDK当中的,所以需要把完整的包名写出来。然后还必须定义一个app的命名空间,这样才能使用百分比布局的自定义属性。
不过PercentFrameLayout还是会继承FrameLayout的特性,即所有的控件默认都是摆放在布局的左上角。
常用控件和布局的继承结构
所有的控件都是直接或间接继承自View的,所有的布局都是直接或间接继承自ViewGroup。View是Android中最基本的一种UI组件,可以在屏幕上绘制一块矩形区域,并能相应这块区域的各种事件,因此我们使用的各种控件其实就是在View的基础上又添加了各自特有的功能。而ViewGroup则是一种特殊的View,可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器。
如果系统的控件并不能满足我们时,我们可以利用上面的继承结构来创建自定义控件。
创建一个新项目,新建一个布局title.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#28af63">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title_back"
android:text="Back"
android:layout_gravity="center"
android:layout_margin="5dp"
android:textAllCaps="false"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/title_text"
android:layout_gravity="center"
android:gravity="center"
android:text="Title Text"
android:textColor="#fafafa"/>
<Button
android:id="@+id/title_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:textAllCaps="false"
android:text="Edit"/>
LinearLayout>
修改activity_main.xml中的代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/title"/>
androidx.constraintlayout.widget.ConstraintLayout>
只需要通过一行include语句将标题栏布局引入进来就可以了。
最后在MainActivity中将系统自带的标题栏隐藏掉
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
if(actionBar!=null){
actionBar.hide();
}
}
}
调用了getSupportActionBar方法获得ActionBar的实例,然后再调用ActionBar的hide()方法将标题栏隐藏起来。
引入布局的技巧确实解决了重复编写布局代码的问题,如果布局中要求一些控件能够响应事件,我们还需在每个活动中为这些控件单独编写一次事件注册的代码,无疑会增加很多代码,这些情况最好使用自定义控件来解决。
新建TitleLayout继承自LinearLayout,让他成为我们自定义的标题栏控件
public class TitleLayout extends LinearLayout {
public TitleLayout(Context context, AttributeSet attrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.title,this);
}
}
首先我们重写了LinearLayout中带有两个参数的构造函数,在布局中引入TitleLayout控件就会调用这个构造函数。然后在这个函数中需要对标题栏布局进行动态加载,要用LayoutInflater来实现**,通过LayoutInflater的from()方法可以构建出一个LayoutInflater对象**,然后调用inflate()方法就可以动态加载一个布局文件,inflate()接受两个参数,一个参数是要加载布局文件的id,这里传入R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout,直接传入this。
现在自定义控件已经创建好了,然后我们需要在布局文件中添加这个自定义控件,activity_main.xml中的代码如下:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.uicustomviews.TitleLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
androidx.constraintlayout.widget.ConstraintLayout>
需要注意,在添加自定义控件时我们需要指明控件的完整类名,包名在这里不可以省略。
然后为标题栏中的按钮注册点击事件,修改TitleLayout中的代码:
public class TitleLayout extends LinearLayout {
public TitleLayout(Context context, AttributeSet attrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.title,this);
Button titleBack = (Button) findViewById(R.id.title_back);
Button titleEdit = (Button) findViewById(R.id.title_edit);
titleBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
((Activity) getContext()).finish();
}
});
titleEdit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getContext(),"you click Edit button",Toast.LENGTH_SHORT).show();
}
});
}
}
先通过findViewById()方法得到按钮的实例,然后分别调用setOnClickListener()方法给两个按钮注册了点击事件,当点击返回时销毁当前活动,当点击编辑时弹出一段文本。运行效果如下:
可以称为是Android中最常用的控件之一,允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时,屏幕上原有的数据会滚动出屏幕。
新建项目,修改activity_main.xml中的代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view"/>
androidx.constraintlayout.widget.ConstraintLayout>
接下来修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private String[] fruit = {"apple","banana","orange","apple","banana","orange","apple","banana",
"orange","apple","banana","orange","apple","banana","orange"};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.list_view);
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, fruit);
listView.setAdapter(adapter);
}
}
简单定义一个fruit数组来测试。不过数组中的数据是无法直接传递给ListView的,我们需要借助适配器来完成。Android中提供了很多适配器的实现类,其中ArrayAdapter,可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入。在ArrayAdapter的构造函数中依次传入当前上下文,ListView子项布局的id,以及要适配的数据。这里使用了android.R.layout.simple_list_item_1作为ListView子项布局的id,这是一个Android的内置的布局文件,里面只有一个TextView,可用于简单地显示一段文本,这样适配器对象就构建好了。最后还需要调用ListView的setAdapter()方法,将构建好的适配器对象传递进去,这样ListView和数据之间的关联就建立完成了。
可以事先准备一组图片,这里为了方便没有准备。
接着定义一个实体类,作为ListView适配器的适配类型。新建类Fruit
public class Fruit{
private String name;
private int imageId;
public Fruit(String name,int imageId){
this.name=name;
this.imageId=imageId;
}
public int getImageId() {
return imageId;
}
public String getName() {
return name;
}
}
然后需要为ListView的子项指定一个我们自定义的布局,在layout目录下新建fruit.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_image"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_name"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
LinearLayout>
在这个布局里,定义ImageView用于显示水果图片,定义一个TextView用于显示水果名称,并让TextView在垂直方向上居中显示。
接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类,新建类FruitAdapter
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> object){
super(context,textViewResourceId,object);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitName.setText(fruit.getName());
fruitImage.setImageResource(fruit.getImageId());
return view;
}
}
FruitAdapter重写了父类的一组构造函数,用于将上下文,ListView的子项布局的id和数据都传递进来,另外又重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在getView()方法中,首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为这个子项加载我们传入的布局。
这里LayoutInflater的inflate()方法接收三个参数,第三个false,表示只让我们在父布局中声明的layout属性生效,但不会为这个View添加父局,因为一旦View有了父局之后,就不能再添加到ListView中了。
接下来调用View的findViewById()方法分别获取到ImageView和TextView的实例,并分别调用他们的setImageResource()和setText()方法来设置显示的图片和文字,最后将布局返回,这样自定义的适配器就完成了。
下面修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
ListView listView = (ListView) findViewById(R.id.list_view);
FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit,fruitList);
listView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<10;i++){
Fruit apple = new Fruit("apple",R.drawable.ic_launcher_background);
fruitList.add(apple);
}
}
}
目前的运行效率还是很低的,在FruitAdapter的getView()方法中,每次都将布局重新加载了一遍,当ListView快速滚动时,就会成为性能的瓶颈。
getView()方法中还有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用,修改FruitAdapter中的代码
@Override
......
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit = getItem(position);
View view;
if(convertView==null){
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
}else
view=convertView;
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
在getView()方法中进行了判断,如果convertView为null,则使用LayoutInflater去加载布局,如果不为null,则直接对convertView进行重用。这样就大大提升了ListView的效率。
虽然现在已经不会重复加载布局,但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例,我们可以借助一个ViewHolder来对这部分性能进行优化,修改FruitAdapter中的代码
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if(convertView==null) {
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder); //将ViewHolder存储在View中
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();//重新获取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView控件为null的时候,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里,然后调用View的setTag()方法,将ViewHolder对象存储在View中,当convertView不为null时,则调用View的getTag()方法,把ViewHolder重新取出,这样所有的控件的实例都缓存在了ViewHolder里,每次没有必要通过findViewById()方法来获取控件实例了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
ListView listView = (ListView) findViewById(R.id.list_view);
FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit,fruitList);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
}
使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击ListView的任何一个子项时,就会回调setOnItemClick()方法,这个方法中可以通过position参数判断用户点击的是哪一个子项,然后获得相应的水果,并通过Toast将水果的名字显示出来。
RecyclerView是一个增强版的ListView,和百分比布局类似,也属于新增的控件,首先需要在项目的build.gradle中添加相应的依赖库。
implementation 'com.android.support:recyclerview-v7:24.2.1'
然后修改activity_main.xml中的代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycle_view"/>
androidx.constraintlayout.widget.ConstraintLayout>
由于RecyclerView并不是内置在SDK当中,所以需要把完整的路径写出来。
将Fruit类和fruit.xml复制过来
接下来为RecyclerView准备一个适配器,新建FruitAdapter类,并让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder.其中ViewHolder是我们在FruitAdapter中定义的一个内部类。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view){
super(view);
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList){
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder,int position){
Fruit fruit = mFruitList.get(position);
holder.fruitName.setText(fruit.getName());
holder.fruitImage.setImageResource(fruit.getImageId());
}
@Override
public int getItemCount(){
return mFruitList.size();
}
}
首先定义了一个内部类ViewHolder,继承自RecyclerView.ViewHolder,然后ViewHolder的构造函数中传入一个View参数,这个参数通常就是RecycleView子项的最外层布局,然后可以通过findViewById()方法获取到布局中的ImageView和TextView的实例了。
接着往下FruitAdapter中也有一个构造函数,这个方法用于把要展示的数据源传递过来赋值给一个全局变量mFruitList,之后的操作都会在这个数据源的基础上进行。
由于FruitAdapter继承RecyclerView.Adapter,所以必须重写onCreateViewHolder(),onBindViewHolder,getItemCount()这三个方法。onCreateViewHolder()用于创建ViewHolder实例,在这个方法中将fruit布局加载出来,然后创建一个ViewHolder的实例,并把加载出来的布局传入到构造函数当中,最后将ViewHolder的实例返回。onBindViewHolder()方法是用于对RecycleView子项的数据进行赋值,会在每个子项滚动进屏幕执行,通过position获得当前Fruit的实例,然后在将数据设置到ViewHolder的ImageView和TextView。getItemCount()用于告诉RecycleView一共有多少个子项,直接返回数据源的长度。
适配器准备好之后,修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<10;i++){
Fruit apple = new Fruit("apple",R.drawable.ic_launcher_background);
fruitList.add(apple);
Fruit banana = new Fruit("banana",R.drawable.ic_launcher_foreground);
fruitList.add(banana);
}
}
}
先获取RecyclerView的实例,然后创建了一个LinearLayoutManager对象,并把它设置在RecyclerView里。LayoutManager用于指定布局方式,这里使用LinearLayoutManager是线性布局的意思,可以实现和ListView一样的效果。然后创建FruitAdapter实例,将水果数据传入到构造函数当中,最后调用RecycleView的setAdapter()方法来完成适配器设置。这样RecyclerView和数据之间的关联就建立完成了。
运行效果和之前一样。
要实现横向滚动,应该把fruit里的元素改成垂直排列
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/fruit_image"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_name"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"/>
LinearLayout>
因为每种水果的文字长度不一样,把宽度设成100dp,为了美观,然后将ImageView和TextView都设置成了水平居中,并使用layout_marginTop属性让文字和图片之间保持距离。
接下来修改MainActivity中的代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
只需加入一行代码,调用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列,我们传入LinearLayoutManager.HORIZONTAL,表示让布局横行排列。
ListView的布局排列是由自身去管理,RecycleView将这个工作交给了LayoutManager,LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能制定出不同排列方式的布局。
还有GridLayoutManager可用于实现网格布局,StaggeredGridLayoutManager可用于实现瀑布流布局。
实现瀑布流布局,修改fruit中的代码,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/fruit_image"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_name"
android:layout_gravity="left"
android:layout_marginTop="10dp"/>
LinearLayout>
接着修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<10;i++){
Fruit apple = new Fruit(getRandomLengthName("apple"),R.drawable.ic_launcher_background);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("banana"),R.drawable.ic_launcher_foreground);
fruitList.add(banana);
}
}
private String getRandomLengthName(String name){
Random random = new Random();
int length = random.nextInt(20)+1;
StringBuilder builder = new StringBuilder();
for(int i=0;i<length;i++){
builder.append(name);
}
return builder.toString();
}
}
首先创建StaggeredGridLayoutManager的实例,StaggeredGridLayoutManager的构造函数接收两个参数,第一个用于指定布局的列数,第二个用于指定布局的排列方向,最后把创建好的实例设置到RecyclerView中。 getRandomLengthName(),这个方法使用Random对象来创造一个1到20的随机数,然后将参数中传入的字符串重复几遍。效果如下
RecyclerView没有类似与setOnItemClickListener()这样的注册监听器方法,需要我们自己给子项具体的View去注册点击事件。
修改FruitAdapter中的代码
......
static class ViewHolder extends RecyclerView.ViewHolder{
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view){
super(view);
fruitView = view;
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList){
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit,parent,false);
final ViewHolder holder = new ViewHolder(view);
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(view.getContext(),"you clicked view"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(view.getContext(),"you clicked image"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
......
在ViewHolder中添加fruitView变量来保存子项最外层布局的实例然后在onCreateViewHolder()方法中注册点击事件,这里分别为最外层布局和ImageView都注册了点击事件在点击事件中先获取了用户点击的position,然后通过position拿到相应的Fruit实例,再使用Toast弹出两种不同的内容以示区别。
点击不同的地方弹出的内容不同,效果如下