Custom ListView 와 adapter pattern
먼저 ListView 에 대해 알아보자.
보통의 ListView 를 생각하면
연락처 나 메시지 나 목록을 표시해야할때(SMS) 앱 등
필요한 위젯입니다.
Google play (구 버전)
하지만 ListView 는 선택 위젯입니다.
(일반 위젯이 아니다.)
안드로이드에서는 리스트뷰처럼 여러 개의 아이템 중에
하나를 선택할 수 있는 위젯들을 특별히 '선택 위젯'이라고 부름
선택위젯은 직접 데이터를 설정 할 수가 없습니다.
선택위젯에 데이터를 설정하기위해 사용하는게
Adapter pattern 이다.
우리는 이 Adapter 에서 만들어주는 getView() 메서드를 이용해 아이템을 표시해준다.
이해를 위한 예시
ListView 를 정의해보자면
Adapter를 사용해 데이터를 표시하는 View 입니다.
만든 CustomListView 예제를 살펴보자면
메모장처럼 제목 과 내용을 적고 저장 해주면
제목을 누르면 내용이 Toast로 띄어주는 그러한 예제이다.
먼저 Layout을 살펴보자.
일단 ListView 를 띄어줄 MainLayout 에는
간단히 ListView 만 추가해주면 된다
(나는 데이터를 잠깐 저장하기위한 FloatingActionButton 을 사용해줬다.)
MainActivity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
이런식으로 MainActivity에 추가 해주면된다.
하지만 우리는 리스트뷰에 표시할 아이템을 위해
item_Layout 을 만들어 줘야한다
item_default_data
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:id="@+id/titleText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
android:text="제목"
android:layout_margin="10dp"/>
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="@android:color/darker_gray">
</View>
</LinearLayout>
이런식으로 우리가 보여줄 item 을위한
Layout이 구현됬으면 이제부터
ListAdapter 의 코드를 보자.
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
public class ListAdapter extends ArrayAdapter<Data> {
private List<Data> dataList;
private Context context;
private int layout;
public ListAdapter(@NonNull Context context, int resource, @NonNull List<Data> objects) {
super(context, resource, objects);
this.dataList = objects;
this.context = context;
this.layout = resource;
}
@NonNull
@Override
public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = convertView;
if(view == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(layout, null);
}
TextView titleText = view.findViewById(R.id.titleText);
Data data = dataList.get(position);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, dataList.get(position).getContent(), Toast.LENGTH_SHORT).show();
}
});
titleText.setText(data.getTitle());
return view;
}
public void setDataList(List<Data> dataList){
this.dataList = dataList;
}
}
이 Adapter를 통해 ListView 의
한 item 에 표시될 정보를 설정할수 있습니다.
먼저 생성자를 만들어준다.
그리고 여기서 살펴볼점은
1) LayoutInflater란?
xml 에 정의된 Resource(자원) 들을 View 형태로 변환시켜줍니다.
우리가 보통 Activity를 만들고 자바 코드안에
onCreate() 메서드에 기본으로 추가되는
setContentView(R.layout.activity_main); 와 같은 원리라고 생각하시면 됩니다.
2) ListView에 view 값이 Null 이라면?
ListView 에 아이템을 표시해줄 getView() 메서드를 보자면
view 값이 Null 이라면 view 가 없게 되닌까
View 가 없으니 inflater 해줘서 할당을 해줍니다.
xml에 정의된 레이아웃을 자바코드로 동적 할당해서 매칭시킨다고 보시면됩니다
그리고 view 의 아이템이 눌리게되면 onClick 이벤트가 실행되
Toast 를 띄어주게 되는데요 Toast 내용은 Content 인 즉 내용이 띄어지고
titleText.setText(data.getTitle());
즉 제목인 Title 을 ListView 에 텍스트로 띄어줍니다.
먼저 이렇게만 작성한다면 .
private List<Data> dataList;
이 코드에 Data 가 빨간밑줄 일 것 입니다.
우리는 저 Data class 를 만들어줘야합니다.
그래서 저는
import java.io.Serializable;
public class Data implements Serializable {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Data(String title, String content) {
this.title = title;
this.content = content;
}
}
이런식으로 Data class를 만들어 줬는데요.
이 class 는 아이템에 출력될 데이터를 위한 클래스 입니다.
이렇게 Data를 위한 클래스, Adapter, item_default_data 까지 모두 완성해준다면
이제 상단 액션바에 Action(추가)인 menu xml 코드를 작성해주자
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action"
android:title="추가"
app:showAsAction="always">
</item>
</menu>
이런식으로 작성하게 되면 액션바에 추가라는 이름이 적히는데요
app:showAsAction="always" 라는 속성은
항상 보이게 설정해줍니다
이 코드는 이예제의 WriteActivity 에 넘어가서 작성할수 있도록 버튼같은 역활을합니다.
이제 WriteActivity 를 봅시다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".WriteActivity"
android:orientation="vertical"
android:weightSum="360">
<EditText
android:id="@+id/titleEditText"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:hint="제목"
android:layout_margin="10dp"/>
<EditText
android:id="@+id/contentEditText"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="300"
android:layout_margin="10dp"
android:hint="내용"
android:lines="10"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/saveFab"
android:src="@drawable/ic_save_black_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:layout_gravity="right"/>
</LinearLayout>
간단하게
제목을 적을 EditText 와 내용을적을 EditText랑
데이터를 저장해줄 FloatingActionButton
이 구성되 있습니다.
이제 본격적으로
WriteActivity의 xml 코드를 보자면
import android.content.Intent;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class WriteActivity extends AppCompatActivity {
private EditText titleEditText;
private EditText contentEditText;
private FloatingActionButton saveFab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write);
init();
setListeners();
}
private void init(){
titleEditText = findViewById(R.id.titleEditText);
contentEditText = findViewById(R.id.contentEditText);
saveFab = findViewById(R.id.saveFab);
}
private void setListeners(){
saveFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String title = titleEditText.getText().toString();
String content = contentEditText.getText().toString();
if(!title.equals("") && !content.equals("")){
Data data = new Data(titleEditText.getText().toString(), contentEditText.getText().toString());
Toast.makeText(getApplicationContext(), "성공적으로 작성하셨습니다.", Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.putExtra("data", data);
setResult(RESULT_OK,intent);
finish();
}
}
});
}
}
간단하게 해석하자면 각각의 EditText의 id를 찾아주고
saveFab은 FloatingActionButton의 id 이다.
saveFab을 눌르면 onClick 이벤트가 실행된다.
내용은 titleEditText 내용을 가져와String 형태로 title 에 담고
contentEditText의 내용도 가져와 String 형태로 Content에 담습니다.
if문 은 title 과 content 의 값이 있을때 실행되는데요
먼저 Data class의 객체를 만들어줘 titleEditText 내용, contentEditText의 내용
을 가져와 data 에 저장해줍니다.
그뒤에 Toast로 "성공적으로 작성하셨습니다." 라는 Toast를 띄어줍니다.
그뒤에 Intent 객체를 만들어주고
.putExtra() 로 "data" 라는 키로 data를 넘겨줍니다.
그리고
setResult(RESULT_OK,intent);
finish();
이코드로 Activity를 종료해줍니다.
이제 마지막으로 data값을 ListView 에 띄어주는 MainActivity.java code 를보자.
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<Data> dataList;
private ListView listView;
private ListAdapter listAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
setListView();
}
private void setListView(){
listAdapter = new ListAdapter(getApplicationContext(), R.layout.item_default_data, dataList);
listView.setAdapter(listAdapter);
}
private void init(){
dataList = new ArrayList<>();
listView = findViewById(R.id.listView);
}
public void changeData(Data data){
dataList.add(data);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action:
Intent intent = new Intent(this, WriteActivity.class);
startActivityForResult(new Intent(this, WriteActivity.class), 3000);
return true;
default:
return false;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK && requestCode == 3000){
Log.d("asdf", "asdf");
Data receiveData = (Data) data.getSerializableExtra("data");
dataList.add(receiveData);
setListView();
}
}
}
먼저 onCreate 에 호출된 init(); 과 setListView(); 를 보면
init(); 은 dataList 에 변수에 arrayList 형식으로 데이터를 저장합니다.
그리고 listView 의 id 를 listView에 저장해줍니다.
그리고 setListView();를 보면
Adapter 의 객체를 만들어주는데 Layout은 아까 만들어준
item_default_data 을 사용해주고 내용은 datalist 입니다.
그리고 listView.setAdapter(listAdapter);
Adapter의 값을 정해주고
이제 다른 코드를 보자면
public void changeData(Data data){
dataList.add(data);
}
를 보면 매개변수는 data를 받아오고
dataList.add(data); 를 이용해 데이터를 더해준다.
그리고
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
보게 되면 Inflater 를 이용해
xml 로 정의된 menu 를 실제 객체화를 해줍니다.
이 코드를 써준이유는
미리 xml 을 만들어준 뒤 java code 에서
inflater 을 활용해 바로 view 를 생성할 수있기 때문이다.
그리고
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.action:
Intent intent = new Intent(this, WriteActivity.class);
startActivityForResult(new Intent(this, WriteActivity.class), 3000);
return true;
default:
return false;
}
}
이 코드를 보자
switch 문을 사용했다. 먼저 item 의 id 를 얻어
id 가 action 인 item 은 아까 추가해준
item_default_data 이 xml 이다.
item 의 id 가 action 이면 추가 버튼을 눌르게된다면 WriteActivity 로 intent 로 넘어가고
startActivityForResult() 를 이용해 WriteActivity 를 시작하고 그 Activity로부터 결과를 수신할 수도 있습니다.
값은 3000으로 지정해줬습니다.
만약 id 가 action 아니면 false를 return 해줍니다.
그리고 마지막으로
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK && requestCode == 3000){
Log.d("asdf", "asdf");
Data receiveData = (Data) data.getSerializableExtra("data");
dataList.add(receiveData);
setListView();
}
}
}
이 코드를 해석하자면 WriteActivity의 data를 받는 메서드 이다.
처리된 결과 코드 resultCode 가 RESULT_OK 이거나 requestCode 가 3000 인 이유는 위에
startActivityForResult(new Intent(this, WriteActivity.class), 3000);
이코드를 이용해 WriteActivity 를 시작하고 그 Activity로부터 결과를 수신 했습니다
Data receiveData = (Data) data.getSerializableExtra("data");
이 코드로 아까 putExtra() 의 키값인 data로 넘겨준 data를 받는다.
그리고 dataList.add(receiveData); 로 data를 더해준다.
그리고 마지막으로
setListView(); 를 이용해세
private void setListView(){
listAdapter = new ListAdapter(getApplicationContext(), R.layout.item_default_data, dataList);
listView.setAdapter(listAdapter);
}
을 호출해 준다.
이렇게 되면 ListView가 띄어지는걸 볼수있다.
여기까지 adapter pattern 을 이용한 CustomListView 사용법 이였다.
굳!
'Android > Android ( JAVA )' 카테고리의 다른 글
Android (일곱번째 수업 - Fragment와 TapLayout) (1) | 2018.10.04 |
---|---|
Android (여섯번째 수업 - RecyclerView) (0) | 2018.09.17 |
Android (네번째 수업 - Glide , Ratingbar) (0) | 2018.08.14 |
Android (세번째 수업 - 안드로이드 Design Support Library( Snackbar, AlertDialog )) (0) | 2018.08.09 |
Android (두번째 수업 - 안드로이드 인텐트 / 인텐트 콜백(Intent Callback)) (0) | 2018.07.27 |