이 튜토리얼에서는 CustomAdapter를 사용하여 Android ListView의 사용자 지정 행을 ArrayList
로 채울 것입니다. 또한 사용자 경험을 향상시키기 위해 ListView를 스크롤하는 동안 애니메이션을 적용할 것입니다.
Android ListView 사용자 정의 어댑터 개요
ArrayAdapter
를 사용하여 ArrayList에서 뷰를 채우는 가장 간단한 어댑터입니다. 이것을 이 튜토리얼에서 구현할 것입니다. CursorAdapter
와 같은 다른 어댑터도 있습니다. 이는 로컬 SQLite Database의 결과 세트에 직접 바인딩되며 데이터 소스로 Cursor를 사용합니다.
행 재활용
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가 표시됩니다.
프로젝트 구조
코드
우리는 DataModel을 객체로 사용하여 ArrayAdapter를 서브 클래스화하여 사용자 지정 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.journaldev.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 implements View.OnClickListener{
private ArrayList dataSet;
Context mContext;
// View lookup cache
private static class ViewHolder {
TextView txtName;
TextView txtType;
TextView txtVersion;
ImageView info;
}
public CustomAdapter(ArrayList 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) {
// 이 위치의 데이터 항목 가져오기
DataModel dataModel = getItem(position);
// 기존 뷰가 재사용되고 있는지 확인하고, 그렇지 않은 경우 뷰를 인플레이션합니다.
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 convertView;
}
}
위의 코드에서는 해당 행에 대한 설명과 함께 클릭 시 SnackBar를 표시하는 onClickListener
를 ImageView에 추가했습니다. 또한 목록 행은 스크롤될 때 애니메이션 효과가 적용됩니다. 두 가지 애니메이션 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 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","May 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, 2015"));
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) {
// 메뉴를 인플레이트합니다. 이렇게 하면 작업 표시줄에 항목이 추가됩니다.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 작업 표시줄 항목 클릭을 처리합니다. 작업 표시줄은
// Home/Up 버튼을 클릭하는 경우 자동으로 처리됩니다.
// AndroidManifest.xml에서 부모 활동을 지정하는 한.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
애플리케이션 실행 결과가 아래에 표시됩니다. 이 튜토리얼은 여기서 마무리됩니다. 최종 Android ListView 사용자 지정 어댑터 프로젝트는 아래 링크에서 다운로드할 수 있습니다.
Android ListView 사용자 지정 어댑터 프로젝트 다운로드
참고: API 가이드 목록 보기