68일차(2)/Android App(10) : ListView, OnClickListener, AlertDialog 활용 예제
- xml 레이아웃을 사용하는 setContentView 로 액티비티를 보여준다.
- 이 메소드를 활용해서 xml로 만들어진 UI의 참조값을 얻어온다.
- 어떤 UI에 id를 부여하고, 이 id를 활용해서 액티비티에서 참조값을 얻어와서 사용할 수 있다.
- 하나의 어플리케이션은 여러개의 activity로 구성될 수 있다.
- 하나의 액티비티가 첫 화면으로써 사용자를 대면한다.
- intent filter 가 들어있는 액티비티가 앱이 처음 런칭될 때 실행된다.
- 액티비티를 이동하고자 한다면?
- 액티비티를 활성화시키는 객체는 intent 객체이다.
- 이 객체를 생성해서 그 안에 어떤 액티비티를 활성화시킬 것인지에 대한 정보를 담고
startActivity() 메소드에 객체를 전달하면 된다.
- 나의 참조값 this.
- this의 타입은 object, MainActivity이기도 하고 OnClickListener 이기도 하고 AppCompatActivity이기도 하다.
MainActivity a=this;
Object b=this;
View.OnClickListener c=this;
Context d=this;
- 클래스 안에서 이런 식으로 작성해보면서 this가 어떤 타입인지 확인해 볼 수 있다.
- intent 객체를 전달하는 데에는 인자 context가 필요하다.
→ activity가 context 타입이기 때문에 this를 전달한 것이다.
- Activity의 부모가 Context 라는 것 기억하기!★
- 새 모듈 생성
step05listview
MainActivity
package com.example.step05listview;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity
implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
List<String> names;
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//listView의 참조값
ListView listview=findViewById(R.id.listView);
//listView에 출력할 sample data
names=new ArrayList<>();
names.add("바나나");
names.add("딸기");
names.add("복숭아");
for(int i=0;i<100;i++){
names.add("아무개"+i);
}
//listView 에 연결할 아답타 객체
//new ArrayAdapter<>( Context , layout resource, 모델 )
adapter=new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1,
names);
//listView에 아답타 연결하기
listview.setAdapter(adapter);
//Activity를 아이템 클릭 리스너로 등록하기
listview.setOnItemClickListener(this);
listview.setOnItemLongClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
// int i는 클릭한 아이템의 인덱스가 들어있다.
String name=names.get(i);
Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
}
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
//오랫동안 클릭한 셀에 출력된 이름 읽어오기
String name=names.get(i);
//아래의 익명 클래스에서 참조 가능하도록
int SelectedIndex=i;
/*AlertDialog.Builder builder=new AlertDialog.Builder(this);
builder.setTitle("알림");
builder.setMessage(name+" 을 삭제 하시 겠습니까?");
builder.setNegativeButton("아니요", null);
builder.setPositiveButton("네", null);
AlertDialog dialog=builder.create();
dialog.show();*/
new AlertDialog.Builder(this)
.setTitle("알림")
.setMessage(name+"을 삭제하시겠습니까?")
.setNegativeButton("아니요", null)
.setPositiveButton("네", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//모델에서 해당 인덱스의 데이터를 삭제한다.
names.remove(SelectedIndex);
//아답타에 모델에 변화가 생겼다고 알리기
adapter.notifyDataSetChanged();
}
})
.create()
.show();
//이벤트 전파를 여기서 막기
return true;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
<ListView
android:id="@+id/listView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- Legacy-ListView 요소 추가해주기. 목록을 보여주는 UI 이다.
- 끌어다놓고 이 리스트로 화면 꽉 채우기
- 폭과 높이 모두 0dp, 상하좌우 마진을 0으로 하면 화면 전체를 차지하는 ListView로 만들 수 있다.
- 코드로 보면 이렇다. 상하좌우 마진부분을 parent 상태로!
- listView라는 아이디를 부여해준다.
- @+id/아이디 라고 적는 것이 규칙이다.
- @ : 리소스, +id : 아이디를 추가하겠다 라는 뜻이다!
- MainActivity에 리스트 추가하기
- ListView의 구조! 데이터(모델)를 listview에 바로 연결할 수는 없다.
- 중간에 adapter 가 끼어있어야 한다. 이렇게 연결되어 동작한다.
- 모델을 아답타에 연결해두면 아답타가 listview에 각각의 셀을 나타낼 수 있는 cellView 를 공급해준다.
- 이 각각의 cellview에는 TextView가 들어있다. 문자열 하나를 출력한 것이라고 보면 된다.
- 아답타는 list에 들어있는 문자 데이터를 이용해서 textView에 있는 이름을 하나씩 출력해서 ListView에 공급하는 것이다.
- adapter 에서는 데이터를 가공해서 공급한다.
- ListView는 사용자가 볼 수 있는내용을 출력하는 곳인데 출력할 view는 아답타로부터 요청해서 받는다.
- 이런 식으로 출력하려면 셀 하나하나의 레이아웃도 필요하다.
- 안드로이드에 간단한 레이아웃은 미리 만들어져있다. 가져다가 사용하면 된다.
(코드의 simple_list_item_1 부분)
- 리스트뷰의 참조값을 adapter에 전달한다.
- 2번째 인자: 셀 하나하나의 레이아웃 전달(만들어져있는 레이아웃을 참조)
- 샘플 데이터 names는 adapter의 3번째 인자로 전달되었다.
- android.R 에는 개발자를 위해 미리 만들어 놓은 것들이 들어있다.
- 해당 레이아웃을 ctrl+클릭하면 이 레이아웃 설정으로 들어갈 수 있는데,
여기에서 레이아웃의 양식을 볼 수 있다.
- Model → Adapter → ListView 로 받아서 사용
- 이 xml 문서를 하나 전개해서 이 view를 만든 것이라고 보면 된다.
- new ArrayAdapter<> 타입으로 객체 생성
- 화면에는 이렇게나온다.
- 만약 클릭한 셀을 토스트메시지에 출력하고 싶다면,
리스트뷰의 셀을 클릭했는지, 했다면 어디를 했는지 감시할 리스너가 필요하다.
- listview에서 보면 클릭, 드래그, hover, 아이템클릭, select 등등... 다양한 리스너가 등록되어 있다.
- itemClickListener 를 사용한다. 인터페이스 타입으로, 인터페이스를 구현해야 한다.
- 구현하고 메소드 override까지 해주기!
- 이제 아이템을 클릭하면 이 onItemClick() 메소드로 실행 순서가 들어온다.
이곳에 클릭한 아이템의 정보가 전달된다.
- onItemClick() 의 인자인 int i 에는 클릭한 아이템의 인덱스가 들어있다.
- i 번호값을 가지고 names의 값을 얻어오기!
- 하지만 메소드 밖에서참조하려면 필드가 필요하다.
→ names라는 ArrayList를 필드로 만들어주기
- 실행해보면 클릭한 인덱스에 해당하는 이름이 toast 메시지로 나온다.
- 액티비티에 리스너를 사용하는 것은 안드로이드에서 매우 중요하다!
- 먼저 액티비티를 리스너로 만들고,
사용자가 어디를 클릭했는지 알아내서 여기서 작업을 한다.
- 길게 클릭했을 때 다른 동작을 하고 싶다면?
setOnItemLongClickListener
- 이것도 인터페이스이므로 구현해준다. override도 하기!
- 아이템을 길게 클릭했을 때(누르고 있을 때) 이 메소드에 실행순서가 돌아온다.
- AlertDialog.Builder 객체 생성
- setTitle() 메소드를 보면 builder 타입이 리턴된다. 이런 구조에서는 메소드를 연속적으로 이어서 사용할 수 있다.
- 재귀적으로 자기자신의 타입이 계속 리턴되는 메소드들이 있다.
- 설정을 여러개 할 때 .xxx().yyy().zzz() 라고 연이어서 작성할 수 있다.
- 왜 이런 메소드들이 있는지? 어떤 설정을 편하게 할 수 있도록!
- 오랫동안 클릭하면 이렇게 알림이 뜬다.
- return true; 하면 다른 동작을 막아준다.
(원래는 길게 클릭할 때에도 일반 클릭도 동작이 들어온다. 그런데 저 값을 true로 바꿔주면 토스트 메시지가 나오지 않는다.)
- 부정버튼, 긍정버튼을 추가해준 모습!
- i 를 클릭하면 오랫동안 클릭한 아이템(이름)을 읽어올 수 있다.
- 이런 형태로 만들어볼 수 있다.
- 이 긍정/부정 버튼을 눌렀는지 안눌렀는지 알고싶으면 이 값을 읽어오는 리스너를 넣으면 된다.
- DialogInterface.OnClickListener 를 넣어주기!
- 그럼 익명클래스가 열린다.
- 위에 있는 i를 참조하고 싶은데, 그냥 i를 작성하면 이름이 가까워서 이 i가 참조될 것이다.
int SelectedIndex=i;
→ 이 i 값을 필드로 만들어 담아준다.
- 이런 구조로 연결되어 있는데,
모델이 바뀌었다고 Listview가 자동으로 업데이트 되는 것은 아니다.
- 바뀌었다고 adapter에 알려야 한다! 그럼 adapter가 리스트뷰를 업데이트 시켜준다.
- 위에 있는 메소드에도 변했다는 내용을 전달해야 하기 때문에
→ ArrayAdapter<String> adapter; 필드로 선언!
- 이제 삭제 기능을 사용할 수 있다.
- AlertDialog.Builder 작성법 관련
- 위에는 리턴타입이 다 Builder 타입인데
.create() 에서는 AlertDialog 타입이 리턴된다.
- 이 메소드는 자신의 타입 객체가 계속 리턴된다. 즉 설정을 편리하게 할 수 있다!
- 원래는 이렇게 이 안에 들어있는 Builder 이너클래스로 객체를 생성한 것이다.
- 위와 아래 블럭은 같은 내용이다!
- 일반적으로 작성하면 원래는 위와 같은 모양이 되어야 한다. 그러나 아래와 같이 코딩할수 있으면 엄청 효율적이다!
- 코딩의 유행이 이런 형태로 많이 가고 있다.
- 무조건 이렇게 할 수 있는 것은 아니다. 그 객체가 리턴되게끔 설계되어 있어야만 이렇게 코딩할 수 있다.
- 액티비티를 DialogInterface.OnClickListener 로 만들 수도 있다.
- 기존 MainActivity를 복사해서 만든 MainActivity2
package com.example.step05listview;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity2 extends AppCompatActivity
implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
List<String> names;
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//listView의 참조값
ListView listview=findViewById(R.id.listView);
//listView에 출력할 sample data
names=new ArrayList<>();
names.add("바나나");
names.add("딸기");
names.add("복숭아");
for(int i=0;i<100;i++){
names.add("아무개"+i);
}
//listView 에 연결할 아답타 객체
//new ArrayAdapter<>( Context , layout resource, 모델 )
adapter=new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1,
names);
//listView에 아답타 연결하기
listview.setAdapter(adapter);
//Activity를 아이템 클릭 리스너로 등록하기
listview.setOnItemClickListener(this);
listview.setOnItemLongClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
// int i는 클릭한 아이템의 인덱스가 들어있다.
String name=names.get(i);
Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
}
//DialogInterface.OnClickListener 타입 필드
DialogInterface.OnClickListener listener=new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//눌러진 버튼이 Negative 버튼인지 Positive 버튼인지 구별할 숫자값이 매개변수 i에 전달된다.
if(i == DialogInterface.BUTTON_POSITIVE){//네 버튼
//필드에 저장된 값을 활용해서 데이터를 삭제
names.remove(selectedIndex);
adapter.notifyDataSetChanged();
}else if(i == DialogInterface.BUTTON_NEGATIVE){//아니요 버튼
}
}
};
//응 클릭된 인덱스를 저장할 필드
int selectedIndex;
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
//오랫동안 클릭한 셀에 출력된 이름 읽어오기
String name=names.get(i);
//위의 필드에 값을 넣어줄 때 사용한 익명 클래스에서 참조 가능하도록 필드에 담아둔다.
selectedIndex=i;
new AlertDialog.Builder(this)
.setTitle("알림")
.setMessage(name+"을 삭제하시겠습니까?")
.setNegativeButton("아니요", this.listener)
.setPositiveButton("네", listener)
.create()
.show();
//이벤트 전파를 여기서 막기
return true;
}
}
- 네/아니오 버튼 중 무엇을 눌러도 동일한 리스너가 동작하게 하기
(어떤 것을 눌렀는지 구분, 분기가 필요하다)
- 필드에 무언가를 선언하고 값을 집어넣을 수 있다.
→ DialogInterface를 필드로 만들기
- 필드에 직접 new해서 객체를 생성해주기
- DialogInterface 타입의 OnClickListener 객체를 생성해서 listener에 담아준 것이다.
- DialogInterface.OnClickListener 타입의 listener 라는 필드를 생성
- 이 필드를 선언해서 값도 직접 넣어준 것이다.
- 이렇게 리스너를 각각의 Button에 등록해준다.
- 현재 동일한 리스너를 등록했으므로, 어떤 버튼을 눌러도 이 메소드로 순서가 들어온다.
- 구분할 수 있는 방법은? int i 활용
- 눌러진 버튼이 Negative 버튼인지 Positive 버튼인지 구별할 숫자(정수) 값이 매개변수 i 에 전달된다.
- 버튼에 따라 상수값으로 정의되어있다.
- Negative 버튼을 클릭하면 -2, Positive 버튼을 클릭하면 -1이 출력된다.
- Neutral 버튼은 보통 확인 버튼으로 사용된다.
- 정의되어 있는 상수를 사용해 이렇게 분기할수있다
- 그런데 빨강->파랑으로 실행 순서가 들어오는데
파란색 메소드 안에서 빨간 메소드 안에 있는 값(index)이 필요하다. 접근이 불가능하다.
→ 이 i 값을 필드에 넣어두어야 참조가 가능하다.
- selectedIndex를 필드로 선언. 이 값을 필드에 넣어두면 위의 onclick메소드에서도 참조 가능해진다.
- 값을 담아두었다가 필요할 때 사용할 수 있다!
- 필드값을 참조해서 삭제하고, adapter에서 listview로 값이 바뀌었다고 전달하기.
- mainActivity2를 화면에 띄워보려면? manifest에 등록하기
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyAndroid">
<activity android:name=".MainActivity2" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
</application>
</manifest>
- 이렇게 작성하고 옮겨놓으면 mainActivity2를 테스트할 수 있다.
'국비교육(22-23)' 카테고리의 다른 글
69일차(2)/Android App(12) : ListView, Adapter 활용 예제 / java to kotlin 작성 연습 (0) | 2023.01.15 |
---|---|
69일차(1)/Android App(11) : Kotlin Constructor, field / null 값 허용 문법 (0) | 2023.01.14 |
68일차(1)/Android App(9) : Kotlin 기본 문법 예제 (0) | 2023.01.13 |
67일차(2)/Android App(8) : Activity 화면 전환하기 (1) | 2023.01.12 |
67일차(1)/Android App(7) : Constraint Layout 연습, findViewById 사용 (0) | 2023.01.11 |