Android app开发学习笔记——Android高级控件-上

一、ListView(列表视图)

在Android开发中,ListView是很常用的控件,以列表的形式展示具体的内容,并且能够根据数据的长度自适应显示
列表的显示需要三个元素:
ListView:用来展示列表的View
适配器:用来把数据映射到ListView上的中介
数据源:ListView中的每一行View对应数据源的一条数据

1.建立适配器(以BaseAdapter为例)

其余类型可以参考:适配ListView的几种常见Adapter的用法总结
BaseAdapter是最万能最好用的数据适配器之一,可以给ListView、Spinner、GridView多种控件填充数据。通俗的说,适配器的作用就是在数据和视图之间建立一种桥梁,类似一个转换器,能够将复杂的数据转换成用户可以接受的方式进行呈现。
我们新建一个java文件,继承自BaseAdapter,并且实现它的4个基础方法(这4个基础方法可能不会自动生成需要我们自己输入)。

public class ListViewAdapter1 extends BaseAdapter{
    @Override
    public int getCount() {
        return 0;
    }
    @Override
    public Object getItem(int position) {
        return null;
    }
    @Override
    public long getItemId(int position) {
        return 0;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return null;
    }
}

getCount : 要绑定的条目的数目,比如格子的数量
getItem : 根据一个索引(位置)获得该位置的对象
getItemId : 获取条目的id
getView : 获取该条目要显示的界面
可以简单的理解为,adapter先从getCount里确定数量,然后循环执行getView方法将条目一个一个绘制出来,所以必须重写的是getCount和getView方法。而getItem和getItemId是调用某些函数才会触发的方法,如果不需要使用可以暂时不修改。
下面我们具体介绍BaseAdapte的四个基础方法的使用

1->getCount

返回值为填充的item个数,即列表有多少列
比如我们使用

public int getCount() {return data.size();}

就可以得到匹配data数据源的item行数

2->getView

其作用为填充每个item的可视内容并返回
其中getView方法中的三个参数,position是指现在是第几个条目;convertView是旧视图,就是绘制好了的视图;parent是父级视图,也就是ListView之类的。
我们用inflate方法绘制好后view,最后return返回给getView方法就可以了。
补充:
1.getView方法是由系统自动回调的方法,每当可视区域内需要刷新一个item时就会被调用,来填充数据并绘制View。
2.界面启动时,自动调用getView方法传入的convertView均为null; 根据代码逻辑

if(convertView == null){
 convertView = View.inflate(MainActivity.this,R.layout.item_view,null); 
 findViews(ics,convertView); convertView.setTag(ics); }

当convertView为null时就是调用View.inflate给其赋值,当然最后只要返回convertView就行了

3->getItem&getItemId

它也不会被自动调用,它是用来在我们设置setOnItemClickListener、setOnItemLongClickListener、setOnItemSelectedListener的点击选择处理事件中方便地调用,来获取当前行数据的。

至于getItemId(int position),它返回的是该postion对应item的id
不同getItem的是,某些方法(如onclicklistener的onclick方法)有id这个参数,而这个id参数就是取决于getItemId()这个返回值的。
详细可以参考:android中Baseadapter的 getItem 和 getItemId 的作用和重写

这边再给出一个适配器的样例,用于下一节的简单使用

public class ListViewAdapter1 extends BaseAdapter{
    private List<String> data;
    private LayoutInflater inflater;
    public  ListViewAdapter1(Context context,List<String> data){
        //加载布局管理器
        inflater=LayoutInflater.from(context);
        this.data=data;
    }
    //数据源的长度
    @Override
    public int getCount() {
        return data.size();
    }
    //每一行绑定的数据源
    @Override
    public Object getItem(int position) {
        return data.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    //获取每一行的View
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if(convertView==null)
        {
            viewHolder=new ViewHolder();
            //xml文件加载成View
            //android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源
            convertView=inflater.inflate(android.R.layout.simple_list_item_1,parent,false);
            //利用view对象,找到布局中的组件
            viewHolder.text=(TextView) convertView.findViewById(android.R.id.text1);
            convertView.setTag(viewHolder);
        }
        else {
            //使用tag,不用每次加载xml,提高速度
            viewHolder=(ViewHolder) convertView.getTag();
        }
        viewHolder.text.setText(data.get(position));
        return convertView;
    }
    private class ViewHolder{private  TextView text;}
}

代码解读参考BaseAdapter使用教程及方法详解中的ViewHolder优化

2.ListView简单使用

布局文件中仅需添加如下代码

    <ListView
        android:id="@+id/liebiao"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ListView>

在活动文件中我们用setAdapter选择应用我们自己创建的适配器
使用List类型的变量作为数据源,存储数据源的对应每一条数据

public class liebiao extends AppCompatActivity {
    private ListView listView;
    private BaseAdapter adapter;
    private List<String> items;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_liebiao);
        initData();
        listView=(ListView)findViewById(R.id.liebiao);
        listView.setAdapter(adapter = new  ListViewAdapter1(this,items));
    }
    //初始化数据
    private  void initData(){
        items =new ArrayList<>();
        for(int i=0;i<20;i++)
        {items.add("item:"+(i+1));}
    }
}

最终效果如下图
在这里插入图片描述

String[]与List< Sring>的区别及相互转换

3.给每一行点击监听

不需要给ListView每一行的View设置点击事件,ListView源码已经封装了setOnClickListener方法
下面代码设置了ListView行点击事件,点击之后显示一个Toast

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(liebiao.this,"点击Item位置:"+(position+1),Toast.LENGTH_SHORT).show();
            }
        });

setOnItemClickListener参数参考:【转载】ListView中的setOnItemClickListener参数

4.设置分割线

只需要在布局中增加两个属性就行了

        android:divider="@color/black"//分割线颜色
        android:dividerHeight="5dp"//分割线高度

如果不想要分割线,可以设置分割线为空,或者将分割线颜色设为透明:

        android:divider="@null"
        或者
        android:divider="@color/transparent"

5.添加header和footer

通过调用inflate方法将布局文件转化成View
head.xml,foot.xml文件在这里不显示了

View header= LayoutInflater.from(this).inflate(R.layout.head,null);
listView.addHeaderView(header);
View footer= LayoutInflater.from(this).inflate(R.layout.foot,null);
listView.addFooterView(foot);

6.动态修改Item

动态改变ListView只需要修改数据源,然后调用adapter的notifyDataSetChanged方法更新适配器即可
比如如下给尾部布局的点击添加一个添加项目功能

footer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch (v.getId())
                {
                    case R.id.foot:
                        items.add("我是被添加的");
                        adapter.notifyDataSetChanged();
                        break;
                }
            }
        });

7.设置显示位置

setSelection(),传入一个index整型数值,就可以让ListView定位到指定Item的位置。
setSelectionFromTop()的作用是设置ListView选中的位置,同时在Y轴设置一个偏移量(padding值)。

listView.setSelection(10);//显示第10行的数
listView.setSelectionFromTop(10,500);//显示第10行的数,同时y轴加500偏移量

8.实现聊天界面

由于要实现不同样式的列表,所以我们新建一个Message类用于记载消息内容区别是我们还是对方发出的消息

public class Message{
    private  String content;//消息内容
    private boolean sended;//是否发送
    public Message(){}
    public Message(String content,boolean sended){
        this.content=content;
        this.sended=sended;
    }
    public String getContent(){
        return content;
    }
    public void setContent(String content){
        this.content=content;
    }
    public boolean isSended(){
        return sended;
    }
    public void setSended(boolean sended){
        this.sended=sended;
    }
}

下面是活动文件的主题我们以List代替List

public class liebiao extends AppCompatActivity {
    private ListView listView;
    private BaseAdapter adapter;
    private List<Message> messages;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_liebiao);
        init();
        listView=(ListView)findViewById(R.id.liebiao);
        //listView.setAdapter(adapter=new com.example.hzhapplication.ListViewAdapter(this,data));
        listView.setAdapter(adapter=new ListViewAdapter1(this,messages));
    }
    private void init(){
        messages = new ArrayList<>();
        messages.add(new Message("为什么程序员到哪里都背着电脑包",false));
        messages.add(new Message("因为他们没有别的包包。",true));
        messages.add(new Message("程序员最烦两件事,第一件事是别人要他给自己的代码写文档,第二件呢?",false));
        messages.add(new Message("是别人的程序没有留下文档。",true));
        messages.add(new Message("如何生成一个随机的字符串?",false));
        messages.add(new Message("让新手退出VIM",true));
    }
}

适配与先前类似,添加了getViewTypeCount()和getItemViewType(int position)和两个类别用于分别辨识我们需要的样式

public class ListViewAdapter1 extends BaseAdapter{
    private LayoutInflater inflater;
    private List<Message> messages;
    private final int TYPE_SEND=0;
    private final  int TYPE_ACCEPT=1;
    public ListViewAdapter1(Context context,List<Message> data){
        inflater=LayoutInflater.from(context);
        this.messages=data;
    }
    //数据源的长度
    @Override
    public int getCount() {
        return messages.size();
    }
    //每一行绑定的数据源
    @Override
    public Object getItem(int position) {
        return messages.get(position);
    }
    @Override
    public int getViewTypeCount() {
        return TYPE_ACCEPT+1;
    }
    @Override
    public int getItemViewType(int position){
        if (messages.get(position).isSended()) {
            return TYPE_SEND;
        }
        return  TYPE_ACCEPT;
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    //获取每一行的View
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int type = getItemViewType(position);
        Message message=messages.get(position);
        ViewHolder viewHolder;
        if(convertView==null)
        {
            viewHolder=new ViewHolder();
            if(type==TYPE_SEND)
            { convertView=inflater.inflate(R.layout.item_message_chat_send,null);}
            else
              convertView=inflater.inflate(R.layout.item_message_chat_accept,null);
            viewHolder.tvContent=(TextView)convertView.findViewById(R.id.tv_content);
            convertView.setTag(viewHolder);
        }
        else {
            viewHolder=(ViewHolder) convertView.getTag();
        }
        //括号里一定要时string类型的,我有一次就写了一个position每次运行到这里app都会崩溃,心态都崩了
        viewHolder.tvContent.setText(message.getContent());
        return convertView;
    }
    private class ViewHolder{
        private TextView tvContent;
    }
}

接受样式与发送样式相似,这里仅给出发送的样式

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="7dip"
    android:paddingTop="7dip">
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="55dp"
        android:layout_toLeftOf="@+id/iv_message_from_head_image"
        android:gravity="center"
        android:paddingRight="20dip"
        android:textColor="@color/black"
        android:background="@drawable/duihuakuang"
        android:textSize="17sp"
        ></TextView>
    <ImageView
        android:id="@+id/iv_message_from_head_image"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:src="@mipmap/ic_launcher"
        ></ImageView>
</RelativeLayout>

duihuakuang布局由如下代码中的shapes下编写出

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--圆角-->
    <corners android:radius="10dp"/>
    <!--填充颜色-->
    <solid android:color="@color/浅蓝"/>
    <!--描边-->
    <stroke
        android:width="2dp"
        android:color="@color/black"/>
    <!--内边距-->
    <padding android:top="10dp" android:bottom="10dp"
        android:left="10dp" android:right="10dp"/>
</shape>

最终效果
在这里插入图片描述

二、GridView(网格视图)

布局文件

GridView是按照行列的方式显示内容,一般用于显示图表列表。
首先是修改主布局文件,其中

    android:numColumns="7":一行显示7列
    android:scrollbars="none":去掉滚动条
    android:verticalSpacing="10dp":两行之间的距离
    android:horizontalSpacing="10dp":两列之间的距离
<?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"
    tools:context=".wangge">
    <GridView
        android:id="@+id/wangge"
        android:numColumns="7"
        android:scrollbars="none"
        android:layout_marginBottom="10dp"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

然后是书写每个网格内部的代码

<?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">
    <ImageView
        android:id="@+id/wanggetu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:scaleType="centerCrop"
        android:src="@mipmap/zhengjing"/>
</LinearLayout>

活动文件

最后是活动文件与适配器,整体样式与ListView类似就不详细解释了
这里只给出一个疑惑解答:为什么将图像放入整数数组,这样可以存储吗?

这是由于android的SDK会自动为每个资源资源(图像,xml文件等)生成唯一的整数值。
比如当我们调用 R.drawable.resource_name如R.drawable.image1时,它就会返回一个Integer ID。
这就是为什么在您的代码中需要把R.drawable.image放入Integer Array的原因。
参考:为什么要在Android中将图像放入整数数组?

package com.example.h
public class wangge extends AppCompatActivity {
    private GridView data;
    private List<Integer> images;
    private BaseAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wangge);
        init();
        data = (GridView) findViewById(R.id.wangge);
        data.setAdapter(adapter=new GridViewAdapter(this));
    }
    private void init(){
        images=new ArrayList<>();
        for(int i=0;i<100;i++)
        {
            if(i%2==1){images.add(R.mipmap.zhengjing);}
            else{images.add(R.mipmap.ic_launcher);}
        }
    }
    public class GridViewAdapter extends BaseAdapter{
        private LayoutInflater inflater;
        public  GridViewAdapter(Context context){
            inflater=LayoutInflater.from(context);
        }
        //数据源的长度
        @Override
        public int getCount() {
            return images.size();
        }
        //每一行绑定的数据源
        @Override
        public Object getItem(int position) {
            return images.get(position);
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        //获取每一行的View
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if(convertView==null)
            {
                viewHolder=new ViewHolder();
                convertView=inflater.inflate(R.layout.grid_item,parent,false);
                viewHolder.imageView=(ImageView) convertView.findViewById(R.id.wanggetu);
                convertView.setTag(viewHolder);
            }
            else {
                viewHolder=(ViewHolder) convertView.getTag();
            }
            viewHolder.imageView.setImageResource(images.get(position));
            return convertView;
        }
        private class ViewHolder{private ImageView imageView;}
    }
}

最终效果如图所示

在这里插入图片描述

部分代码及内容参考自《Android app开发入门到精通》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

for-nothing

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

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

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

打赏作者

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

抵扣说明:

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

余额充值