基本用法
- 想要使用这个控件,首先在项目的build.gradle中添加相应的依赖库才行
打开app/build.gradle文件,在dependencies闭包中添加
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
// 添加的是这一句
compile 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
- 添加完成后点击一下Sync Now来进行同步,然后修改activity_main.xml中的代码
添加一个控件,宽度和高度都是占满整部布局空间的
- 接下来定义一个自定义布局fruit_item.xml,这个时候注意layout_width="wrap_content"的设置,让它自适应大就可以了
4.定义一个实体类,作为RecyclerView适配器的适配类型,建立Fruit
public class Fruit {
private String name;
private int imageId;
public Fruit(String name,int imageId){
this.imageId = imageId;
this.name = name;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
- 为RecyclerView准备一个适配器,新建FruitAdapter类,让这个适配器继承RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder,其中ViewHolder是我们在FruitAdapter中定义的一个内部类
public class FruitAdapter extends RecyclerView.Adapter {
private List mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view){
super(view);
fruitImage=(ImageView)view.findViewById(R.id.img);
fruitName = (TextView)view.findViewById(R.id.text);
}
}
public FruitAdapter(List fruitList){
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
- 首先定义了一个内部类,ViewHolder要继承自
RecyclerView.ViewHolder
,然后在这个里面的构造函数中传入了一个View参数,这个参数通常就是RecyclerView子项的最外层布局,那么就可以通过这个findViewById()的方法获取到布局中的ImageVIew和TextView的实例了 - FruitAdapter中也有一个构造函数,这个函数用于将把要展示的数据源传递进来,并赋值给一个全局变量mFruitList,后面操作的就是这个数据源了
- 由于FruitAdapter继承自RecyclerView.Adapter,那么就必须重写 onCreateViewHolder(),onBindViewHolder(),getItemCount()这三个方法,
-
onCreateViewHolder()
方法用于创建一个ViewHolder实例,并把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回 -
onBindViewHolder()
方法用与对RecyclerView子项的数据进行赋值,会在每个子项滚动到屏幕内的时候执行,通过position参数可以获得当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextVIew中 -
getItemCount()
用于告诉RecyclerVIew一共有多少个子项,直接返回数据源的长度
- 接下来就可以使用RecyclerView,修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 用于初始化水果
initFruits();
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for (int i= 0;i<2;i++){
Fruit apple = new Fruit("Apple",R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana",R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("orange",R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("watermelon",R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("pear",R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("grape",R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("pineapple",R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("strawberry",R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("cherry",R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("mango",R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
- 使用了同样的initFruits()方法,用于初始化水果的数据,接着onCreate()方法获取到RecyclerView的实例,
- 创建一个LayoutManager用于指定RecycleView的布局方式,这里使用的是LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果,
- 接下来创建一个FruitAdapter的实例,将水果的数据传入到FruitAdapter的构造函数中
-
最后调用RecyclerView的setAdapter()方法来完成适配器的设置,这样RecyclerVIew和数据之间的关联就建立完成了
实现横向滚动和瀑布流布局
- 首先要对fruit_item布局进行修改,实现横向滚动,得把这里面的元素改成垂直排列的方式
- android:orientation="vertical"该成了垂直方向排列,而且宽度色设置为100dp,这样不会因为每种水果名字的长度不一而导致RecycleView的各个子项长短不一
- android:layout_gravity="center_horizontal" 都设置为水平居中
- 修改MainActivity中的代码
package com.example.md.recyclerview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 用于初始化水果
initFruits();
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 多添加这一句话
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
..............
}
- 相比之下,只多了一行代码,调用了layoutManager的setOrientation()方法来设置布局的排列方法,默认是纵向的,传入
LinearLayoutManager.HORIZONTAL
表示让布局横向排列
- 除了LinearLayoutManager之外,RecyclerView还提供了
GridLayoutManager
和StaggeredGridLayoutManager
这样中内置的布局排列方式,GridLayoutManager是用于网络布局,StaggeredGridLayoutManager用于瀑布布局
修改fruit_item.xml中的代码
只有几处调整,把宽度有100dp改成match_parent,因为瀑布的宽度是根据布局的列数自动匹配的,不是一个固定的值,还用margin属性,让子项之间留点间距,文字的对齐方式改成了左对齐
- 修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 用于初始化水果
initFruits();
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
// LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 多添加这一句话
// layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// 主要就是这一行代码,瀑布
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(
3, StaggeredGridLayoutManager.VERTICAL
);
// 网络
//GridLayoutManager layoutManager = new GridLayoutManager(this,4);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for (int i= 0;i<2;i++){
Fruit apple = new Fruit(getRandomLengthName("Apple"),R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"),R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("orange"),R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("watermelon"),R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("pear"),R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("grape"),R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("pineapple"),R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("strawberry"),R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("cherry"),R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("mango"),R.drawable.mango_pic);
fruitList.add(mango);
}
}
// 不想实现也可以不用写
private String getRandomLengthName(String name){
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for(int i = 0;i
- 首先在onCreate()方法中,创建了StaggeredGridLayoutManager的实例,它的构造函数接收了两个参数,第一个用于指定布局的列数,传入3表示会把布局分为3列,第二个参数用于指定布局的排列方向, StaggeredGridLayoutManager.VERTICAL表示是纵向排列,最后再把创建好的实例设置到RecyclerView中
-
这里使用了一个小技巧,在这个getRandomLengthName()方法上,这个方法用了Random对象来创造一个1到20之间的随机数,然后将参数中传入的字符重复随即便,在initFruits()方法中,每个水果的名字都调用这个方法来生成,这样就保证水果的名字的长短差距大,子项的高度就不一样了
-
若不写这个方法也是可以的,在传入的时候直接写入水果的名字也可以
-
当然了,还可以写网络布局,只需要创建一个GridLayoutManager实例就可以了,它的构造函数接收两个参数,第一个是上下文对象,第二个参数是一行显示几列数据
RecyclerView的点击事件
这个的注册监听器方法是需要我们自己给子项具体的View去注册点击事件的
- 修改FruitAdapter中的代码
public class FruitAdapter extends RecyclerView.Adapter {
private List mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
// 这里定义一个View变量
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view){
super(view);
// 保存最外层的布局实例
fruitView = view;
fruitImage = view.findViewById(R.id.img);
fruitName = view.findViewById(R.id.text);
}
}
public FruitAdapter(List fruitList){
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
//
final ViewHolder holder = new ViewHolder(view);
//分别为最外层的布局和Image注册点击事件
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取到position,然后拿到Fruit实例
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you click view"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you clcik image"+fruit.getImageId(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
- 先修改ViewHolder,在ViewHolder中添加fruitView变量来保存子项最外层布局的实例,然后在onCreateViewHolder()方法中注册点击事件
-
分别为最外层的布局和Image都注册了事件,先获取到用户点击的position,然后通过position拿到响应的Fruit实例,然后通过Toast显示
了解Nine-Patch
- 现在项目中有一个气泡状的图片,将这个图片设置为背景图片
这个时候可以看到图片被均匀的拉伸了,效果极差,这个时候就可以使用Nine-Patch来处理了
-
右击要添加的图片,点击Create 9-Patch file
-
就进入到这个页面
我们可以在四周绘制一个个小黑点,按住Shift键拖动就可以进行擦除,使用这个新的图片替换原来的,再运行程序就可以看到
- 这样,当图片需要拉伸的时候,就指定拉伸的区域,在外观上就好看了
编写聊天界面
- 首先使用到的是RecyclerView,所以首先进行在app/build.gradle中添加依赖库
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
// 添加这行代码
compile 'com.android.support:recyclerview-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
- 编写主页面,修改activity_main.xml
- 在主页面中放一个RecycleView用于显示聊天的消息内容,又放置了一个输入框,和一个按钮
- 定义消息的实体类,新建一个Msg
public class Msg {
// 接收到一条消息
public static final int TYPE_RECEVED = 0;
// 发出一条消息
public static final int TYPE_SENT = 1;
//消息内容
private String content;
// 消息类型
private int type;
public Msg(String content,int type){
this.content = content;
this.type = type;
}
public String getContent(){
return content;
}
public int getType(){
return type;
}
}
- Msg中只有两个字段,content表示消息的内容,type表示消息的类型,其中消息的类型有两个值可选,一个是TYPE_RECEVED表示接收到的数据,TYPE_SENT表示发送的数据
- 接下来编写RecyclerView的子项布局,新建msg_item.xml
- 接收到的消息左对齐,发送的消息右对齐,这个时候问题来了,怎么让收到的消息和发出的消息在一个布局中呢在这一个布局中,必定有一个控件是空白的在学基本控件的时候学到了可见属性,稍后在代码中可以根据这个消息的类型来决定隐藏和显示那种消息
- 编写自定义的MagAdapter适配器,和在RecyclerView写的适配器代码基本一样
public class MsgAdapter extends RecyclerView.Adapter {
private List mMsgList;
static class ViewHolder extends RecyclerView.ViewHolder{
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView lefMsg;
TextView rightMsg;
public ViewHolder(View itemView) {
super(itemView);
leftLayout = (LinearLayout)itemView.findViewById(R.id.left_layout);
rightLayout = (LinearLayout)itemView.findViewById(R.id.right_layout);
lefMsg = (TextView)itemView.findViewById(R.id.left_msg);
rightMsg = (TextView)itemView.findViewById(R.id.right_msg);
}
}
public MsgAdapter(List msgList){
mMsgList = msgList;
}
@Override
public MsgAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(MsgAdapter.ViewHolder holder, int position) {
Msg msg = mMsgList.get(position);
if (msg.getType() == Msg.TYPE_RECEVED){
// 如果是接收到的消息,则显示左边的消息布局,将右边的隐藏
holder.leftLayout.setVisibility(View.VISIBLE);
holder.rightLayout.setVisibility(View.GONE);
holder.lefMsg.setText(msg.getContent());
}else if (msg.getType() == Msg.TYPE_SENT){
// 如果是发送消息,那么显示右边的布局,把左边的布局隐藏
holder.rightLayout.setVisibility(View.VISIBLE);
holder.leftLayout.setVisibility(View.GONE);
holder.rightMsg.setText(msg.getContent());
}
}
@Override
public int getItemCount() {
return mMsgList.size();
}
}
- 修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List msgList = new ArrayList<>();
private EditText inputText;
private Button send;
private RecyclerView msgRecyclerView;
private MsgAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
// 初始化消息数据
initMsag();
inputText = (EditText)findViewById(R.id.edit_1);
send = (Button)findViewById(R.id.send);
// 获取到这个滚动控件
msgRecyclerView = (RecyclerView)findViewById(R.id.recyc_1);
// 指定这个控件的布局方式
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//设置到这个滚动控件中
msgRecyclerView.setLayoutManager(layoutManager);
// 数据传入到自定义的适配器中
adapter = new MsgAdapter(msgList);
msgRecyclerView.setAdapter(adapter);
//点击按钮的时候
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 获取到消息框中的内容
String content = inputText.getText().toString();
if (!"".equals(content)){
Msg msg = new Msg(content,Msg.TYPE_SENT);
msgList.add(msg);
// 当有新的消息的时候刷新ListView中的显示
adapter.notifyItemInserted(msgList.size()-1);
// 将ListView定位到最后一行
msgRecyclerView.scrollToPosition(msgList.size()-1);
// 清空输入框中的内容
inputText.setText("");
}
}
});
}
public void initMsag(){
Msg msg = new Msg("hello pony",Msg.TYPE_RECEVED);
msgList.add(msg);
Msg msg1 = new Msg("hello Tom",Msg.TYPE_SENT);
msgList.add(msg1);
Msg msg2 = new Msg("我们去哪里呀?",Msg.TYPE_RECEVED);
msgList.add(msg2);
}
}
- 在initMsag()中初始化了几条数据,用于在RecyclerView中显示
- 在点击按钮的时候,如果内容不为空,就创建了一个Msg对象,并且添加到了msgList中去
- 然后调用适配器的notifyItemInserted()方法,用于通知列表中有新的数据插入,这样新增的一条消息才能够在RecyclerVIew中显示,
- 接着调用RecyclerView的scrollToPosition()方法,将数据定位到最后一行,
-
最后调用setTExt()方法将输入的内容清空
参考数据:第一行代码
若有错误请指出