带有自定义适配器示例教程的Android ListView
在本教程中,我们将使用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); } }