Android关于 ListView知识点

本文详细介绍了Android中的ListView控件,包括如何显示数据,ListView的数据适配原理,以及局部刷新的两种方法。此外,还讨论了如何实现ListView的Item单选和多选效果,涉及到choiceMode属性和Checkable接口的使用。

一. 关于ListView控件展现数据及原理
它以列表的形式展示具体数据内容,当数据过多时会出现滚动条,并且能够根据数据的长度适应屏幕的显示。
ListView是一个列表视图,由很多Item(条目)组 成,每个Item的布局都是相同的,这个Item会单独使用一个XML进行定义。
为了使ListView有数据显示需要进行数据适配,即数据与视图之间的桥梁,将复杂的数据换成用户可以接受的方式呈现。
Android实验三简单例子

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.example.hp.listview.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

simple_items.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="wrap_content"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/header"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:textSize="18dp"
        android:paddingLeft="10dp"
        />
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/images"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_margin="0dp"
            android:layout_alignParentRight="true"
           />
    </RelativeLayout>
</LinearLayout>

MainActivity.java

package com.example.hp.listview;

import android.content.Context;
import android.graphics.Color;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

     private String[] names=new String[]{"Lion","Tiger","Monkey","Dog","Cat","elephant"};
     private int[] image=new int[]{R.drawable.lion,R.drawable.tiger,R.drawable.monkey,R.drawable.dog ,R.drawable.cat,R.drawable.elephant};
     @Override
    protected void onCreate(Bundle savedInstanceState){
         super.onCreate(savedInstanceState);
         requestWindowFeature(Window.FEATURE_NO_TITLE); //设置当前的Activity无Title并且全屏
         setContentView(R.layout.main);  //所在的Activity采用R.layout下的main布局文件进行布局
         final int color1=0xFFC5B5FF;
         final int color2=0xFFFFFFFF;
         //创建一个list集合,list集合的元素是Map
         List<Map<String,Object>> ListItems=new ArrayList<Map<String, Object>>();
         for(int i=0;i<names.length;i++){
             Map<String,Object> listItem=new HashMap<String,Object>();
             listItem.put("header",names[i]);
             listItem.put("images",image[i]);
             ListItems.add(listItem);  //加入list集合
         }
         //创建一个SimpleAdapter,因为在使用ListView时需要进行数据适配

         SimpleAdapter simpleAdapter=new SimpleAdapter(this,ListItems,R.layout.simple_items,new String[]{"header","images"},new int[]{R.id.header,R.id.images});
         final ListView list=(ListView)findViewById(R.id.mylist);
         //为ListView设置Adapter
         list.setAdapter(simpleAdapter);
         list.setOnItemClickListener(new AdapterView.OnItemClickListener(){
             public void onItemClick(AdapterView<?> parent,View view,int position,long id){
                 int flag=0;
                 System.out.println(names[position]+position+"被单击");
                 switch (flag){
                     case 0:
                         view.setSelected(true);
                         CharSequence text=names[position];
                         int duration=Toast.LENGTH_SHORT;
                         Toast toast=Toast.makeText(MainActivity.this,text,duration);
                         toast.show();
                         flag=1;
                         break;
                     case 1:
                         view.setBackgroundColor(color2);
                         view.setSelected(false);
                         CharSequence text1=names[position];
                         int duration1=Toast.LENGTH_SHORT;
                         Toast toast1=Toast.makeText(MainActivity.this,text1,duration1);
                         toast1.show();
                         flag=0;
                         break;
                 }
             }
         });
         list.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
             public void onItemSelected(AdapterView<?> parent, View view, int position, long id){
                 System.out.println(names[position]+"选中");
             }
             public void onNothingSelected(AdapterView<?> parent){

             }

         });

     }

    }

在这里插入图片描述
二. ListView局部刷新
对ListView有了基本的了解,了解到在Android开发中我们经常会用到listview的数据和界面刷新动作,我们基本上用到的都是Adapter.notifyDataSetChanged()方法。这个方法的原理是利用观察者模式对我们的数据源进行监听,当我们的数据源发生变化的时候,会调用Adapter的getView()方法进行整个界面的刷新。getview()会调用多次,刷新了好多个不需要刷新的item,这样的话相对而言,降低了效率。但是,我们有的情况下是只需要对某个item的数据进行刷新就可以了。如点击下图中的任一条目可以实现局部刷新,而不会全部刷新。
在这里插入图片描述
1.ListView实现局部刷新方法一: 更的新对应view的内容 这种方法先通过listView.getChild(position)拿到要更新的对应的item布局文件,然后再通过findViewById找到对应的控件进行设置。

package cn.bluemobi.dylan.listviewupdate;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import cn.bluemobi.dylan.listviewupdate.adapter.CommonAdapter;
import cn.bluemobi.dylan.listviewupdate.adapter.CommonViewHolder;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private List datas;
    private CommonAdapter commonAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        updateOneTest();
    }
 /**
     * 只是局部更新某个界面
     */
    private void updateOneTest() {
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.listview);
        datas = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            datas.add("万能适配器测试" + i);
        }
        commonAdapter = new CommonAdapter(this, datas, R.layout.item) {

            @Override
            protected void convertView(View item, String s) {
                TextView textView = CommonViewHolder.get(item, R.id.textView);
                textView.setText(s);
            }
        };
        listView.setAdapter(commonAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                datas.set(position, "update 万能适配器测试" + position);
                updateSingle(position);
                 }
        });
    }

      /**
     * 第一种方法 更新对应view的内容
     *
     * @param position 要更新的位置
     */
    private void updateSingle(int position) {
        /**第一个可见的位置**/
        int firstVisiblePosition = listView.getFirstVisiblePosition();
        /**最后一个可见的位置**/
        int lastVisiblePosition = listView.getLastVisiblePosition();

        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/
        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {
            /**获取指定位置view对象**/
            View view = listView.getChildAt(position - firstVisiblePosition);
            TextView textView = (TextView) view.findViewById(R.id.textView);
            textView.setText(datas.get(position));
        }
    }
}

2.ListView实现局部刷新方法二: 调用一次getView()方法
这种方法是调用适配器对应的getView方法,用它里面的代码对界面进行刷新。

package cn.bluemobi.dylan.listviewupdate;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import cn.bluemobi.dylan.listviewupdate.adapter.CommonAdapter;
import cn.bluemobi.dylan.listviewupdate.adapter.CommonViewHolder;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private List datas;
    private CommonAdapter commonAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        updateOneTest();
        }

    /**
     * 只是局部更新某个界面
     */
    private void updateOneTest() {
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.listview);
        datas = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            datas.add("万能适配器测试" + i);
        }
        commonAdapter = new CommonAdapter(this, datas, R.layout.item) {

            @Override
            protected void convertView(View item, String s) {
                TextView textView = CommonViewHolder.get(item, R.id.textView);
                textView.setText(s);
            }
        };
        listView.setAdapter(commonAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                datas.set(position, "update 万能适配器测试" + position);
               updateItem(position);

            }
            });
    }

     /**
     * 第二种方法 调用一次getView()方法;
     * Google推荐的做法
     * @param position 要更新的位置
     */
    private void updateItem(int position) {
        /**第一个可见的位置**/
        int firstVisiblePosition = listView.getFirstVisiblePosition();
        /**最后一个可见的位置**/
        int lastVisiblePosition = listView.getLastVisiblePosition();

        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/
        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {
            /**获取指定位置view对象**/
            View view = listView.getChildAt(position - firstVisiblePosition);
            commonAdapter.getView(position, view, listView);
        }

    }
    }

最后封装在万能适配器当中

package cn.bluemobi.dylan.listviewupdate.adapter;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;

import java.util.List;

/**
 * Created by yuandl on 2016-10-13.
 * 万能适配器
 */

public abstract class CommonAdapter extends BaseAdapter {
    private Context context;
    private List datas;
    private int layoutId;
    public CommonAdapter(Context context, List datas, int layoutId) {
        this.context = context;
        this.datas = datas;
        this.layoutId = layoutId;
    }

    @Override
    public int getCount() {
        return datas == null ? 0 : datas.size();
    }

    @Override
    public T getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }
@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(layoutId, null);
        }
        Log.d("listview", "---------getView()-----------");
        T t = getItem(position);
        convertView(convertView, t);
        return convertView;
    }

    /**
     * 局部更新数据,调用一次getView()方法;Google推荐的做法
     *
     * @param listView 要更新的listview
     * @param position 要更新的位置
     */
    public void notifyDataSetChanged(ListView listView, int position) {
        /**第一个可见的位置**/
        int firstVisiblePosition = listView.getFirstVisiblePosition();
        /**最后一个可见的位置**/
        int lastVisiblePosition = listView.getLastVisiblePosition();

        /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/
        if (position >= firstVisiblePosition && position <= lastVisiblePosition) {
            /**获取指定位置view对象**/
            View view = listView.getChildAt(position - firstVisiblePosition);
            getView(position, view, listView);
        }

    }

    /**
     * 需要去实现的对item中的view的设置操作
     *
     * @param item
     * @param t
     */
    protected abstract void convertView(View item, T t);

}

我们每次更新的时候只需要调用notifyDataSetChanged(ListView listView, int position),传入对应的要更新的listview和要更新的位置position即可

三 ListView实现Item单选 多选效果
首先,在ListView的布局中设置了android:choiceMode属性后,item布局需要实现checkable,才有选中效果。
checkable接口:

/**
 * Defines an extension for views that make them checkable.
 *
 */
public interface Checkable {

    /**
     * 设置是否选中。
     * 当我们点击item的时候,
     * 会调用这个方法。
     */
    void setChecked(boolean checked);

    /**
     *判断是否选中
     */
    boolean isChecked();

    /**
     * 如果当前是选中的状态,
     * 调用该方法后取消选中,
     * 反之,选中
     */
     void toggle();
}

实现单选效果:
1、 ListView布局中android:choiceMode设置为singleChoice。
2、选取实现了checkable接口的View或者ViewGroup作为item布局控件。
当item展示的数据比较简单,例如就是一段文本,item布局可以直接使用系统自带的CheckedTextView控件,该控件有一个属性:android:checkMark=”?android:listChoiceIndicatorSingle”为单选样式;“?android:listChoiceIndicatorMultiple”为多选样式。若要修改显示的样式,可以自己写一个selector,然后checkMark指定为这个selector。例如:
在drawable文件夹下面创建一个ic_hideable_item.xml文件

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="false" android:drawable="@mipmap/ic_hideable_item_unchecked" />
    <item android:drawable="@mipmap/ic_hideable_item_checked" />
</selector>

checkMark指定为上面的那个xml文件:

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_single_choice"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeightSmall"
    android:textSize="14sp"
    android:gravity="center_vertical"
    android:checkMark="@drawable/ic_hideable_item"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">
</CheckedTextView>

实现多选效果:
1、 ListView布局中android:choiceMode设置为multipleChoice。
2、选取实现了checkable接口的View或者ViewGroup作为item布局控件。
这里笔者自定义一个控件实现Checkable接口。代码如下:

public class CheckableLayout extends RelativeLayout implements Checkable {

private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};

private boolean mChecked;

public CheckableLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}
@Override
public void setChecked(boolean b) {

    if (b != mChecked){
        mChecked = b;
        refreshDrawableState();
    }
}

@Override
public boolean isChecked() {
    return mChecked;
}

@Override
public void toggle() {

    setChecked(!mChecked);
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {

    final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);

    if (isChecked()) mergeDrawableStates(drawableState, CHECKED_STATE_SET);

    return drawableState;
}

}
应用到item布局:

<com.jm.customchoicelist.CheckableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">


    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:duplicateParentState="true"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="30dp"
        android:textColor="@color/hideable_text_color"
        tools:text="测试数据"/>
        <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:duplicateParentState="true"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="20dp"
        android:src="@drawable/ic_hideable_item"/>

</com.jm.customchoicelist.CheckableLayout>

上面TextView、ImageView控件中的android:duplicateParentState属性,
该属性表示当前控件是否跟随父控件的状态(点击、焦点等)。若将TextView的该属性置为false,则文字无变色效果;若将ImageView的该属性置为false,则无选中效果。

单选—> 通过ListView的getCheckedItemPosition()获取选中的位置。
多选—> 通过ListView的getCheckedItemPositions()得到一个SparseBooleanArray,key为position,value为是否选中。

mSingleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                int checkedItemPosition = mSingleListView.getCheckedItemPosition();
                Toast.makeText(MainActivity.this, "you chose item " + checkedItemPosition, Toast.LENGTH_SHORT).show();
            }
        });


        mMultipleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                SparseBooleanArray checkedItemPositions = mMultipleListView.getCheckedItemPositions();
                boolean isChecked = checkedItemPositions.get(position);
                Toast.makeText(MainActivity.this, "item " + position + " isChecked=" + isChecked, Toast.LENGTH_SHORT).show();
            }
        });

作者:杨雅茹
原文地址 https://blog.youkuaiyun.com/qq_40946053/article/details/90550202

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值