带有自定义适配器示例教程的Android ListView

时间:2020-02-23 14:29:02  来源:igfitidea点击:

在本教程中,我们将使用CustomAdapter,该适配器将用" ArrayList"填充Android ListView的自定义行。
为了增强用户体验,我们将在滚动时为ListView设置动画。

Android ListView自定义适配器概述

从ArrayList填充视图的最简单的适配器是ArrayAdapter。
这就是我们将在本教程中实现的。
还有其他适配器,例如" CursorAdapter",它直接绑定到本地SQLite数据库的结果集,并且使用游标作为数据源。

回收行

实例化ListView并填充行时,将填充列表的整个高度。
之后,不会在内存中创建新的行项目。
当用户滚动列表时,离开屏幕的项目将保留在内存中以备后用,然后进入屏幕的每个新行都将重复使用保留在内存中的较旧行。

创建一个视图模板

让我们创建一个xml布局,以自定义方式连续显示各项。
row_item.xml

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical"
  android:padding="10dp">

  <TextView
      android:id="@+id/name"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentTop="true"
      android:text="Marshmallow"
      android:textAppearance="?android:attr/textAppearanceSmall"
      android:textColor="@android:color/black" 

  <TextView
      android:id="@+id/type"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@+id/name"
      android:layout_marginTop="5dp"
      android:text="Android 6.0"
      android:textColor="@android:color/black" 

  <ImageView
      android:id="@+id/item_info"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentEnd="true"
      android:layout_alignParentRight="true"
      android:layout_centerVertical="true"
      android:src="@android:drawable/ic_dialog_info" 

  <LinearLayout
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerInParent="true">

      <TextView
          android:id="@+id/version_heading"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="API: "
          android:textColor="@android:color/black"
          android:textStyle="bold" 

      <TextView
          android:id="@+id/version_number"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="23"
          android:textAppearance="?android:attr/textAppearanceButton"
          android:textColor="@android:color/black"
          android:textStyle="bold" 

  </LinearLayout>

</RelativeLayout>

在本教程中,我们将构建一个应用程序,该应用程序由显示文本描述和信息图标的行列表组成。
单击该行将显示带有该行文本元素的SnackBar。
单击信息将显示一个SnackBar,其中包含该行的特定信息。

代码

我们通过将ArrayAdapter子类化为DataModel作为对象来创建自定义ListView。
getView()是一种返回实际视图的方法,该实际视图用作ListView中特定位置的行。

content_main.xml包含ListView,如下所示。
content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  tools:context="com.theitroad.customlistview.MainActivity"
  app:layout_behavior="@string/appbar_scrolling_view_behavior"
  tools:showIn="@layout/activity_main">

  <ListView
      android:id="@+id/list"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      

</RelativeLayout>

ArrayList中包含的数据模型如下所示。
DataModel.java

public class DataModel {

  String name;
  String type;
  String version_number;
  String feature;

  public DataModel(String name, String type, String version_number, String feature ) {
      this.name=name;
      this.type=type;
      this.version_number=version_number;
      this.feature=feature;

  }

  public String getName() {
      return name;
  }
  
  public String getType() {
      return type;
  }
  
  public String getVersion_number() {
      return version_number;
  }
  
  public String getFeature() {
      return feature;
  }
  
}

下面显示了将DataModel填充到ListView中的CustomAdapter。
CustomAdapter.java

public class CustomAdapter extends ArrayAdapter<DataModel> implements View.OnClickListener{

  private ArrayList<DataModel> dataSet;
  Context mContext;

  //View lookup cache
  private static class ViewHolder {
      TextView txtName;
      TextView txtType;
      TextView txtVersion;
      ImageView info;
  }

  public CustomAdapter(ArrayList<DataModel> data, Context context) {
      super(context, R.layout.row_item, data);
      this.dataSet = data;
      this.mContext=context;

  }

  @Override
  public void onClick(View v) {

      int position=(Integer) v.getTag();
      Object object= getItem(position);
      DataModel dataModel=(DataModel)object;

      switch (v.getId())
      {
          case R.id.item_info:
              Snackbar.make(v, "Release date " +dataModel.getFeature(), Snackbar.LENGTH_LONG)
                      .setAction("No action", null).show();
              break;
      }
  }

  private int lastPosition = -1;

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
      //Get the data item for this position
      DataModel dataModel = getItem(position);
      //Check if an existing view is being reused, otherwise inflate the view
      ViewHolder viewHolder; //view lookup cache stored in tag

      final View result;

      if (convertView == null) {

          viewHolder = new ViewHolder();
          LayoutInflater inflater = LayoutInflater.from(getContext());
          convertView = inflater.inflate(R.layout.row_item, parent, false);
          viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
          viewHolder.txtType = (TextView) convertView.findViewById(R.id.type);
          viewHolder.txtVersion = (TextView) convertView.findViewById(R.id.version_number);
          viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);

          result=convertView;

          convertView.setTag(viewHolder);
      } else {
          viewHolder = (ViewHolder) convertView.getTag();
          result=convertView;
      }

      Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
      result.startAnimation(animation);
      lastPosition = position;

      viewHolder.txtName.setText(dataModel.getName());
      viewHolder.txtType.setText(dataModel.getType());
      viewHolder.txtVersion.setText(dataModel.getVersion_number());
      viewHolder.info.setOnClickListener(this);
      viewHolder.info.setTag(position);
      //Return the completed view to render on screen
      return convertView;
  }
}

在上面的代码中,我们在ImageView中添加了一个onClickListener,当单击时显示了SnackBar以及相应行的描述。

滚动时,列表行也具有动画效果。
下面给出了两个动画xml资源文件。
down_from_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
  android:shareInterpolator="@android:anim/decelerate_interpolator">
  <translate
      android:fromXDelta="0%" android:toXDelta="0%"
      android:fromYDelta="-100%" android:toYDelta="0%"
      android:duration="400" 
</set>

up_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
  android:shareInterpolator="@android:anim/decelerate_interpolator">
  <translate
      android:fromXDelta="0%" android:toXDelta="0%"
      android:fromYDelta="100%" android:toYDelta="0%"
      android:duration="400" 
</set>

CustomAdapter设置为ListView的MainActivity.java在下面定义。
随之而来的是随机填充的DataModel对象的ArrayList。

MainActivity.java

public class MainActivity extends AppCompatActivity {

  ArrayList<DataModel> dataModels;
  ListView listView;
  private static CustomAdapter adapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
      setSupportActionBar(toolbar);

      listView=(ListView)findViewById(R.id.list);

      dataModels= new ArrayList<>();

      dataModels.add(new DataModel("Apple Pie", "Android 1.0", "1","September 23, 2008"));
      dataModels.add(new DataModel("Banana Bread", "Android 1.1", "2","February 9, 2009"));
      dataModels.add(new DataModel("Cupcake", "Android 1.5", "3","April 27, 2009"));
      dataModels.add(new DataModel("Donut","Android 1.6","4","September 15, 2009"));
      dataModels.add(new DataModel("Eclair", "Android 2.0", "5","October 26, 2009"));
      dataModels.add(new DataModel("Froyo", "Android 2.2", "8","Jan 20, 2010"));
      dataModels.add(new DataModel("Gingerbread", "Android 2.3", "9","December 6, 2010"));
      dataModels.add(new DataModel("Honeycomb","Android 3.0","11","February 22, 2011"));
      dataModels.add(new DataModel("Ice Cream Sandwich", "Android 4.0", "14","October 18, 2011"));
      dataModels.add(new DataModel("Jelly Bean", "Android 4.2", "16","July 9, 2012"));
      dataModels.add(new DataModel("Kitkat", "Android 4.4", "19","October 31, 2013"));
      dataModels.add(new DataModel("Lollipop","Android 5.0","21","November 12, 2014"));
      dataModels.add(new DataModel("Marshmallow", "Android 6.0", "23","October 5, 2014"));

      adapter= new CustomAdapter(dataModels,getApplicationContext());

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

              DataModel dataModel= dataModels.get(position);

              Snackbar.make(view, dataModel.getName()+"\n"+dataModel.getType()+" API: "+dataModel.getVersion_number(), Snackbar.LENGTH_LONG)
                      .setAction("No action", null).show();
          }
      });
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
      //Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.menu_main, menu);
      return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
      //Handle action bar item clicks here. The action bar will
      //automatically handle clicks on the Home/Up button, so long
      //as you specify a parent activity in AndroidManifest.xml.
      int id = item.getItemId();

      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
          return true;
      }

      return super.onOptionsItemSelected(item);
  }
}