recyclerview实战——对话界面

Posted mask-d

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了recyclerview实战——对话界面相关的知识,希望对你有一定的参考价值。

 

最近在做一个电影智能问答系统,需要有一个相应的界面来操作问答,而最近又在学android,使用决定采用recyclerview控件来写相关的内容。

android本身有一种相关的控件叫listview,但是相比recyclerview来说,Listview只能实现上下滑动而且扩展性不好。重要的是recyclerview可以实现复用,即已经移出屏幕的样式会在屏幕的下方进行复用。下面开始进行相关的操作。

一 前期准备

1 对话框素材

开始实现前需要有一个对话框,从icon网站上找到一个对话框png格式,背景透明作为所用的对话框。

技术分享图片

 

将其导入drawable之后转换为 9 patch格式,可以确保放大的时候图像部分放大,从而确保不失真不变形。(改变图片左边和上边的黑色条来确定哪里被拉伸)

技术分享图片

2 建立recyclerview依赖

因为recyclerview是新增空间,需要在项目的build.gradle中增加其依赖,注意此处的build.gradle的地址为appuild.gradle.在该文件下增加recyclerview依赖如下:

 1 dependencies {
 2     implementation fileTree(dir: libs, include: [*.jar])
 3     implementation com.android.support:appcompat-v7:26.1.0
 4     implementation com.android.support.constraint:constraint-layout:1.1.2
 5     testImplementation junit:junit:4.12
 6     androidTestImplementation com.android.support.test:runner:1.0.2
 7     androidTestImplementation com.android.support.test.espresso:espresso-core:3.0.2
 8 
 9 //增加recyclerview依赖
10     compile com.android.support:recyclerview-v7:26.1.0
11 }

新增后记得点击sync now来进行同步。

二 布局文件

1 主界面

首先写主界面,主界面其实有两大部分组成,第一部分是上面的recyclerview控件,存放发出和接收的消息。第二部分是位于底部的输入部分,其中包括editview控件(输入框)和button控件(发送按键)两个小部分。所以整体用线性布局将屏幕分为上下两部分,上部分高度设为0,权重为1,下部分高度设为适应高度。下部分再使用一个线性布局(默认为横向线性),将输入框设宽为0,权重为1;将按钮设为适应宽度,这样就可以很好的分屏了。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:background="@drawable/bg">
 7 
 8 //上半部分的滑动列表
 9     <android.support.v7.widget.RecyclerView
10         android:id="@+id/msg_recycler_view"
11         android:layout_width="match_parent"
12         android:layout_height="0dp"
13         android:layout_weight="1"/>
14 
15 //下部分的输入
16     <LinearLayout
17         android:layout_width="match_parent"
18         android:layout_height="wrap_content">
19 
20         <EditText
21             android:id="@+id/input_text"
22             android:layout_width="0dp"
23             android:layout_height="wrap_content"
24             android:layout_weight="1"
25             android:hint="输入问题"
26             android:maxLines="2"/>
27 
28         <Button
29             android:id="@+id/send"
30             android:layout_width="wrap_content"
31             android:layout_height="wrap_content"
32             android:text="Send"/>
33 
34     </LinearLayout>
35 
36 </LinearLayout>

效果如下图:

技术分享图片

这里布局的时候要善用weight(权重)这个属性。

比如说有一个linearlayout他的高度是100

 a控件weight=1   b控件weight=2,c控件weight=3,d控件height=40,那么

a控件的高度就是 ((100-40)/(1+2+3)) *1

b控件的高度就是 ((100-40)/(1+2+3)) *2

c控件的高度就是 ((100-40)/(1+2+3)) *3

如果a不设置height=0dp,那么当a控件高度大于((100-40)/(1+2+3)) *1时,weight属性不起作用,设置等于0,那么weight属性什么时候都起作用。

2 对话框布局

对话框布局用在recyclerview布局里,即使recyclerview子项的布局。

将左右对话框的布局集合在一个布局里,再通过setVisiblity中view.visible和gone来设置布局可见不可见。

整体布局任为线性布局,分为两部分,分别是左对话框布局和右对话框布局,设置一个边框内填充的距离padding。左右对话框分别也是线性布局,里面包含一个textview文本框。左对话框设置背景图为准备好的左对话框素材,属性layout_gravity为lift 使其靠左布局,大小设置为适应性大小。里面的文本框也是适应性大小,布局属性为居中,设置一个边外框边界margin使字不会填充太满,字体颜色为黑色。右对话框和左对话框对称。代码如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="wrap_content"
 4     android:orientation="vertical"
 5     android:padding="10dp">
 6 
 7     <LinearLayout
 8         android:id="@+id/left_layout"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_gravity="left"
12         android:background="@drawable/left">
13 
14         <TextView
15             android:id="@+id/left_msg"
16             android:layout_width="wrap_content"
17             android:layout_height="wrap_content"
18             android:layout_gravity="center"
19             android:layout_margin="10dp"
20             android:textColor="#000000" />
21 
22     </LinearLayout>
23 
24     <LinearLayout
25         android:id="@+id/right_layout"
26         android:layout_width="wrap_content"
27         android:layout_height="wrap_content"
28         android:layout_gravity="right"
29         android:background="@drawable/right">
30 
31         <TextView
32             android:id="@+id/right_msg"
33             android:layout_width="wrap_content"
34             android:layout_height="wrap_content"
35             android:layout_gravity="center"
36             android:layout_margin="10dp"
37             android:textColor="#000000"
38             />
39 
40     </LinearLayout>
41 
42 </LinearLayout>

示例图如下:

技术分享图片

注意这里有一个坑,因为是直接从icon下载的素材,而且对话框的宽高为适应性大小,这个适应不但是适应里面的文字框大小,而且还适应背景图本身的大小,这就导致了如果输入文字很少,文本框就会远大于文字大小。解决方法:在ps里将素材进行重新的处理,将其缩小到60*60像素点左右大小,再通过9 patch转化图片,这样效果就很好了。

三 定义消息实体类 Message类

新建Message类,包括两个字段,content表示消息的内容,type表示消息的类型(TYPE_RECEIVED接收的消息 和TYPE_SEND发送的消息 )

 1 public class Message {
 2     //    设置收到消息类型为0 发送信息类型为1
 3     public static final int TYPE_RECEIVED = 0;
 4     public static final int TYPE_SEND = 1;
 5 
 6     private String content;
 7     private int type;
 8 
 9     //构造函数赋值
10     public Message(String content, int type) {
11         this.content = content;
12         this.type = type;
13     }
14 
15     public String getContent() {
16         return content;
17     }
18 
19     public int getType() {
20         return type;
21     }
22 }

四 Recyclerview适配器类

新建MsgAdapter类,代码如下:

 1 public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
 2 
 3     //展示数据源存放
 4     private List<Message> mMsgList;
 5 
 6     //内部类 将子项的所有布局进行传入匹配
 7     static class ViewHolder extends RecyclerView.ViewHolder {
 8         //       布局元素
 9         LinearLayout leftLayout;
10         LinearLayout rightLayout;
11         TextView leftMsg;
12         TextView rightMsg;
13 
14         //        内部类的构造函数传入recycler子项的布局
15         public ViewHolder(View view) {
16             super(view);
17             leftLayout = (LinearLayout) view.findViewById(R.id.left_layout);
18             rightLayout = (LinearLayout) view.findViewById(R.id.right_layout);
19             leftMsg = (TextView) view.findViewById(R.id.left_msg);
20             rightMsg = (TextView) view.findViewById(R.id.right_msg);
21         }
22     }
23 
24     //    传入要展示的数据源
25     public MsgAdapter(List<Message> msgList) {
26         mMsgList = msgList;
27     }
28 
29     //    用于创建内部类viewholder实例 传入msg_item布局,在构造函数加载出所有布局
30     @Override
31     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
32         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item, parent, false);
33         return new ViewHolder(view);
34     }
35 
36     //    对recyclerview子项数据进行赋值,在子项滚动到屏幕内时执行。通过position参数得到当前项具体内容
37     @Override
38     public void onBindViewHolder(ViewHolder holder, int position) {
39         Message msg = mMsgList.get(position);
40         if (msg.getType() == Message.TYPE_RECEIVED) {
41             //收到消息,用左边的格式 右边格式隐藏
42             holder.leftLayout.setVisibility(View.VISIBLE);
43             holder.rightLayout.setVisibility(View.GONE);
44             holder.leftMsg.setText(msg.getContent());
45         } else if (msg.getType() == Message.TYPE_SEND) {
46             //发送消息,用右边格式 左边格式隐藏
47             holder.rightLayout.setVisibility(View.VISIBLE);
48             holder.leftLayout.setVisibility(View.GONE);
49             holder.rightMsg.setText(msg.getContent());
50         }
51     }
52 
53     //    返回recyclerview有多少子项
54     @Override
55     public int getItemCount() {
56         return mMsgList.size();
57     }
58 
59 }

这里的重点是重写三个方法 onCreateViewHolder(),onBindViewHolder()和getItemCount()。首先建立一个内部类将子项的所有布局进行传入匹配。

1 onCreateViewHolder()

  用于创建内部类viewholder实例,在这个方法中传入msg_item布局,创建一个viewHolder实例,并把加载出来的布局传入到构造函数中,将viewholder的实例返回。

2 onBindViewHolder()

  用于对recyclerview子项的数据进行赋值,在子项滚动到屏幕内时执行。通过position参数得到当前项的实例,在将其内容设置到实例中即可。

3 getItemCount()

  返回recyclerview有多少子项

五 主函数

主函数主要是分配布局文件,并创建布局管理器,为滑动框设置布局管理器,设置适配器。之后为发送按钮设置点击事件,再初始化一下消息列表。具体的内容在代码的注释中有详细介绍。

 1 public class MainActivity extends AppCompatActivity {
 2 
 3     private List<Message> msgList = new ArrayList<>();
 4     private EditText inputText;
 5     private Button send;
 6     private RecyclerView msgRecyclerView;
 7     private MsgAdapter adapter;
 8 
 9     @Override
10     protected void onCreate(Bundle savedInstanceState) {
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.activity_main);
13 
14         initMsgs();
15 
16         //分配布局文件
17         inputText = (EditText) findViewById(R.id.input_text);
18         send = (Button) findViewById(R.id.send);
19         msgRecyclerView = (RecyclerView) findViewById(R.id.msg_recycler_view);
20 
21         //布局管理器
22         LinearLayoutManager layoutManager = new LinearLayoutManager(this);
23         //为滑动框设置布局管理器,设置适配器
24         msgRecyclerView.setLayoutManager(layoutManager);
25         adapter = new MsgAdapter(msgList);
26         msgRecyclerView.setAdapter(adapter);
27         //为发送按钮设置监听事件
28         send.setOnClickListener(new View.OnClickListener() {
29             @Override
30             public void onClick(View view) {
31                 String content = inputText.getText().toString();
32                 //如果输入不为空
33                 if (!"".equals(content)) {
34                     Message msg = new Message(content, Message.TYPE_SEND);
35                     msgList.add(msg);
36                     //通知列表有新数据插入 这样数据才能在recyclerview中显示
37                     adapter.notifyItemInserted(msgList.size() - 1);
38                     //定位将显示的数据定位到最后一行,保证可以看到最后一条消息
39                     msgRecyclerView.scrollToPosition(msgList.size() - 1);
40                     inputText.setText("");
41                 }
42             }
43         });
44     }
45 
46     //初始化消息列表
47     private void initMsgs() {
48         Message msg1 = new Message("Hallo,我是电影专家", Message.TYPE_RECEIVED);
49         msgList.add(msg1);
50     }
51 }

 

完成效果图:

技术分享图片

 

以上是关于recyclerview实战——对话界面的主要内容,如果未能解决你的问题,请参考以下文章

如何在 TabLayout 中找到由 RecyclerView 打开的对话框的上下文?

儿童片段到父母片段沟通

如何将数据从 BottomSheetDialogFragment 返回到父片段

实战 | 认识 RecyclerView

RecyclerView 内容未使用片段父级的全宽

Android:RecyclerView 不显示片段中的列表项