Android 常见界面控件(ListView、RecyclerView、自定义View篇)

Android 常见界面控件(ListView、RecyclerView、自定义View篇)

3.3 ListView的使用

例如微信淘宝等应用程序,这些程序通常会在一个页面中展示多个条目,并且每一个条目布局风格一致,这种数据的展示方式是通过ListView控件实现的

3.3.1 ListView控件的简单使用

ListView以列表的形式展示数据内容,并且能根据列表高度自适应屏幕显示,ListView控件的常用属性:

在这里插入图片描述

3.3.2 常用数据适配器

在为ListView控件添加数据时会用到数据适配器。数据适配器是数据与视图之间的桥梁,它类似于一个转换器,将复杂的数据转换成用户可以接受的方式进行呈现。在Android系统中提供了多种适配器(Adapter)对ListView控件进行数据适配

  1. BaseAdapter

BaseAdapter是基础的适配器,其实际上是一个抽象类,通常在自定义适配器时会继承BaseAdapter,该类有四个抽象方法,根据这些抽象方法来对ListView控件进行数据适配

在这里插入图片描述

  1. SimpleAdapter

SimpleAdapter继承自BaseAdapter,实现了BaseAdapter的四个抽象方法对其进行封装,因此在使用SimpleAdapter进行数据适配时,只需要在构造方法中传入相应的参数即可

public SimpleAdapter(Context context,List<? extends Map ?>

data, int resource,String[] from,int[] to)

  • context:上下文对象
  • data:数据集合,data中的每一项对应ListView控件中的条目的数据
  • resource:Item布局的资源id
  • from:Map集合中的key值
  • to:Item布局中对应的控件
  1. ArrayAdapter

ArrayAdapter也是BaseAdapter的子类,使用时只需要在构造方法中传入相应的参数即可,ArrayAdapter常用于适配TextView控件,例如Android Studio的Help,ArrayAdapter有多个构造方法

在这里插入图片描述

  • context:上下文对象
  • resource:Item布局的资源id
  • textViewResourceID:Item布局中相应TextView的id
  • T[] objects:需要适配的数组类型的数据
  • List object:需要适配的List类型的数据

在创建数据适配器后,可以通过ListView对象的setAdapter()方法添加适配器

例如:要将继承 BaseAdapter的MyBaseAdapter实例添加到ListView

ListView mListView =ListView)findViewById(R.id.lv);
MyBaseAdapter mAdapter = new MyBaseAdapter();
mListView.setAdapter(mAdapter);
3.3.3 案例——Android生物商城
  1. 创建listview应用程序

  2. 导入图片资源

  3. 放置界面控件(编写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>
  1. 创建Item界面(编写list_item.xml文件)
<?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>
  1. 编写界面交互代码(MainActivity中的MyBaseAdapter)
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;
        }
    }
}

启动测试

在这里插入图片描述

  1. 优化ListView加载数据逻辑

运行上述程序后,当ListView控件 上加载的Item过多并快速滑动该控件时,界面会出现卡顿的现象,出现这个现象的原因如下:

(1)当滑动屏幕时,不断地创建Item对象。ListView控件在当前屏幕上显示多少个Item,就会在适配器MyBaseAdapter中的getView0方法中创建多少Item对象。当滑动ListView控件时, 滑出屏幕的Item对象会被销毁,新加载到屏幕上的Item会创建新的对象,因此快速滑动ListView控件时会不断地对Item对象进行销毁和创建

(2)不断执行findViewByld0方法初始化控件。每创建-一个Item对象都需要加载一次Item布局,加载布局时会不断地执行findViewByld0方法初始化控件。这些操作比较耗费设备(模拟器、手机等设备)的内存并且浪费时间,如果每个Item都需要加载网络图片,加载网络图片是个比较耗时的操作,就会造成程序内存溢出的异常

由于以上两点原因,我们需要对ListView控件进行优化,优化的目的就是使ListView控件在快速滑动时不再重复创建Item对象,减少内存的消耗和屏幕渲染的处理

优化步骤

  1. 创建ViewHolder类。 在MainActivity中创建一 个ViewHolder类, 将需要加载的控件变量放在改类中
class ViewHolder{
	TextView title;
    ImageView iv;
}
  1. 在MyBaseAdapter的getView(int position, View convertView, ViewGroup parent)方法中,第2个参数convertView代表的就是之前滑出屏幕的Item对象。如果第一次加载getView0方法时,会创建Item对象,当滑动ListView控件时, 滑出屏幕的Item对象会以缓存的形式存在,而convertView代表的就是缓存的Item对象,我们可以通过复用convertView对象从而减少Item对象的创建,在getView()方法中进行优化的代码
@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类的对象

3.4 RecyclerView的使用

在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));

启动测试

在这里插入图片描述

3.5 自定义View

通常开发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控件,则需要使用到该类的第二个构造方法

3.5.1 自定义View常用的3个方法

由于系统自带的控件不能满足需求中的某种样式或功能,因此我们需要在自定义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分别表示子控件与父容器左边、顶部、右边、底部的距离

3.5.2 用自定义View在界面中显示圆形

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>

启动测试

在这里插入图片描述

如果文章对您有所帮助,记得一键三连支持一下哦~

  • 11
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香蕉道突破手牛爷爷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值