赞
踩
Android 常见界面控件(ListView、RecyclerView、自定义View篇)
例如微信淘宝等应用程序,这些程序通常会在一个页面中展示多个条目,并且每一个条目布局风格一致,这种数据的展示方式是通过ListView控件实现的
ListView以列表的形式展示数据内容,并且能根据列表高度自适应屏幕显示,ListView控件的常用属性:
在为ListView控件添加数据时会用到数据适配器。数据适配器是数据与视图之间的桥梁,它类似于一个转换器,将复杂的数据转换成用户可以接受的方式进行呈现。在Android系统中提供了多种适配器(Adapter)对ListView控件进行数据适配
BaseAdapter是基础的适配器,其实际上是一个抽象类,通常在自定义适配器时会继承BaseAdapter,该类有四个抽象方法,根据这些抽象方法来对ListView控件进行数据适配
SimpleAdapter继承自BaseAdapter,实现了BaseAdapter的四个抽象方法对其进行封装,因此在使用SimpleAdapter进行数据适配时,只需要在构造方法中传入相应的参数即可
public SimpleAdapter(Context context,List<? extends Map ?>
data, int resource,String[] from,int[] to)
ArrayAdapter也是BaseAdapter的子类,使用时只需要在构造方法中传入相应的参数即可,ArrayAdapter常用于适配TextView控件,例如Android Studio的Help,ArrayAdapter有多个构造方法
在创建数据适配器后,可以通过ListView对象的setAdapter()方法添加适配器
例如:要将继承 BaseAdapter的MyBaseAdapter实例添加到ListView
ListView mListView = (ListView)findViewById(R.id.lv);
MyBaseAdapter mAdapter = new MyBaseAdapter();
mListView.setAdapter(mAdapter);
创建listview应用程序
导入图片资源
放置界面控件(编写activity_main.xml文件)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="368dp" android:layout_height="45dp" android:text="生物图鉴" android:textSize="18sp" android:textColor="#FFFFFF" android:background="#FF8F03" android:gravity="center" /> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/lv"/> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <ImageView android:id="@+id/iv" android:layout_width="120dp" android:layout_height="90dp" android:layout_centerVertical="true"/> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_toRightOf="@+id/iv" android:layout_centerVertical="true"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="豹猫" android:textSize="20sp" android:textColor="#000000"/> <TextView android:id="@+id/tv_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="价格" android:textSize="20sp" android:textColor="#FF8F03" android:layout_below="@+id/title" android:layout_marginTop="10dp"/> <TextView android:id="@+id/price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="1000" android:textSize="20sp" android:textColor="#FF8F03" android:layout_below="@+id/tv_price" android:layout_marginTop="10dp"/> </RelativeLayout> </RelativeLayout>
public class MainActivity extends AppCompatActivity { private ListView mListView; //生物与价格数据集合 private String[] titles = {"豹猫","毪菇","马","狼"}; private String[] prices = {"1000","4000","6577","5000"}; //图片数据集合 private int[] icons = {R.mipmap.cat,R.mipmap.cow,R.mipmap.horse,R.mipmap.wolf}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化ListView控件 mListView = (ListView)findViewById(R.id.lv); //创建一个数据适配器实例 MyBaseAdapter ma = new MyBaseAdapter(); mListView.setAdapter(ma); } //编写一个实现了BaseAdapter抽象类的数据适配器类 class MyBaseAdapter extends BaseAdapter{ @Override public int getCount() { //获取Item的总数 return titles.length; } @Override public Object getItem(int position) { //返回Item的数据对象 return titles[position]; } @Override public long getItemId(int position) { //返回item的id return position; } //得到item的视图 @Override public View getView(int position, View convertView, ViewGroup parent) { //加载activity_item.xml布局文件 View view = View.inflate(MainActivity.this,R.layout.activity_item,null); TextView title = (TextView) view.findViewById(R.id.title); TextView price = (TextView) view.findViewById(R.id.price); ImageView iv = (ImageView) view.findViewById(R.id.iv); title.setText(titles[position]); price.setText(prices[position]); iv.setBackgroundResource(icons[position]); return view; } } }
启动测试:
运行上述程序后,当ListView控件 上加载的Item过多并快速滑动该控件时,界面会出现卡顿的现象,出现这个现象的原因如下:
(1)当滑动屏幕时,不断地创建Item对象。ListView控件在当前屏幕上显示多少个Item,就会在适配器MyBaseAdapter中的getView0方法中创建多少Item对象。当滑动ListView控件时, 滑出屏幕的Item对象会被销毁,新加载到屏幕上的Item会创建新的对象,因此快速滑动ListView控件时会不断地对Item对象进行销毁和创建
(2)不断执行findViewByld0方法初始化控件。每创建-一个Item对象都需要加载一次Item布局,加载布局时会不断地执行findViewByld0方法初始化控件。这些操作比较耗费设备(模拟器、手机等设备)的内存并且浪费时间,如果每个Item都需要加载网络图片,加载网络图片是个比较耗时的操作,就会造成程序内存溢出的异常
由于以上两点原因,我们需要对ListView控件进行优化,优化的目的就是使ListView控件在快速滑动时不再重复创建Item对象,减少内存的消耗和屏幕渲染的处理
优化步骤:
class ViewHolder{
TextView title;
ImageView iv;
}
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView == null){ //将activity_item.xml布局文件找出来并转换为View对象 convertView = View.inflate(MainActivity.this,R.layout.activity_item,null); //找到activity_item.xml中的TextView holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.title); holder.price = (TextView) convertView.findViewById(R.id.price); holder.iv = (ImageView) convertView.findViewById(R.id.iv); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.title.setText(titles[position]); holder.price.setText(prices[position]); holder.iv.setBackgroundResource(icons[position]); return convertView; }
代码解析:
第2行代码用于判断convertView对象是否为null,若为null,则会创建ViewHolder类中的对象holder,并将获取的界面控件赋值给ViewHolder类中的属性,最后通过setTag()方法将对象holder添加到convertView对象中,否则不会创建ViewHolder类的对象,会通过getTag()方法获取缓存在convertView对象中的ViewHolder类的对象
在Android5.0之后,谷歌提供了用于在有限的窗口范围内显示大量数据的控件RecyclerView,与ListView控件相似,RecyclerView控件同样 是以列表的形式展示数据,并且数据都是通过适配器加载的。但是,RecyclerView的功能更加强大
(1)展示效果: RecyclerView控件 可以通过LayoutManager类实现横向或竖向的列表效果、瀑布流效果和GridView效果,而ListView控件 只能实现竖直的列表效果
(2)适配器: RecyclerView 控件使用的是RecyclerView.Adapter适配器,该适配器将BaseAdapter中的getView()方法拆分为onCreateViewHolder()方法和onBindViewHolder0方法,强制使用ViewHolder类,使代码编写规范化,避免了初学者写的代码性能不佳
(3)复用效果: RecyclerView控件复用Item对象的工作由该控件自己实现,而ListView控件复用Item对象的工作需要开发者通过convertView的setTag()方法和getTag()方法进行操作
(4)动画效果: RecyclerView控件可以通过setltemAnimator()方法为Item添加动画效果,而ListView控件不可以通过该方法为Item添加动画效果
典例:
RecyclerView是com.android. support:recyclerview-v7库中的控件,因此需要将该库添加到程序中。首先选中程序名称,右击选择[Open Module Settings]选项,在Project Structure窗口中的左侧选择[app],接着选中[Dependencies]选项卡,单击右上角的绿色加号
activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.app.AlertController.RecycleListView
android:id="@+id/id_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.app.AlertController.RecycleListView>
</RelativeLayout>
activity_item.xml文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" android:gravity="center" android:orientation="horizontal"> <ImageView android:id="@+id/iv" android:layout_width="120dp" android:layout_height="90dp" android:src="@drawable/cat"/> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginTop="5dp"> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="豹猫" android:textSize="20sp" android:textColor="#000000"/> <TextView android:id="@+id/introduce" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textColor="#FF716C6D" android:layout_below="@+id/name" android:layout_marginTop="10dp" android:maxLines="2" android:ellipsize="end" android:text="没有被驯服的豹猫会杀鸡,它会渐渐靠近一只鸡,然后迅速扑过去杀掉它,也是十分的凶残啊!小豹猫一点也不示弱,照样会杀鸡哦!"/> </RelativeLayout> </LinearLayout>
MainActivity.java文件
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private HomeAdapter mAdapter; private String[] names = {"豹猫","马","毪菇","兔子"}; private int[] icons = {R.drawable.cat,R.drawable.cow,R.drawable.horse,R.drawable.wolf}; private String[] introduces = {"...","...","...","..."}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView)findViewById(R.id.id_recyclerview); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mAdapter = new HomeAdapter(); mRecyclerView.setAdapter(mAdapter); } class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{ @Override public HomeAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_item,parent,false)); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.name.setText(names[position]); holder.iv.setImageResource(icons[position]); holder.introduce.setText(introduces[position]); } @Override public int getItemCount() { return names.length; } class MyViewHolder extends RecyclerView.ViewHolder { TextView name; ImageView iv; TextView introduce; public MyViewHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(R.id.name); iv = (ImageView) itemView.findViewById(R.id.iv); introduce = (TextView) itemView.findViewById(R.id.introduce); } } } }
代码解析:
先在第11行代码获取RecyclerView控件,接着通过setLayoutManager()方法设置RecyclerView控件的显示方式为线性垂直的效果,然后通过setAdapter()方法将适配器HomeAdapter的对象设置在RecyclerView上
在第17行创建一个HomeAdapter类继承自RecyclerView.Adapter类,并在其中重写方法,onCreateViewHolder()方法用于加载Item界面的布局文件,并将MyViewHolder类的对象返回,onBindViewHolder()方法用于将获取的数据设置在对应控件上,getItemCount()方法用于获取列表条目的总数
第33行创建了一个MyViewHolder类继承自RecyclerView.ViewHolder类,在该类中获取Item界面上的控件
关键语句:
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_item,parent,false));
启动测试
通常开发Android应用的界面时,使用的控件都不直接使用View,而是使用View的子类。例如,如果要显示一段文字,可以使用View的 子类TextView,如果要显示-一个按钮,可以使用View的子类Button。虽然Android系统中提供了很多继承自View类的控件,但是在实际开发中,还会出现不满足需求的情况,因此我们可以通过自定义View的方式进行实现
最简单的自定义View就是创建一个类继承View类或其子类,并重写该类的构造方法
public class CustomView extends View{
public CustomView(Context context){
super(context);
}
public CustomView(Context context,AttributeSet attrs){
super(context,attrs);
}
}
定义好自定义类CustomView后,若想创建一个该类的对象,则需要使用到该类的第一个构造方法,若想要在布局文件中引入该自定义的CustomView控件,则需要使用到该类的第二个构造方法
由于系统自带的控件不能满足需求中的某种样式或功能,因此我们需要在自定义View中通过重写指定的方法来添加额外的样式和功能,自定义View常用的3个方法的具体介绍如下:
(1) onMeasure()方法:该方法用于测量尺寸,在该方法中可以设置控件本身或其子控件的宽高
onMeasure (int widthMeasureSpec, int heightMeasureSpec)
onMeasure()方法中的第1个参数widthMeasureSpec表示获取父容器指定该控件的宽度,第2个参数heightMeasureSpec表示获取父容器指定该控件的高度
widthMeasureSpec和heightMeasureSpec参数不仅包含父容器指定的属性值,还包括父容器指定的测量模式
测量模式分为三种:
●EXACTLY: 当自定义控件的宽高的值设置为具体值时使用,如100dp、 match _parent等,此时控件的宽高值是精确的尺寸
●AT_MOST: 当自定义控件的宽高值为wrap_content时使用,此时控件的宽高值是控件中的数据内容可获得的最大空间值
●UNSPECIFIED: 当父容器没有指定自定义控件的宽高值时使用
需要注意的是,虽然参数widthMeasureSpec和heightMeasureSpec是父容器指定该控件的宽高,但是该控件还需要通过setMeasuredDimension(int,int)方法设置具体的宽高
(2) onDraw()方法:该方法用于绘制图像
onDraw (Canvas canvas)
onDraw0方法中的参数canvas表示画布。Canvas类经常与Paint类(画笔)配合使用,使用Paint类可以在Canvas类中绘制图像
(3) onLayout()方法:onLayout()方 法用于指定布局中子控件的位置,该方法通常在自定义ViewGroup中重写
onLayout (boolean changed, int left, int top, int right, int bottom)
onLayout(方法中有5个参数,其中,第1个参数changed表示自定义View的大小和位置是否发生变化,剩余的4个参数left、top、 right、 bottom分别表示子控件与父容器左边、顶部、右边、底部的距离
CustomView.java
public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context,AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int r = getMeasuredWidth()/2; int cX = getLeft()+r; int cY = getTop()+r; Paint paint = new Paint(); paint.setColor(Color.RED); //绘制 canvas.drawCircle(cX,cY,r,paint); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp">
<com.itcast.CustomView.CustomView
android:layout_width="100dp"
android:layout_height="100dp"
/>
</RelativeLayout>
启动测试
如果文章对您有所帮助,记得一键三连支持一下哦~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。