성능이슈로 인해 나온 기능들
라이브러리 추가
package com.bsj93.ex77butterknife;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
//버터나이프 라이브러리
// 편의성은 개선되었지만 성능이슈는 개선되지않음
// 그래서 등장한 안드로이드의 ViewBinding와 DataBinding
@BindView(R.id.tv) //find 한것
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv.setText("aaa");
}
@OnClick(R.id.btn)
void clickBtn() {
tv.setText("버튼 클릭 이벤트");
}
}
ViewBinding 기능을 그래이들에서 켠다
main.java에서 할 일
① setContentView(R.layout.activity_main) : 바인딩 클래스 객체가 만든 뷰로 액티비티 보여줘야하기에 삭제!
setContentView가 layoutInfate를 해주고 있었음
② binding 객체 생성, activity_main.xml을 객체로 생성하여 액티비티에 뷰로 설정
-> 메인 함수가 자동으로 하던 inflate를 위임함
③ setContentView에게 바인딩으로 화면 만들라고 하기
setContentView(binding.getRoot());
-> 모든 뷰들을 감싸고 있는 layout을 가진 root(xml의 최상위 레이아웃)
main.xml
<?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:orientation="vertical"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--1) TextView 제어 -->
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
android:padding="8dp"
android:textColor="@color/black"
/>
<!--2) 버튼 클릭이벤트 처리 -->
<Button
android:id="@+id/btn"
android:text="글씨 바꾸기"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!--3) EditText 글씨 입력받아 텍스트뷰에 보이기 -->
<EditText
android:id="@+id/et"
android:layout_marginTop="24dp"
android:inputType="text"
android:hint="Enter Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn2"
android:text="button"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_result"
android:text="tesult"
android:textColor="@color/black"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
main.java 클래스
package com.bsj0420.exviewbinding;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.bsj0420.exviewbinding.databinding.ActivityMainBinding;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
//ViewBinding 은 라이브러리가 아니고 안드로이드 아키텍쳐 고유의 기능이다
//따라서 그냥 기능만 On 하면 됨
// => 그래이들에서!!
//activity_main.xml과
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//바인딩 클래스 객체가 만든 뷰로 액티비티 보여줘야하기에 삭제!
//setContentView(R.layout.activity_main); => 삭제
//binding 객체 생성
// activity_main.xml을 객체로 생성하여 액티비티에 뷰로 설정
//메인 함수가 자동으로 하던 inflate를 위임함
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); //모든 뷰들을 감싸고 있는 layout을 가진 root
//1. 텍스뷰 제어 - 이미 바인딩 객체가 텍스트뷰 침조 하고 있음
binding.tv.setText("바인딩 완");
//2. 버튼 클릭 리스너
binding.btn.setOnClickListener(v->{
binding.tv.setText("버튼클릭");
});
//2-1) 버튼 롱 클릭 이벤트-- 람다식으로
binding.btn.setOnLongClickListener(view -> {
Toast.makeText(this, "long - click", Toast.LENGTH_SHORT).show();
return true;
});
//3) EditText 글씨 입력받아 텍스트뷰에 보이기
binding.btn2.setOnClickListener(view -> {
binding.tvResult.setText(binding.et.getText().toString());
binding.et.setText("");
});
}
화면의 일부분을 별도로 설계하여 관리하는 Fragment
플래그먼트를 제어하는 자바에서 플래그먼트의 xml을 바인딩하여 따로 제어하도록 함
main.xml
메인에서 플래그먼트 보일 자리 만듦
<?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:orientation="vertical"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 4) 프래그먼트에서 뷰 바인딩 -->
<!-- 프래그먼트 정적으로 놓기 -->
<fragment
android:id="@+id/frag"
android:layout_width="match_parent"
android:layout_height="200dp"
android:name="com.bsj0420.exviewbinding.MyFragment"
tools:layout="@layout/fragment_my"/>
</LinearLayout>
Fragment.xml
플래그먼트 화면 구성
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:padding="16dp">
<TextView
android:id="@+id/tv"
android:padding="8dp"
android:textStyle="bold"
android:textColor="@color/white"
android:text="마이 프래그먼트"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn"
android:layout_below="@+id/tv"
android:text="텍스트 변경"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
Fragment.java
onCreateView()에서 바인딩 작업한다
-> 뷰를 리턴해 주는 기능 메소드 재정의
바인딩으로 레이아웃 inflate 한다
리턴값으로 최상위 root를 준다onViewCreated()에서 바인딩한 뷰를 참조하여 xml에 있는 뷰를 찾아와 명령
package com.bsj0420.exviewbinding;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.bsj0420.exviewbinding.databinding.FragmentMyBinding;
public class MyFragment extends Fragment {
//fragment_my.xml 파일과 연결되어 있는 바인딩 클래스 참조변수 생성
FragmentMyBinding binding;
//이 프래그먼트가 보여줄 뷰를 리턴해 주는 기능 메소드 재정의
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentMyBinding.inflate(inflater, container, false);
//플래그먼트 만들 때 조각 사이즈(container) 미리 같이 정함
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.btn.setOnClickListener(v -> binding.tv.setText("Good day"));
}
}
main.xml
<?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:orientation="vertical"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--5) RecyclerView에서 viewBinding 사용해보기-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</LinearLayout>
main.java
대량의 데이터 준비 & 어답터 연결
//대량의 데이터
ArrayList<ItemVO> items = new ArrayList<>();
MyAdapter adapter;
//리사이클러뷰는 가져올 필요 없음 이미 메인 액티비티 바인디으로 연결되어 있으니까
@Override
protected void onResume() {
super.onResume();
//임의 데이터 추가
items.add(new ItemVO("모아나1", R.drawable.moana01));
items.add(new ItemVO("모아나2", R.drawable.moana02));
items.add(new ItemVO("모아나3", R.drawable.moana03));
items.add(new ItemVO("모아나4", R.drawable.moana04));
items.add(new ItemVO("모아나5", R.drawable.moana05));
//리사이클러에 아답터 설정
adapter = new MyAdapter(this,items);
binding.recycler.setAdapter(adapter);
}
리사이클러 뷰 xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="4dp"
app:cardElevation="8dp"
android:layout_margin="8dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="120dp"
android:src="@drawable/moana02"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/tv"
android:layout_below="@id/iv"
android:layout_centerHorizontal="true"
android:padding="8dp"
android:textColor="@color/black"
android:textStyle="bold"
android:text="모아난"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
리사이클러뷰에 쓸 item class
package com.bsj0420.exviewbinding;
public class ItemVO {
String title; //제목 글씨
int imgRes; //이미지 리소스 아이딘
public ItemVO() {
}
public ItemVO(String title, int imgRes) {
this.title = title;
this.imgRes = imgRes;
}
}
package com.bsj0420.exviewbinding;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bsj0420.exviewbinding.databinding.RecyclerItemBinding;
import java.util.ArrayList;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.VH> {
//바인딩 2가지
Context context;
ArrayList<ItemVO> items;
public MyAdapter(Context context, ArrayList<ItemVO> items) {
this.context = context;
this.items = items;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false);
return new VH(itemView);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
holder.binding.tv.setText(items.get(position).title);
holder.binding.iv.setImageResource(items.get(position).imgRes);
}
@Override
public int getItemCount() {
return items.size();
}
class VH extends RecyclerView.ViewHolder {
// recycler_item.xml과 연결되는 바인딩 클래스 필요
RecyclerItemBinding binding;
public VH(@NonNull View itemView) {
super(itemView);
//이미 만든 뷰 객체의 안에 있는 자식뷰들과 연결하는 바인딩 객체
//인플레이트 이미 위에서 되어 있으니 바로 바인드
binding = RecyclerItemBinding.bind(itemView);
}
}
}
package com.bsj0420.exviewbinding;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bsj0420.exviewbinding.databinding.RecyclerItemBinding;
import java.util.ArrayList;
public class MyAdapter2 extends RecyclerView.Adapter<MyAdapter2.VH> {
Context context;
ArrayList<ItemVO> items;
public MyAdapter2(Context context, ArrayList<ItemVO> items) {
this.context = context;
this.items = items;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//여기서 바인딩 만듦!!!!!!!!!!!!!!!!
return new VH(RecyclerItemBinding.inflate(LayoutInflater.from(context), parent, false));
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
holder.binding.iv.setImageResource(items.get(position).imgRes);
holder.binding.tv.setText(items.get(position).title);
}
@Override
public int getItemCount() {
return items.size();
}
// ViewHolder에 매개변수를 RecyclerItemBinding로 받기
class VH extends RecyclerView.ViewHolder {
RecyclerItemBinding binding;
public VH(@NonNull RecyclerItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
① data : 뷰와 데이터 연결하는 곳
② 실질적 화면 부분
data binding에 기능에 인해 변수값이 바뀌면 화면에 자동갱신되는 특별한 자료형(Observable) 가진 클래스 작성
💡 옵저버 클래스 작성
- Observablexxx : 기본 자료형, 어레이리스트, 배열
- ObservableField<자료형> : 기본 자료형 뺀 나머지 참조형 객체
생성자
생성자로 초기화 할 값은 무조건 set으로 넣는다리스너 메소드
버튼 클릭 시 실행 될 콜백메소드 여기에 만들어야함
why? 바꾸려는 변수가 이 클래스에 있으니까
주의) 원래 리스너의 콜백 메소드에 있는 파라미터가 있다면 반드시 꼭 같이 써야함
package com.bsj0420.ex79databinding;
import android.view.View;
import androidx.databinding.ObservableBoolean;
import androidx.databinding.ObservableField;
import androidx.databinding.ObservableInt;
public class User {
//일반 자료형은 값이 변경되어도 화면 갱신을 하지는 않음
//data binding에 기능에 인해 변수값이 바뀌면 화면에 자동갱신되는 특별한 자효형
// Observablexxx 클래스 객체 - 기본 자료형, 어레이리스트, 배열을 있음
// ObservableField<자료형> 나머지 참조형 객체들은 ObservableField<>
public ObservableField<String> name = new ObservableField<>();
public ObservableInt age = new ObservableInt();
public ObservableBoolean fav = new ObservableBoolean();
public User(String name, int age, boolean fav) {
this.name.set(name); //무조건 셋으로 넣는다
this.age.set(age);
this.fav.set(fav);
}
//버튼 클릭 시 실행 될 콜백메소드 여기에 만들어야함
// why? 바쑤려는 변수가 이 클래스에 있으니까
//원래 리스너의 콜백 메소드에 있는 파라미터가 반드시 꼭같이 있어야됨
public void changeName(View view) {
this.name.set("로빈");
}
public void increaseAge(View view){
this.age.set(this.age.get()+1); //get / set으로 값 넣고 뺸다
}
}
기본 뷰 : text에 데이터에 정한 네임을 참조하여 @{데이터네임.옵저버변수이름}
함수를 가진 뷰 : 함수이름=@{데이터네임::함수이름}
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
>
<!-- 1. 레이아웃 뷰와 바인딩할 데이터들 명칭과 클래스 지칭 -->
<!-- 뷰 -->
<data>
<variable
name="userData"
type="com.bsj0420.ex79databinding.User" />
</data>
<!-- 2. 화면에 그려낼 레이아웃 뷰 : 기존의 최상위 뷰그룹-->
<LinearLayout
android:padding="16dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 뷰 배치용 아님 id 필요 없음 -->
<TextView
android:text="@{userData.name}"
android:textColor="@color/black"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{String.valueOf(userData.age)}"
android:textColor="@color/black"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:text="좋아요"
android:checked="@{userData.fav}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="chageText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{userData::changeName}"/>
<!-- 온클릭 됐을 때 실행 할 함수 이름 써야함
원래 클릭리스너는 액티비티에 써야됨 하지만 databinding
-->
<Button
android:text="나이값 증가"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{userData::increaseAge}"/>
</LinearLayout>
</layout>
① setContentView(R.layout.activity_main); 삭제
② DataBinding 의 기능을 통해 액티비티에 보여질 내용물(뷰) 설정
③ xml문서의 data 요소 안에 있는 variable 의 type에 지정한 클래스를 객체로 만들어서 set() 해주면 그와 연결된 뷰들에 값이 보여짐
=> xml에 쓴 name을 참조하여 setter / getter 만들어짐
package com.bsj0420.ex79databinding;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.bsj0420.ex79databinding.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
//DataBinding - 뷰를 참조하지 않고 뷰가 보여주는 값을 가진 변수를 제어하는 방식
//안드로이드 아키텍쳐 구성요소 이기에 사용설정 하면됨
//뷰 바인딩과 다르게 레이아웃 xml 파일의 최상위 요소가
//반드시 레이아웃이어야만 바인딩 클래스들이 만들어진다
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1.
//setContentView(R.layout.activity_main);
//2. DataBinding 의 기능을 통해 액티비티에 보여질 내용물(뷰) 설정 : ActivityMainBinding을 리턴해줌
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//3.
//xml문서의 data 요소 안에 있는 variable 의 type에 지정한
//클래스를 객체로 만들어서 set() 해주면 그와 연결된 뷰들에 값이 보여짐
binding.setUserData(new User("sam",13,true)); //
}
}