될 때까지 안드로이드 정리 (오준석의 생존코딩) 3
- 26장 이벤트 처리
- 26장 분위기 변신 – Toast 커스텀
- 27장 툴바 사용하기
- 28장 리사이클러뷰
- 30장 생산성을 높이는 라이브러리 – Realm
될 때까지 안드로이드 #29 [26장 이벤트 처리]
<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="com.example.eventexam.MainActivity">
<TextView
android:id="@+id/view1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ffebee"
android:text="onClick, onLongClick" />
<EditText
android:id="@+id/edit1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="onFocusChanged, onKey" />
<EditText
android:id="@+id/edit2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="onFocusChanged, onKey" />
<TextView
android:id="@+id/event_info_text"
android:la
public class MainActivity extends AppCompatActivity {
private TextView mView1;
private View mView2;
private EditText mEdit1;
private EditText mEdit2;
private TextView mEventInfoTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mView1 = (TextView) findViewById(R.id.view1);
mView2 = findViewById(R.id.view2);
mEdit1 = (EditText) findViewById(R.id.edit1);
mEdit2 = (EditText) findViewById(R.id.edit2);
mEventInfoTextView = (TextView) findViewById(R.id.event_info_text);
// 클릭
setClickEvent();
// 포커스 변경
setFocusEvent();
// 키
setKeyEvent();
// 터치
setTouchEvent();
}
private void setTouchEvent() {
mView2.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Toast.makeText(MainActivity.this, "터치 다운", Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_MOVE:
mEventInfoTextView.setText("터치 정보 : " + event.toString());
break;
case MotionEvent.ACTION_UP:
Toast.makeText(MainActivity.this, "터치 업", Toast.LENGTH_SHORT).show();
break;
default:
return false;
}
return true;
}
});
}
private void setKeyEvent() {
View.OnKeyListener keyListener = new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 엔터키를 뗄 때 토스트를 표시
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
Toast.makeText(MainActivity.this, "뒤로가기를 눌렀습니다", Toast.LENGTH_SHORT).show();
}
return true;
}
};
View.OnKeyListener keyListener2 = new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 엔터키를 뗄 때 토스트를 표시
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
Toast.makeText(MainActivity.this, "뒤로가기를 눌렀습니다", Toast.LENGTH_SHORT).show();
}
return false;
}
};
mEdit1.setOnKeyListener(keyListener);
mEdit2.setOnKeyListener(keyListener2);
}
private void setFocusEvent() {
View.OnFocusChangeListener focusChangeListener = new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// 포커스를 가지면 배경색을 빨간색으로
if (hasFocus) {
v.setBackgroundColor(Color.RED);
} else {
v.setBackgroundColor(Color.WHITE);
}
}
};
mEdit1.setOnFocusChangeListener(focusChangeListener);
mEdit2.setOnFocusChangeListener(focusChangeListener);
}
private void setClickEvent() {
mView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "클릭", Toast.LENGTH_SHORT).show();
}
});
// 롱 클릭
mView1.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
Toast.makeText(MainActivity.this, "롱 클릭", Toast.LENGTH_SHORT).show();
// 이벤트 소비
return true;
}
});
}
}
될 때까지 안드로이드 #30 [26장 분위기 변신 – Toast 커스텀]
drawable/background_toast.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="4dp"
android:color="#8c9eff" />
<solid android:color="#ffcdd2" />
<corners android:radius="16dp" />
</shape>
layout_toast.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_toast"
android:gravity="center"
android:padding="16dp"
android:textColor="#a1887f"
android:textStyle="bold" />
</LinearLayout>
activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:onClick="showNewToast"
android:text="뉴 토스트"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
public class NewToast extends Toast {
public NewToast(Context context) {
super(context);
}
public static Toast makeText(Context context, String message, int duration) {
Toast toast = new Toast(context);
// Toast의 레이아웃
View customView = LayoutInflater.from(context).inflate(R.layout.layout_toast, null);
// Toast에 표시될 TextView
TextView textView = (TextView) customView.findViewById(R.id.message);
textView.setText(message);
// Toast에 레이아웃 설정
toast.setView(customView);
toast.setDuration(Toast.LENGTH_SHORT);
// 위치 조정
toast.setGravity(Gravity.CENTER, 0, -300);
return toast;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void showNewToast(View view) {
NewToast.makeText(this, "이것이 뉴 토스트다", Toast.LENGTH_SHORT).show();
}
}
될 때까지 안드로이드 #31 [27장 툴바 사용하기]
values/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
memu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:title="검색"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom" />
<item android:title="Item" />
</menu>
activity_main.xml
<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"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<androidx.appcompat.widget.SearchView
android:visibility="gone"
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultQueryHint="검색어를 입력하세요" />
</androidx.appcompat.widget.Toolbar>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
// SearchView searchView = (SearchView) findViewById(R.id.search_view);
// searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
// @Override
// public boolean onQueryTextSubmit(String query) {
// // 서치뷰의 내용으로 검색을 수행할 때 호출 됨
// Log.d(TAG, "onQueryTextSubmit: " + query);
// return true;
// }
//
// @Override
// public boolean onQueryTextChange(String newText) {
// // 서치뷰의 글자가 변경될 때마다 호출 됨
// Log.d("MainActivity", "onQueryTextChange: " + newText);
// return true;
// }
// });
getSupportActionBar().setTitle("변경된 제목");
// // 서치뷰가 열린 상태로
// searchView.setIconified(false);
//
// // 쿼리 힌트
// searchView.setQueryHint("이것은 힌트입니다");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Log.d(TAG, "onQueryTextSubmit: " + query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
Log.d(TAG, "onQueryTextChange: " + newText);
return true;
}
});
return true;
}
}
될 때까지 안드로이드 #32 [28장 리사이클러뷰]
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha05' implementation 'androidx.cardview:cardview:1.0.0'
item_card.xml
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="4dp"
app:cardElevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<!-- 상단 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:padding="16dp"
android:shadowColor="#000000"
android:shadowDx="1.0"
android:shadowDy="1.0"
android:shadowRadius="1.0"
android:text="Title"
android:textColor="#ffffff"
android:textSize="24sp" />
</RelativeLayout>
<!-- 내용 -->
<TextView
android:id="@+id/contents_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="Contents" />
<!-- 버튼 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/share_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ffffff"
android:text="Share" />
<Button
android:id="@+id/more_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ffffff"
android:text="learn more"
android:textColor="#0091ea" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
activity_main.xml
<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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_card" />
</LinearLayout>
public class CardItem {
private String title;
private String contents;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContents() {
return contents;
}
public void setContents(String contents) {
this.contents = contents;
}
public CardItem(String title, String contents) {
this.title = title;
this.contents = contents;
}
}
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> {
private final List<CardItem> mDataList;
private MyRecyclerViewClickListener mListener;
public MyRecyclerAdapter(List<CardItem> dataList) {
mDataList = dataList;
}
// 뷰 홀더를 생성하는 부분. 레이아웃을 만드는 부분
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_card, parent, false);
return new ViewHolder(view);
}
// 뷰 홀더에 데이터를 설정하는 부분
@Override
public void onBindViewHolder(final ViewHolder holder,final int position) {
CardItem item = mDataList.get(position);
holder.title.setText(item.getTitle());
holder.contents.setText(item.getContents());
// 클릭 이벤트
if (mListener != null) {
final int pos = position;
// 현재 위치
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onItemClicked(pos);
}
});
holder.share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onShareButtonClicked(holder.getBindingAdapterPosition());
}
});
holder.more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.onLearnMoreButtonClicked(holder.getBindingAdapterPosition());
}
});
}
}
// 아이템의 수
@Override
public int getItemCount() {
return mDataList.size();
}
// 각각의 아이템의 레퍼런스를 저장할 뷰 홀더 클래스
// 반드시 RecyclerView.ViewHolder를 상속해야 함
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView title;
TextView contents;
Button share;
Button more;
public ViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title_text);
contents = (TextView) itemView.findViewById(R.id.contents_text);
share = (Button) itemView.findViewById(R.id.share_button);
more = (Button) itemView.findViewById(R.id.more_button);
}
}
public void setOnClickListener(MyRecyclerViewClickListener listener) {
mListener = listener;
}
public interface MyRecyclerViewClickListener {
// 아이템 전체 부분의 클릭
void onItemClicked(int position);
// Share 버튼 클릭
void onShareButtonClicked(int position);
// Learn More 버튼 클릭
void onLearnMoreButtonClicked(int position);
}
public void removeItem(int position) {
mDataList.remove(position);
notifyItemRemoved(position);
//notifyItemRangeChanged(position, mDataList.size());
}
public void addItem(int position, CardItem item) {
mDataList.add(position, item);
notifyItemInserted(position);
notifyItemRangeChanged(position, mDataList.size());
}
}
public class MainActivity extends AppCompatActivity implements MyRecyclerAdapter.MyRecyclerViewClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
private MyRecyclerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(false);
// 레이아웃 매니저로 LinearLayoutManager를 설정
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
// StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
// 표시할 임시 데이터
List<CardItem> dataList = new ArrayList<>();
dataList.add(new CardItem("이것은 첫번째 아이템", "안드로이드 보이라고 합니다"));
dataList.add(new CardItem("이것은 세번째 아이템", "이번엔 세줄\n두번째 줄\n세번째 줄 입니다"));
dataList.add(new CardItem("이것은 두번째 아이템", "두 줄 입력도 해 볼게요\n두 줄 입니다"));
dataList.add(new CardItem("이것은 네번째 아이템", "잘 되네요"));
// 어댑터 설정
mAdapter = new MyRecyclerAdapter(dataList);
mAdapter.setOnClickListener(this);
recyclerView.setAdapter(mAdapter);
// ItemAnimator
DefaultItemAnimator animator = new DefaultItemAnimator();
animator.setAddDuration(1000);
animator.setRemoveDuration(1000);
animator.setMoveDuration(1000);
animator.setChangeDuration(1000);
recyclerView.setItemAnimator(animator);
// ItemDecoration
DividerItemDecoration decoration = new DividerItemDecoration(this, layoutManager.getOrientation());
recyclerView.addItemDecoration(decoration);
}
@Override
public void onItemClicked(int position) {
Log.d(TAG, "onItemClicked: " + position);
}
@Override
public void onShareButtonClicked(int position) {
Log.d(TAG, "onShareButtonClicked: " + position);
mAdapter.addItem(position, new CardItem("추가 됨", "추가 됨"));
}
@Override
public void onLearnMoreButtonClicked(int position) {
Log.d(TAG, "onLearnMoreButtonClicked: " + position);
// 아이템 삭제
mAdapter.removeItem(position);
}
}
될 때까지 안드로이드 #33 [30장 생산성을 높이는 라이브러리 – Realm]
https://realm.io/kr/docs/java/latest
// 프로젝트 레벨
dependencies {
classpath "io.realm:realm-gradle-plugin:3.5.0"
// app 레벨 apply plugin: 'realm-android'
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
<application
android:name=".MyApplication"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/email_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="email"
android:inputType="textEmailAddress" />
<EditText
android:id="@+id/password_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="password"
android:inputType="textPassword" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="SignIn"
android:text="로그인" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="SignUp"
android:text="가입" />
<EditText
android:id="@+id/new_password_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="수정할 비밀번호" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="updatePassword"
android:text="비번 수정" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="deleteAccount"
android:text="탈퇴" />
<TextView
android:id="@+id/result_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
</LinearLayout>
models/User.java
public class User extends RealmObject {
private String email;
private String password;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"email='" + email + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class MainActivity extends AppCompatActivity implements RealmChangeListener<Realm> {
private EditText mEmail;
private EditText mPassword;
private EditText mNewPassword;
private TextView mResultText;
private Realm mRealm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEmail = (EditText) findViewById(R.id.email_edit);
mPassword = (EditText) findViewById(R.id.password_edit);
mNewPassword = (EditText) findViewById(R.id.new_password_edit);
mResultText = (TextView) findViewById(R.id.result_text);
mRealm = Realm.getDefaultInstance();
mRealm.addChangeListener(this);
showResult();
}
@Override
protected void onDestroy() {
super.onDestroy();
mRealm.removeAllChangeListeners();
mRealm.close();
}
public void SignIn(View view) {
String email = mEmail.getText().toString();
String password = mPassword.getText().toString();
User user = mRealm.where(User.class)
.equalTo("email", email)
.equalTo("password", password)
.findFirst();
if (user != null) {
Toast.makeText(this, "성공", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "실패", Toast.LENGTH_SHORT).show();
}
}
// 회원가입
public void SignUp(View view) {
mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
if (realm.where(User.class).equalTo("email", mEmail.getText().toString()).count() > 0) {
realm.cancelTransaction();
}
User user = realm.createObject(User.class);
user.setEmail(mEmail.getText().toString());
user.setPassword(mPassword.getText().toString());
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
Toast.makeText(MainActivity.this, "성공", Toast.LENGTH_SHORT).show();
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
Toast.makeText(MainActivity.this, "실패", Toast.LENGTH_SHORT).show();
}
});
}
public void deleteAccount(View view) {
final RealmResults<User> results = mRealm.where(User.class).equalTo("email", mEmail.getText().toString()).findAll();
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
results.deleteAllFromRealm();
}
});
}
public void updatePassword(View view) {
final User user = mRealm.where(User.class).equalTo("email", mEmail.getText().toString()).findFirst();
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
user.setPassword(mNewPassword.getText().toString());
}
});
}
private void showResult() {
RealmResults<User> userList = mRealm.where(User.class).findAll();
mResultText.setText(userList.toString());
}
@Override
public void onChange(Realm element) {
showResult();
}
}
될 때까지 안드로이드 (오준석의 생존코딩) 유튜브 강의
https://www.youtube.com/watch?v=euTtMnN-TgI&list=PLxTmPHxRH3VWTd-8KB67Itegihkl4SVKe&index=2
될 때까지 안드로이드 (오준석의 생존코딩) 깃헙
https://github.com/junsuk5/android-first-book
될 때까지 안드로이드 소스 다운로드
android-first-book-master.zip