UI开发
常用控件
TextView
- 包括对齐方式graivty、颜色color、大小size。
- 另外可以使用"|"来同时指定多个值.|左右没有空格。
- 布局大小layout的固定值单位是dp,这是一种屏幕密度无关的尺寸单位,可以保证在不同手机上显示相同的效果。
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:textColor="#00ff00"
android:textSize="24sp"
android:text="This is a TextView!" />
Button
- Button默认是TEXT都是大写,如果需要小写,需要把textAllCaps改为false
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:textAllCaps="false"/>
EditText
- 允许用户在控件中输入和编辑内容,并可以在程序中对这些内容进行处理。
-
hint
是用于写入提示用的。 - 可能存在输入文字过多的问题,可能存在超过一行的情况,这个时候使用
maxLines
。2表示输入的内容超过两行时,文本就会向上滚动,EditText则不会再继续拉伸。
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Type something here"/>
ImageView
ImageView是用于在界面上展示图片的一个控件。
-
在res目录下创建一个drawable-xxhdpi目录。放入img【注意这个地方是选择的project而不是app】
-
app只负责显示,不同文件夹下的创建还是在project下【这也是xlm中的路径】
-
-
修改主页布局activity_main.xml
<ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/apple_pic"/>
-
动态的修改ImageView中的图片,比如在回调函数中来写(同理java中的代码也是根据app的):
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button showEditTextButton = (Button) findViewById(R.id.showEditTextButton); final EditText editText = (EditText) findViewById(R.id.editText); ImageView imageView = (ImageView)findViewById(R.id.imageView); showEditTextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this,editText.getText().toString(),Toast.LENGTH_SHORT).show(); imageView.setImageResource(R.drawable.banana_pic); } }); }
ProgressBar
progressBar用于在界面上显示一个进度条,表示我们的程序正在加载一些数据。它的用法也非常简单,修改activity_main.xml中的代码,如下所示:
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
通过Android:visibility指定控件的可见属性:
- visible:可见
- invisible:不可见
- gone:不可见+不占用资源
切换轮播图效果:
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,editText.getText().toString(),Toast.LENGTH_SHORT).show();
if(progressBar.getVisibility() == View.VISIBLE){
imageView.setImageResource(R.drawable.banana_pic);
progressBar.setVisibility(View.GONE);
}else {
imageView.setImageResource(R.drawable.apple_pic);
progressBar.setVisibility(View.VISIBLE);
}
}
设置横线进度条:
- 通过
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
把圆的变成横线。 - 通过
android:max="100"
。设置最大值为100 - 修改java中的代码
progressBar.progress = progressBar.progress +10
TODO:【做成一个每日任务完成进度条 =task_name + expected_comsume_time】
AlertDialog
alertDialog可以子啊当前界面弹出一个对话框,这个对话框置顶了。
直接在activity代码中使用即可。
@Override
public void onClick(View v) {
int progress = progressBar.getProgress();
if( progress ==progressBar.getMax()){
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("progressBar is filled, will you want to restart?");
dialog.setCancelable(false);
/**
* 如果Yes就从头开始计算progressBar
*/
dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
progressBar.setProgress(0);
}
});
dialog.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show();
}else {
progress = progress + 10;
progressBar.setProgress(progress);
}
}
});
三种基本布局
LinearLayout
线性布局,线性方向上依次排列控件。方向的选择取决于android:orientation
。而控件的对齐方式则是根据android:layout_gravity
RelativeLayout
relativelayout相对布局,
-
相对父layout布局:[上对齐+左对齐] [中心对齐]
android:layout_alignParentRight
android:layout_alignParentTop
-
android:layout_centerInParent
-
相对于控件对齐:
android:layout_above="@id/button3"
android:layout_toLeftOf="@id/button3"
android:layout_toRightOf="@id/button3"
android:layout_below="@id/button3"
-
另外一组相对于控件进行定位的属性
-
android:layout_alignLeft="@id/button3"
表示让一个控件和另外一个控件的左边缘对齐 -
等等
-
FrameLayout
所有控件都默认在布局的左上角,
ConstrainLayout
这个布局替代了弃用的百分比布局。不过使用relativelayout + linear也够用了。
虽说现在Button已经添加到界面上了,但是由于我们还没有给Button添加任何的约束,因此Button并不知道自己应该出现在什么位置。现在我们在预览界面上看到的Button位置并不是它最终运行后的实际位置,如果一个控件没有添加任何约束的话,它在运行之后会自动位于界面的左上角。
-
给Button添加约束,每个控件的约束都分为垂直和水平两类,一共可以在四个方向上给控件添加约束:
-
相对于parent_layout的约束
-
相对于控件的约束
-
-
删除约束的方式一共有三种
- 选中约束圆圈然后delete
- 删除某一个控件的所有约束,选中一个控件,反键删除即可
- 删除整个layout的约束,在蓝图上面有一个删除按钮
-
Inspector:选中控件右边就会有Properties,其中包括控件的所有属性,如文本内容、颜色、点击事件等等。Properties区域的上半部分,这部分也被称为Inspector
- Inspector中有一个纵向的轴和一个横向的轴,这两个轴也是用于确定控件的位置的
- 位于Inspector最中间的那个正方形区域,它是用来控制控件大小的。一共有三种模式可选,每种模式都使用了一种不同的符号表示,点击符号即可进行切换。
- 表示wrap content,这个我们很熟悉了,不需要进行什么解释。
- 表示固定值,也就是给控件指定了一个固定的长度或者宽度值。
-
表示any size,它有点类似于match parent,但和match parent并不一样,是属于ConstraintLayout中特有的一种大小控制方式,下面我们来重点讲解一下。
- 首先需要说明,在ConstraintLayout中是有match parent的,只不过用的比较少,因为ConstraintLayout的一大特点就是为了解决布局嵌套,既然没有了布局嵌套,那么match parent也就没有多大意义了。
- 而any size就是用于在ConstraintLayout中顶替match parent的,比如你想让整个button的宽度充满整个布局。只需要修改为any_size然后设置为0dp(左侧的间距设置成0)即可。
- 和match parent最大的区别在于,match parent是用于填充满当前控件的父布局,而any size是用于填充满当前控件的约束规则。
-
Guidelines:上面的方法很容易时间一个控件的居中,如果我们想让两个按钮共同居中对齐需要用到ConstraintLayout中的一个新的功能,Guidelines。
- TODO:没学会
-
自动添加约束:TODO
-
只有linearLayout支持使用layout_weight属性来控制控件的大小
-
relativeLayout很难实现两个按钮平分布局宽度的效果。
创建自定义控件
所有的控件都是继承自View的。所有的布局都是继承自ViewGroup的。View是Android中最基本的一种UI组件,它可以在屏幕上绘制一个矩形区域,并能响应这块区域的各种事件。而ViewGroup则是一种特殊的控件,他可以包含很多子view和子viewgroup,是一个用于放置控件和布局的容器。
引入布局
类似于Vue的组件吧,布局也是可以引入的,A布局引入B布局的方法:
<include layout="@layout/bottom_layout"/>
引入布局可以解决重复编写布局代码的问题。但是布局中有一些控件要求能够响应事件,因此还需要自定义控件
创建自定义控件
TOOD
ListView【过时】
ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据会滚动出屏幕。
ListView简单用法
- 首先创建一个数组把 数据放入 比如String[]。
- 由于集合中的数据是无法直接传递给ListView的,我们还需要借助适配器来完成。适配器的实现类有很多,常用的是ArrayAdapter。
- ArraryAdapter的参数有activity实例、listview子项布局的id以及数据源。
- 而子项布局使用了android.R.layout.simple_list_item_1作为ListView子项布局的id,这个是一个Android内置的布局文件,里面只有一个testview用于简单显示一段文本。
- 最后调用ListView的setAdapter()方法将构建好的适配器对象传递进去。
// private String[] data = {"apple","banana"...}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* android.R.layout. 和 R.layout. 的区别?
*/
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_expandable_list_item_1, data);
ListView listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(adapter);
}
定制ListView的界面
- 创建Fruit类,——POJO类
- 为ListViewd的子项制定一个自定义布局,创建一个fruit_item.xml。主要包括一个imageView和一个TextView
- 创建adapter类。
- 重写了父类的一组构造函数,用于将context、listview子项布局的id(就是上面这个布局)和数据都传递出来。
- 重写了另外一个方法getView。这个方法在每个子项被滚动到屏幕内的时候会被调用。
- 首先通过getItem()方法得到当前项的Fruit实例
- 然后使用LayoutInflater来为这个子项加载我们传入的布局。LayoutInflater的inflate方法接受三个参数,第一个是fruit_item这个布局、第二个是被adapter的水果array集合。第三个参数默认是false。
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit = getItem(position); // 获取当前项的fruit实例
View view = LayoutInflater.from(getContext())
.inflate(resourceId, parent, false);
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitText = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitText.setText(fruit.getName());
return view;
}
-
在mainActivity上面展示出来。
- 通过把Fruit类放入集合中 ->initfruit()。
自己创建的包名.R$drawable
获取内部类drawable。然后getField()获取图片的field
private void initFruits(){ for(int i=0;i<data.length;i++) { String imageName = data[i]+"_pic"; try { field = Class.forName("com.ssozh.listviewtest.R$drawable").getField(imageName); Fruit apple = new Fruit(data[i],field.getInt(R.drawable.class)); fruitList.add(apple); Log.d("initFruits",fruitList.toString()); } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) { e.printStackTrace(); } } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); FruitAdapter fruitAdapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); ListView listView = (ListView)findViewById(R.id.listView); listView.setAdapter(fruitAdapter); }
- 通过把Fruit类放入集合中 ->initfruit()。
提升listview的运行效率
使用getView()方法的参数convertView将之前加载好的布局进行缓存,以便之后可以进行使用。=>不重复加载布局
继续优化=>getView()会调用View的findViewId方法来获取一次控件的实例。=>借助ViewHolder来对这个部分进行优化。
具体就是通过新增一个内部类ViewHolder,用于对控件实例进行缓存。当converView为null的时候,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里面,然后调用View的setTag()方法,将ViewHolder对象存储在View中。
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Fruit fruit = getItem(position); // 获取当前项的fruit实例
ViewHolder viewHolder = null;
View view;
if (viewHolder == null) {
view = LayoutInflater.from(getContext())
.inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
view.setTag(viewHolder);
}else {
view = convertView; // converView 就是缓存
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
public ViewHolder(){}
}
ListView点击事件
和button的点击事件不同,他的点击事件是itemClick而不是直接的clickj同样的,new的View也是adapter的。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
RecyclerView【推荐】
增强版的ListView,不仅可以实现和listView一样的效果,还优化了listview存在的各种不足之处。
recyclerView基本用法
-
写一个RecyclerView的子项:注意Layout占满屏幕可能是因为布局属性没改:https://blog.csdn.net/clzh2013/article/details/53897357
-
定义一个adapter
- 首先定义一个内部类ViewHolder,构造函数传入View参数,这个参数据说RecyclerView子项的最外层布局。
- 重写onCreateViewHolder方法,用来创建ViewHolder实例。
- 重写onBindViewHolder()方法,用来对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行。
- getItemCount()就是用于高速RecyclerView一共有多少个子项,
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private List<Fruit> mFruitList; /** * 构造函数把Fruit集合传入 */ public FruitAdapter(List<Fruit> fruitList) { mFruitList = fruitList; } /** * 创建view 然后创建viewHolder把view放入然后返回。 * 这个方法就类似于ListView的getView */ @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Fruit fruit = mFruitList.get(position); holder.fruitName.setText(fruit.fruitName); holder.fruitImage.setImageResource(fruit.fruitImage); } @Override public int getItemCount() { return mFruitList.size(); } class ViewHolder extends RecyclerView.ViewHolder { ImageView fruitImage; TextView fruitName; public ViewHolder(@NonNull View itemView) { super(itemView); fruitImage = itemView.findViewById(R.id.fruit_image); fruitName = itemView.findViewById(R.id.fruit_name); } @Override public String toString() { return super.toString(); } } }
-
在MainActivity中使用recyclerView
- 关于反射:
fruitList.add(new Fruit(data[i], field.getInt(null)));
Android这个地方可以传入null 暂时还是不是恨透侧。
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); private String[] data = {"apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango","apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); RecyclerView recyclerView =(RecyclerView) findViewById(R.id.recyclerView); recyclerView.setLayoutManager(linearLayoutManager); FruitAdapter fruitAdapter = new FruitAdapter(fruitList); recyclerView.setAdapter(fruitAdapter); } private void initFruits(){ for(int i=0;i<data.length;i++) { String imageName = data[i] + "_pic"; Class clazz = null; try { clazz = Class.forName("com.ssozh.recyclerview.R$drawable"); // 注意必须这么写 如果写为com.ssozh.R.drawable这样提示写法是错的反射!!! Field field = clazz.getField(imageName); // 通过class.getField返回指定的field域,然后通过fiedl.get(obj)返回obj对象中用Field对象表示的值域。 fruitList.add(new Fruit(data[i], field.getInt(null))); } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } } }
- 关于反射:
实现横向滚动和幕布流布局
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
// 重点在这一句!!!
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
RecyclerView recyclerView =(RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(linearLayoutManager);
FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(fruitAdapter);
}
之所以RecyclerView可以横向滚动,是因为RecyclerView将布局排列的任务交给了LayourManager而ListView则是由自身管理的。
RecyclerView提供了GridLayoutManager、StaggerGridLayoutManager和LinearLayoutManager三种内置的布局排泄方法。
- GridLayouManager可以用于实现网格布局
- StaggerGridLayoutManager可以用于实现瀑布流布局。
RecyclerView的点击事件
RecyclerView没有提供类似于setOnItemClickListener)这样的注册监听器的方法,而是需要我们自己给子项具体的view去注册点击事件。
之所以这么做,是因为recyclerview可以给子项中具体的某一个按钮(view)去注册。
=>具体实现是修改fruitAdapter中的代码:
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
// 点击事件写在这里
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = viewHolder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you click view" + fruit.getFruitName(),Toast.LENGTH_SHORT).show();
}
});
viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = viewHolder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you click view" + fruit.getFruitName(),Toast.LENGTH_SHORT).show();
}
});
return viewHolder;
}
编写界面的最佳实践
制作9-patch图片
9-patch图片是一种经过特殊处理过的png图片,能够制定哪些区域可以被拉伸、哪些区域不可以。在Android studio中,我们可以将任何png类型的图片制作成9-patch图片。
编写精美的聊天界面
-
在主界面放一个recyclerView用于显示聊天的消息内容,又放置了一个editText用于输入信息,一个button用于发送消息。
- 其中editText和Button放在同一个LinearLayout中
<androidx.recyclerview.widget.RecyclerView android:id="@+id/msg_recycler_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/input_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="type something here" android:maxLines="2"/> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send"/> </LinearLayout>
-
编写RecyclerVew的子项布局msg_item.xml【LinearLayout】(第三版书包括发送消息的子布局msg_left_item.xml和接收消息的子布局msg_right_item.xml)
<LinearLayout android:id="@+id/left_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left" android:background="@drawable/message_left"> <TextView android:id="@+id/left_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:textColor="#fff"/> </LinearLayout> <LinearLayout android:id="@+id/right_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:background="@drawable/message_right"> <TextView android:id="@+id/right_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:textColor="#000"/> </LinearLayout>
-
定义消息实体类,新建Msg
- 内容content
- type标识是发送还是接受
- 添加final静态变量表示上面两种type:
public final static int TYPE_RECEIVED = 0; public final static int TYPE_SEND = 1; private String content; private int type;
-
编写上面消息类实体的MsgAdapter:
-
定义静态内部类ViewHolder包括:左右消息子布局【linearLayout】和两个TextView
LinearLayout leftLayout; LinearLayout rightLayout; TextView leftMsg; TextView rightMsg; public ViewHolder(@NonNull View itemView) { super(itemView); leftLayout = (LinearLayout) itemView.findViewById(R.id.left_layout); leftMsg = (TextView) itemView.findViewById(R.id.left_msg); rightLayout =(LinearLayout) itemView.findViewById(R.id.right_layout); rightMsg = (TextView)itemView.findViewById(R.id.right_msg); }
-
构造函数应该把msg集合内容传入MsgAdapter:
private List<Msg> mMsgList; public MsgAdapter(List<Msg> msgList) { this.mMsgList = msgList; }
-
继承
RecyclerView.Adapter<MsgAdapter.ViewHolder>
类必须重写的两个方法:-
onCreateViewHolder
用来创建ViewHolder实例。@NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false); ViewHolder viewHolder = new ViewHolder(view); return viewHolder; }
-
onBindViewHolder
:用来对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Msg msg = mMsgList.get(position); if(msg.getType() == Msg.TYPE_RECEIVED){ // 表名是接受消息,显示左边,隐藏右边 holder.leftLayout.setVisibility(View.VISIBLE); holder.rightLayout.setVisibility(View.GONE); holder.leftMsg.setText(msg.getContent()); return; } if(msg.getType() == Msg.TYPE_SEND){ // 表名是接受消息,显示左边,隐藏右边 holder.leftLayout.setVisibility(View.GONE); holder.rightLayout.setVisibility(View.VISIBLE); holder.rightMsg.setText(msg.getContent()); } }
-
-
-
最后在mainActivity上写相关逻辑:
-
初始化msg集合,也就是历史聊天记录
public void initMsgs(){ Msg msg1 = new Msg("hello ssozh", Msg.TYPE_RECEIVED); msgList.add(msg1); Msg msg2 = new Msg("hello,Who is that", Msg.TYPE_SEND); msgList.add(msg2); Msg msg3 = new Msg("This is Tom. Nice to meet you ", Msg.TYPE_RECEIVED); msgList.add(msg3); }
-
获取发送button、输入文本框inputText以及msgRecyclerView这几个view
-
【重点】创建layoutManager 并绑定msgRecyclerView=>否则报错E/RecyclerView: No layout manager attached; skipping layout
-
创建adpater并绑定
-
编写send按钮的回调函数
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initMsgs(); inputText = (EditText) findViewById(R.id.input_text); send = (Button) findViewById(R.id.send); msgRecyclerView = (RecyclerView) findViewById(R.id.msg_recycler_view); // 重点:E/RecyclerView: No layout manager attached; skipping layout 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 v) { String content = inputText.getText().toString(); if(!"".equals( content)){ Msg msg = new Msg(content,Msg.TYPE_SEND); msgList.add(msg); // 当有新消息的时候刷新ListView定位到最后一行 adapter.notifyItemChanged(msgList.size()-1); // 清空输入框 inputText.setText(""); } } }); }
-