最近在做一个电影智能问答系统,需要有一个相应的界面来操作问答,而最近又在学android,使用决定采用recyclerview控件来写相关的内容。
android本身有一种相关的控件叫listview,但是相比recyclerview来说,Listview只能实现上下滑动而且扩展性不好。重要的是recyclerview可以实现复用,即已经移出屏幕的样式会在屏幕的下方进行复用。下面开始进行相关的操作。
一 前期准备
1 对话框素材
开始实现前需要有一个对话框,从icon网站上找到一个对话框png格式,背景透明作为所用的对话框。
将其导入drawable之后转换为 9 patch格式,可以确保放大的时候图像部分放大,从而确保不失真不变形。(改变图片左边和上边的黑色条来确定哪里被拉伸)
2 建立recyclerview依赖
因为recyclerview是新增空间,需要在项目的build.gradle中增加其依赖,注意此处的build.gradle的地址为app\build.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 }
完成效果图:
转载于:https://www.cnblogs.com/Mask-D/p/9261114.html
相关资源:使用RecyclerView实现聊天界面