화면에 전화번호부처럼 리스트를 만들고 아이템 추가해보기
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="추가" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPersonName"
android:text="레드벨벳" />
<EditText
android:id="@+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPersonName"
android:text="010-8000-8000" />
<!-- ems가 들어가있어서 글자 크기가 커진거 같다고 했음
그리고 weight로 크기 조절-->
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="0dp"
android:layout_marginTop="49dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp" />
</RelativeLayout>
디자인 팔레트에 리스트뷰가 있음, 그거 이용해서 리스트 만들면 됨<br>
하지만, 저 아이템들 각각의 요소를 세부적으로 정해주기 위해서는 item 레이아웃을 따로 만들어야 함
singerItem.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">
<ImageView
android:id="@+id/imageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@mipmap/ic_launcher"/>
<!-- mipmap/ic_launcher은 기본적인 안드로이드 보이 이미지-->
<!-- 이미지를 각각의 아이템마다 다르게 하기 위한 아이디 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="10dp">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="이름"
android:textSize="30dp"
android:textColor="@color/colorPrimaryDark"/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="전화번호"
android:textSize="24dp"
android:textColor="@color/colorAccent"
android:layout_marginTop="8dp"/>
</LinearLayout>
</LinearLayout>
보면 각각 아이템들 왼쪽에는 사진, 오른쪽에는 이름과 전화번호 등의 세부 내용을 넣을 수 있도록 디자인 팔레트에서 요소들 가져와서 배치
먼저 리스트를 만들기 전에 리스트 목록에 들어갈 아이템의 클래스를 만들어 각각의 객체를 생성할 수 있도록 해야함.
SingerItem.java
public class SingerItem {//데이터를 담아두기 위한 클래스
String name;
String mobile;
int resId;
public SingerItem(String name, String mobile, int resId) {//생성자
this.name = name;
this.mobile = mobile;
this.resId = resId;
}
public String getName() {//getter, setter 이용하는 이유는 최대한 직접 접근보다(name.name) 간접적으로 메소드를 통해
//접근하라는 것
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
//데이터를 담아두기 위한 클래스를 위해 편리하게 데이터를 담아둘 수 있는 메서드
//또는 이 안에서 사용할 메소드를 생성
public int getResId() {
return resId;
}
public void setResId(int resId) {
this.resId = resId;
}
@Override
public String toString() {//toString() 메소드를 통해 객체 안의 내용을 문자열로 바로 출력해줌
return "SingerItem{" +
"name='" + name + '\'' +
", mobile='" + mobile + '\'' +
'}';
}
}
//걸그룹의 데이터를 정의
이제 정의해놓은 내용을 하나하나 뷰에 붙여야해
아까 디자인 팔레트에 만든 각각 아이템의 내용을 layoutInflater를 이용해 분할해서 붙임. (MainActivity -> view이용 -> List아이템들)
SingerItemView.java
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class SingerItemView extends LinearLayout {
TextView textView1;
TextView textView2;
ImageView imageView;
public SingerItemView(Context context) {//생성자
super(context);
init(context); //각각의 생성자에서 초기화 메서드 호출
}
public SingerItemView(Context context, AttributeSet attrs) {//생성자
super(context, attrs);
init(context);
}
private void init(Context context){//초기화하는 메서드
//여기는 우리의 singer_item.xml파일을 인플레이션해서 여기다가 붙여주는 역할을 하는 거
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//인플레이션을 위해 레이아웃 인플레이션 서비스를 이용해 시스템 서비스를 참조할 수 있음
//화면이 없는 백그라운드 서비스가 돌고 있음, 단말이 시작됐을 때 단말에서 기본적으로 실행시켜 놓는 서비스를 시스템 서비스라고 함.(그 중 레이아웃 인플레이터를 이용해 서비스 받았으니까 이거 쓰자)
inflater.inflate(R.layout.singer_item, this, true);//내가 만든 singer_item.xml을 대상으로 하고, this는 우리가 LinearLayout을 상속받았는데 , 그러면 singer_item.xml에 있는 최상위 레이아웃이 LinearLayout이니까
//인플레이션 해서 여기다가 붙일 수 있음(그래서 this라고 넣어주는거)
//(즉, 내 생각 = 전에 토스트 보더 만들 때도 레이아웃 인플레이션을 했었는데 그 때는 레이아웃 루트에 가져다 붙였음(메인 액티비티에서) 그것은 따로 가져다 붙일 수 있는 뷰를 안 만들어서 루트에 붙인거고
//여기 자바는 일부러 가져다 붙일 수 있는 뷰를 만든 거기 때문에 이 액티비티에 붙이겠다라고 하는 것으로 this를 쓴 것
textView1 = (TextView)findViewById(R.id.textView1);//이 텍스트뷰를 클래스 안에서 어디서든 사용 가능하게 하려면 위에 바깥에 선언해야 함
textView2 = (TextView)findViewById(R.id.textView2);
//이렇게 인플레이션 해주면 xml 안에 들어있는 텍스트 뷰나 이미지 뷰를 참조 가능
imageView = (ImageView)findViewById(R.id.imageView);
}
//이렇게 한 SingerItemView를 어댑터 안에서 쓸건데(데이터니까), 여기다가 데이터를 설정해줄 일이 있을 것임,
// 그러면 SingerItemView를 이용해서 만든 객체에다가 우리가 메서드를 호출하면 바로 데이터를 설정할 수 잇게 메서드를 여기다가 정의를 할거임
public void setName(String name){//텍스트뷰1에는 이름 설정
textView1.setText(name);
}
public void setMobile(String mobile){//텍스트뷰2에는 전화번호 설정
textView2.setText(mobile);
}
public void setImage(int resId){
imageView.setImageResource(resId);
}//이미지 뷰에 이미지 설정하는 거는 아까 내가 추가한 R.drawable 밑에 있는 거니까 메소드 설정하는 메소드 만들고
//Integer 값으로 관리가 되므로 resId라고 해서, resId를 파라미터로 전달하게 함
//데이터에 이미지 값 들어감
}
//SingerItem.xml에서 최상위 레이아웃이 LinearLayout 이었는데 이 자바 클래스도 LinearLayout을 상속해서 만들면 레이아웃 인플레이션 할 수 있음(즉, 메모리 객체화한 다음에 여기다 그대로 붙여줄 수 있다.)
//xml을 메모리 객체화해서 여기 레이아웃에 붙여가지고 자바 소스파일로 아이디 가져와서 사용가능(객체)
//이렇게 해서 하나의 아이템을 보여주기 위해 뷰를 하나 정의한 것, 부분 화면처럼 정의가 됨,
//그럼 이제 이후에는 이걸 가지고 이제 MainAcitivity에서 정의하는 어댑터 안에서 getView를 이용해서 뷰 객체를 만들고 리턴을 하도록 만들거임
//전체 정리 내생각
//지금까지 하나의 아이템을 만들기 위해 부분화면인 xml을 만들고 그걸 객체화해서 뷰에 가져다 붙이기 위해 자바 클래스에서 객체화하고 뷰에 가져다 붙임
//객체화했기 때문에 xml 안에 있는 텍스트뷰를 아이디로 가져올 수 있게 되었고, 이 뷰 안의(아이템 안에) 이름과 전화번호 설정도 할 수 있게 됨
//이 정의된 내용을 담고 있는 뷰 전체를 객체화하기 위해 MainActivity에서 getView()를 이용해 뷰 객체를 만들고 이 객체를 리턴 받아서 사용할거라는 뜻
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
SingerAdapter adapter;
EditText editText1;
EditText editText2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//단지 화면을 객체화 시켜주는
editText1 = (EditText)findViewById(R.id.editText1);//에디트 텍스트에 친 정보를 리스트뷰에 추가하기 위함
editText2 = (EditText)findViewById(R.id.editText2);
ListView listView = (ListView)findViewById(R.id.listView);//지금 얘는 데이터 담을 수 있는 것만 정의해 놓은 거고 시제 데이터는 아예 없음
//어댑터를 만들어야 하는데 안드로이드 api에서 제공하는 다양한 어댑터들이 있음
//어레이 어댑터라든가 이런 거 쓰면 코드를 간단히 만들 수도 있음
//근데 리스트 뷰를 만들고 나서 리스트 뷰 안에 들어가는 게 이미지, 텍스트 이런 게 있다고 하면
//그런 것들을 커스터마이징해서 또 쓰게 되니까 그래서 처음부터 이 리스트 뷰 안에 들어가는 아이템의 모양을
// 걍 내가 원하는대로 바꿀 수 있게 구조를 만드는 게 좋음(그래서 각각의 아이템을 위한 별도의 뷰를 부분 화면처럼 만들 거임
// 그리고 별도의 뷰를 어댑터에 넣어줄 거임)
//어댑터 경우에도 나중에! 커스터마이징하기 쉽게 직접 정의 할 거임
//리스트 뷰에다가 어댑터 객체를 만든 다음에 설정을 해주는 과정이 필요
adapter = new SingerAdapter();//어댑터 객체 만들고
//데이터가 없으니 데이터 추가
adapter.addItem(new SingerItem("소녀시가", "010-9999-9999", R.drawable.face));
adapter.addItem(new SingerItem("소녀시에", "010-9999-9998", R.drawable.face2));
adapter.addItem(new SingerItem("소녀시고", "010-9999-9990", R.drawable.face_two));
adapter.addItem(new SingerItem("소녀시기", "010-9999-9997", R.drawable.iconfinder1));
adapter.addItem(new SingerItem("소녀시먹", "010-9999-9996", R.drawable.iconfinder2));
adapter.addItem(new SingerItem("소녀시고", "010-9999-9995", R.drawable.iconfinder3));
adapter.addItem(new SingerItem("소녀시싶", "010-9999-9994", R.drawable.iconfinder4));
adapter.addItem(new SingerItem("소녀시다", "010-9999-9993", R.drawable.face));
//여기 이 형태만 보면 리스트뷰 껍데기니까 xml 레이아웃에 넣고, 어댑터 객체가 사용되니까 이걸 만들고 데이터를 넣은 다음 리스트 뷰를 설정한 것이다.
//근데 어댑터를 구성하려고 하니까 각각의 아이템을 위한 뷰 객체를 정의해야 했고, 그 뷰 객체안에 들어가야 할 데이터가 여러 개니까 그걸 위한 자바 클래스를 하나 정의한 것
listView.setAdapter(adapter);//리스트뷰 보면 셋 어댑터 메소드가 있는데 여기에 어댑터 객체 전달
//이렇게 하면 리스트뷰가 어댑터를 알게 되어서 어댑터한테 얘기한 것처럼 getCount 호출하면서 몇 개있냐 물어보고 getView를 그 개수만큼 호출하면서 뷰 객체를 받아서 화면에 보여주게 됨
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
//리스트 뷰에 어떤 한 아이템을 클릭했을 때 어떤 아이템이 선택되었는지 이런 정보를 한번 보여주도록 할 것
//어떤 아이템이 선택됐을 때 이벤트를 처리할 수 있어야 그 아이템의 상세정보를 보여주는 화면으로 넘어가던지..이런 정보 보여줘야 함
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//몇 번째 아이템인지에 대한 인덱스 값도 파라미터 position, 이 포지션에 해당하는 데이터를 확인할 수 있어야 어떤 게 선택되는지를 알 수 있음
SingerItem item = (SingerItem) adapter.getItem(position);//어댑터에서 데이터를 관리하므로 어댑터에서 데이터 꺼냄, adapter의 선언을 OnCreate 안에 선언하면 이 new AdapterView.OnItemClickListener() 정의하는 곳 안에서는 처리할 수 없으므로
//(변수 참조 불가능) OnCreate 바깥에 선언해야 함
//getItem(position)하면 지정한 position의 데이터가 넘어옴, 그 데이터는 우리가 위에서 넘겨준 SingerItem 임(데이터를 어댑터한테 넘겨주고 어댑터에서 가져옴)
Toast.makeText(getApplicationContext(), "선택 : " + item.getName(), Toast.LENGTH_LONG).show();//선택한 아이템 토스트로 띄워주기
}
});//아이템을 클릭했을 때 발생하는 이벤트 처리를 위함함
Button button = (Button)findViewById(R.id.button);//버튼 누르면 내용 처리할라고
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = editText1.getText().toString();//각각의 텍스트뷰에 적힌 내용을 가져와서(아이디를 통해) 변수에 넣어줌
String mobile = editText2.getText().toString();
adapter.addItem(new SingerItem(name, mobile, R.drawable.iconfinder4));//넘어온 내용을 어댑터에 추가
//이제 리스트뷰 갱신해줘야 함. 근데 리스트 뷰 자체를 가지고 메서드를 호출하는 건 아니고(뭔소리냐 : 리스트 뷰만을 이용해서 메서드를 호출해서 갱신하는게 아니고)
adapter.notifyDataSetChanged();//어댑터쪽에서 리스트뷰쪽으로 갱신하라고 알려줌, 그럼 끝이래
}
});
}
class SingerAdapter extends BaseAdapter{//여기서 SingerAdapter는 걍 내가 지은 이름이고 기본 어댑터인(일반적, 다양) BaseAdpater 상속 받는 거
//ArrayList<String> items = new ArrayList<String>();//문자열 어레이리스트, 사실 이름/전화번호 등 여러 자료형이 들어가면 어레이 리스트로는 안 되므로 별도의 클래스 정의해야 함
ArrayList<SingerItem> items = new ArrayList<SingerItem>();//SingerItem 클래스의 어레이리스트 객체 생성
//어댑터가 데이터를 관리하는데, 데이터를 추가할 수도 있고 삭제할 수도 있으니까
//이 안에서 데이터를 관리할 수 있는 거를 ArrayList로 만들어봄
//위에 리스트뷰는 데이터 담을 수 있는 거 정의고 데이터는 없으므로 데이터 객체를 만들고 리스트 뷰 설정한 다음에 또는 그전에 추가를 해야 할텐데
//밖에서 추가를, 이 클래스 또는 객체 밖에서 추가를 하려 그러면 메소드가 필요함 그래서 하나 더 정의
public void addItem(SingerItem item){//SingerItem이라고 하는 객체를 하나 파라미터로 전달한다면
items.add(item);//그거를 items.add라고 해서 추가하도록 메서드를 하나 정의함
}
@Override
public int getCount() {
//리스트 뷰가 화면에 보여지는 시점에 리스트뷰가 어댑터한테 몇 개의 아이템이 있니(getCount() 호출하며) 물어봄, 그럼 어댑터가 몇 개야 답하면 리스트뷰가 그 만큼의 아이템을 전달 받아서 보여줌
return items.size();//아이템의 몇 개인지 알려줌(리스트 사이즈)
}
@Override
public Object getItem(int position) {
//필요할 때 그 데이터 아이템 좀 줄래? 라고 리스트뷰가 그러면서 몇 번째 데이터인지 이걸로 넘겨줌(인덱스)
return items.get(position);//position이 인덱스에 해당
}
@Override
public long getItemId(int position) {
return position;
}//그 순서의 데이터가 아이디가 있다면 아이디를 넘겨달라는 뜻
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//뷰의 재사용(마지막 추가 설명 내용)
/*
SingerItemView view= null;
if(convertView == null){//convertView가 널인 경우에만
view = new SingerItemView(getApplicationContext());//뷰에다가 새로 만들어서 할당
}
else{//그렇지 않으면
view = (SingerItemView)convertView;//convertView를 SingerItemView로 캐스팅만 해서 그대로 재사용
}
*/
SingerItemView view = new SingerItemView(getApplicationContext());//어떤 뷰든 안드로이드에서는 전부 다 컨텍스트 객체를 받게 되어있음
SingerItem item = items.get(position);
//이 뷰가 position 값이 넘어옴, 몇 번째 뷰를 달라는건지 이게 넘어오는 거 , 그러면 그 몇 번째에 해당하는 데이터를 이용해 이 뷰에다가 설정을 해줘야 그 데이터가 뷰에 보임
view.setName(item.getName());//이름 설정, item.getName()이라는 메서드로 이름 데이터를 가져와서 설정
view.setMobile(item.getMobile());
view.setImage(item.getResId());//데이터에서 관리되는 걸 가지고 와서 뷰에 설정해주게 됨
//SingerItemView 안에서 뷰를 설정하고 그 자바 소스 클래스의 객체 변수를 만들어서(인스턴스), 그것으로 메소드 호출하여 이미지 아이디로 가져오도록
//얘는 뷰에 붙여주는 역할(화면에 보이도록 하는)이고, 실제 데이터를 넣는 곳은 위에 생성자 부분임
return view;//이 뷰를 리턴해주면 그대로 리스트뷰 하나의 아이템으로 보인다. , 리턴되는 뷰에는 실제 데이터의 SingerItem이라고 하는 데이터 안에 들어가있는 Name과 Mobile 값이 설정되어 보이게 됨
//어댑터가 데이터 관리하기 때문에 데이터 관리하는 어댑터가 화면에 보여질 각각의 아이템에 대한 뷰도 만들어 달라는 뜻(이미지, 글자 필요하다면 여기서 리턴해주는 뷰는 레이아웃으로 구성이 되야 함
//=> 그럼 레이아웃에 해당하는 거를 부분화면으로 하나 정의하고 여기서 그걸 이용해서 객체를 만든 다음에 데이터를 설정하고 리턴해주는 게 가장 좋은 방법)
//SingerAdapter에서 getView를 해줄 때, 실제로 실무에서 쓰기에는 위 내용은 좀 부족
//왜냐면 우리가 이제 리스트뷰 보일 때 많은 아이템을 로딩하기가 메모리가 너무 많이 쓰임 즉, 뷰 만들 때 뷰 객체가 너무 많이 생기면 메모리 엄청난 소비임
//그래서 재사용할 수 있게 해놓음(뷰가 화면에 안 보이는 아이템은 보이는 아이템에 뷰와 붙여줘서 안 쓰는 뷰를 재사용하는거지, 데이터만 바꿔주고)
//코드를 재사용할 수 있도록 위 파라미터에 있는 converView라고 하는 걸 넘겨줌
}
}
//어댑터 객체를 만들기 위해 어댑터 클래스를 먼저 정의(기존 제공의 어댑터를 상속해서 그거를 필요한 만큼
//코드를 넣을 수 있게 할 거임임
}
//리스트 뷰에서 자동으로 클릭 효과 있고, 많으면 스크롤 뷰도 자동으로 생김
DB 없으니까 대충
결과
'Blog > Android' 카테고리의 다른 글
Parcelable 1, 여러 데이터 한번에 전달 (0) | 2020.04.05 |
---|---|
AudioRecorder, 녹음 기능 (0) | 2020.04.05 |
LayoutInflater, 한 화면 쪼개서 부분화면 이용 (0) | 2020.04.05 |
Intent, 메뉴 액티비티와 데이터 전달 (0) | 2020.04.05 |
Event, 버튼 이벤트 처리 (0) | 2020.03.31 |