국비교육(22-23)

118일차(1)/Android App(75) : Wearable App 예제(2), 기기 페어링

서리/Seori 2023. 3. 30. 01:32

118일차(1)/Android App(75) : Wearable App 예제(2), 기기 페어링

 

 

- 가상기기 삭제하고 새로 만들기

 

 

- 가상기기 삭제하기 (옵션-delete)

 

 

 

- Phone에서 새 가상기기를 생성하려고 보면 플레이스토어 표시가 있는데, 가상기기에 구글 플레이스토어가 포함되어있는지 여부를 말한다.

- 가상기기에서도 구글 계정으로 로그인해서 플레이스토어 앱을 받을 수 있다!

- 이 표시가 있어야 웨어러블 앱을 가상기기에서도 연동하여 테스트해볼 수 있다.

 

 

 

- Phone 가상기기 생성. R 선택해서 다운로드 받고 next-next-finish

 

 

 

- 다음은 wear OS- small round로 생성

 

 

- China ver.이 아닌 R 버전으로 생성. next-finish

 

- 이렇게 가상기기 두개를 만들어둔다.(Wearable 앱을 페어링해서 활용하기 위한 것)

- 구동이 오래 걸리므로 앱을 만들기 전에 미리 가상기기를 Run 시켜서 시작해두기

 

 

 

- Phone 가상기기의 구글 플레이스토어에 구글계정으로 로그인하기

 

 

- 구글계정으로 로그인하면 실제 기기를 사용하는 것처럼 플레이스토어를 사용할 수 있다.

 

 

- 이 Phone 기기 안에 wear os 앱을 설치해야 한다.

 

 

- 구글 플레이스토어에서 wear os 검색!

- Wear OS by Google 앱을 install 해두기

 


 

 

 

- 위 아이콘의 1,2번은 시계의 물리 버튼을 말한다.

- 각각 물리버튼 1,2 / 손바닥으로 화면을 덮을 때 / 시계를 기울이는 동작 을 가리킨다.

 

 

- 현재 1번 물리버튼은 최근에 사용했던 앱 목록이다.

 

 

- 2번 물리버튼은 설치된 어플리케이션 보기

 

 

- 손으로 덮으면 화면이 꺼지게 되어있다.

 

 

- 추가 컨트롤을 하고 싶다면 Emulator 창의 Extended control 버튼 클릭

- 새 창이 뜨고, 센서값 등을 조정할 수 있다.

 

 

- 설치되어 있는 앱을 띄워놓고 테스트할 수 있다.

- Rotary Input (회전) 탭에서 시계 옆에 있는 파란 원을 돌려서 회전시킬 수 있다.(시계 화면에서 스크롤하는 효과)

 

 

- 설정에 있는 기능 살펴보기

 

 

- 설정-디스플레이-Change watch face

 

 

- 기본화면 배치 레이아웃을 수정할 수 있다. 우측으로 넘겨서 Add new 하면 새로 만들 수도 있다.

 


 

* Wear OS와 Phone 기기 페어링하기

 

 

- 디바이스 매니저의 메뉴에서 Pair Wearable 클릭

 

 

- 페어링이 가능한 Phone 기기가 나타난다.

 

 

- 이런 오류창이떴는데 Phone 기기의 구글 플레이스토어가 업데이트되지 않아서였다.

- 업데이트를 완료하고 Restart pairing 하니 정상적으로 구동됨!

 

 

 

 

- 우상단 pair with emulater를 클릭해서 연결할 수도 있다.

 

 

 

- 페어링이 완료된 창!

 

 

- 시계에서 사용할 구글계정을 선택하고 로그인해주면 된다.

- 가상기기를 실기기와 페어링할 수도 있다.

 

 

- 페어링이 진행되고 있는 모습

- 싱크(동기화)가 되었다면 전화번호 목록도 볼 수 있다.

 

 

- 완료된 화면!

 

 

- 이런 창이 나타난다.

 


 

- 이전 예제 코드 리뷰

 

 

- 데이터를 아답타에 넣어주고, 아답타에 View를 연결한 구조이다.

 

- 이 프로젝트를 wear os에넣고 run 시켜보려고 한다!

 

 

- 오버라이드한 3개의 메소드

 

- ViewHolder에서 뷰를 최소한의 갯수만 만들어서 리턴한다.

- 목록이 아무리 많아도 최대 3개만 만들어서 돌려가면서 쓰는 것이다(재활용한다)

- 그래서 recyclerView이다!!

 

 

- 내용을 바꾸어주는 부분. 뷰는 그대로 두고, 내용을 바꾸는 메소드

- 포지션 값을 전달하면 텍스트뷰의 내용만 바꾸어준다.

 

- 레이아웃을 전개해서 ViewHolder 의 생성자에 넣어주기

 

 

- ViewHolder 안에서는 뷰 안에 있는 텍스트뷰를 필드로 가지고있고

 가져가고 싶으면 getTextView로 가져가면 된다.

 

 

 

- getTextView로 모델의 데이터를 출력하는 구조이다.

 

- 클릭하면 어떤 동작을 하도록 만들고 싶다면?

 

 

- 크기를 확인하기 위해 cell의 리니어레이아웃 안에서 색상을 부여했다.

- 리니어 레이아웃의 크기가 이렇게 잡히는 것을 볼 수 있다!

 

- 각각의 레이아웃이 하나의 View가 되어 회전할 때마다 교체되는 것이다.

 

 

- LinearLayout 안에 텍스트뷰가 있는 구조

- gravity로 가운데정렬 시키기

 

- 아이템을 클릭했을때 어떤 동작을 하고 싶다면?

- 클릭리스너를 TextView에 달아야 한다.

 

 

- 이런 사각형 형태의 View가 리스트로 되어있는 것

 

 

- textView에 onclickListener를 추가한다.

 

 

- 클릭하면 이 안으로 실행순서가 들어오고, position값이 들어온다. 어떤 포지션(몇번째 목록)을 클릭했는지를 알 수 있다.

 

Log.d("Clicked!!", "position"+position);

  

 

- 이렇게 로그를 넣어두고 run 해보면 클릭한 position 값이 로그로 출력된다.

 


 

- 이런 작업들, 리스너에서 읽어온 것은 모두 아답타 안에서 일어나는 일이다.

 => 이것을 MainActivity로 전달하고 싶다면?

 

- 새 모듈 생성

step26listview2

 

MemberDto 클래스 생성

package com.example.step26listview2;

public class MemberDto {
    private int num;
    private String name;
    private String addr;

    public MemberDto(){}

    public MemberDto(int num, String name, String addr) {
        this.num = num;
        this.name = name;
        this.addr = addr;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

 

 

MemberAdapter

package com.example.step26listview2;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class MemberAdapter extends RecyclerView.Adapter<MemberAdapter.ViewHolder> {
    private List<MemberDto> members;
    private ItemClickListener listener;

    //아답타 안에서 일어나는 특정 이벤트를 전달받을 객체 type을 미리 정의하기
    public interface ItemClickListener{
        public void clicked(int index);
    }

    //생성자
    public MemberAdapter(List<MemberDto> members){
        this.members=members;
    }

    public void setOnItemClickListener(ItemClickListener listener){
        this.listener=listener;
    };

    @NonNull
    @Override
    public MemberAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //cell.xml 문서를 전개해서 View 객체를 얻어낸다.
        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.cell, parent, false);
        //ViewHolder 객체를 생성해서 리턴해준다.
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MemberAdapter.ViewHolder holder, int position) {
        //position에 해당하는 TextView의 참조값
        TextView textView=holder.getTextView();
        //TextView에 회원의 이름 출력하기
        textView.setText(members.get(position).getName());
        //TextView 에 클릭 리스너 등록
        textView.setOnClickListener(v -> {
            //ItemClickListener (여기에서는 MainActivity가 된다.) 에 정보를 전달한다.
            if(listener != null) {
                listener.clicked(position);
            }
        });
    }

    @Override
    public int getItemCount() {
        return members.size();
    }

    //ViewHolder를 내부 클래스로 정의한다.
    public static class ViewHolder extends RecyclerView.ViewHolder{
        //필드
        private TextView textView;
        //생성자
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            //TextView의 참조값을 얻어내서 필드에 저장
            textView=itemView.findViewById(R.id.textView);
        }
        //TextView getter 메소드
        public TextView getTextView(){
            return textView;
        }
    }
}

 

- RecyclerView.Adapter 를 상속받아서 만들기. 추상메소드도 오버라이드

 

 

- ViewHolder를 내부클래스로 정의했다.

 

 

- 필드 선언, 생성자 만들어주기

- 목록을 생성자의 인자로 전달받아서 필드에 저장

 

 

- 인터페이스 ItemClickListener 정의하기

- 이벤트를 전달받을 때 이 객체를 사용하면 된다.

 

 

- ItemClickListener 타입을 커스텀으로 정의했다.

- 리스너 인터페이스의 참조값을 여기에 넣어주려고 한다.

 

 

- MainActivity 에서 사용하려면 일단 인터페이스 구현

- 이 안에서 아답타의 참조값에 점찍어서 setOnItemClickListener 로 사용

- 그 값을 필드에 넣어놓고 필요할때 사용하겠다는 뜻

 


 

- 새 레이아웃 리소스 파일 생성

cell.xml

<?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="match_parent"
    android:gravity="center">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:textAlignment="center"
        android:textSize="20sp"/>

</LinearLayout>

 

 

- TextView를 하나 만들어준다.

 

 

- onCreateViewHolder 메소드에서 객체를 얻어내고,

 view객체로 viewholder 객체를 생성해서 리턴해준다. 아래에서 사용할 수 있도록!

 

 

- 여기서 생성한 참조값이 위 메소드에 전달되고, index도 함께 전달된다.

 

 

 

- 상속받은 Adapter에 제너릭<> 을 설정해야 한다.

- 우리가 직접 만든 ViewHolder를 사용할것

 

 

MemberAdapter.ViewHolder

- 메소드에 타입을 이렇게 적어주어야 한다.

 

 

- 위 clicked 메소드는 인덱스만 전달하는 것으로 수정...

 

 

 

- 클릭했을 때 null일 경우를 방지해야 한다.(position값이 들어오지 않는 경우)

- 이 position값으로 이벤트를 처리해준다.

 

 

 

- 이제 이 MemberAdapter.ItemClickListener를 Activity에서 구현하면 가져다가 사용할 수 있다.

 


 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout 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:padding="@dimen/box_inset_layout_padding"
    tools:context=".MainActivity"
    tools:deviceIds="wear">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="@dimen/inner_frame_layout_padding"
        app:layout_boxedEdges="all">

        <androidx.wear.widget.WearableRecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/recyView"
            android:scrollbars="vertical"/>

    </FrameLayout>
</androidx.wear.widget.BoxInsetLayout>

 

 

- WearableRecycleView 를 FrameLayout 안에 추가해주기

 

 

MainActivity

package com.example.step26listview2;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import androidx.wear.widget.WearableLinearLayoutManager;
import androidx.wear.widget.WearableRecyclerView;

import com.example.step26listview2.databinding.ActivityMainBinding;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity implements MemberAdapter.ItemClickListener{

    private TextView mTextView;
    private ActivityMainBinding binding;
    MemberAdapter adapter;
    List<MemberDto> members;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //Sample Model
        members=new ArrayList<>();
        members.add(new MemberDto(1, "바나나", "서울"));
        members.add(new MemberDto(2, "딸기", "부산"));
        members.add(new MemberDto(3, "복숭아", "대전"));
        members.add(new MemberDto(4, "사과", "서울"));
        members.add(new MemberDto(5, "오렌지", "서울"));

        WearableRecyclerView recyclerView=binding.recyView;
        recyclerView.setHasFixedSize(true);
        recyclerView.setEdgeItemsCenteringEnabled(true);
        recyclerView.setLayoutManager(new WearableLinearLayoutManager(this));
        //아답타 연결
        adapter=new MemberAdapter(members);
        //RecyclerView에 연결
        recyclerView.setAdapter(adapter);
        adapter.setOnItemClickListener(this);
    }

    @Override
    public void clicked(int index) {
        //Model을 변경하고
        members.get(index).setName(index+" clicked!");
        //Adapter에 Model이 변경되었다고 알린다.
        adapter.notifyDataSetChanged();
    }
}

 

 

- 샘플 모델을 추가하고, 아답타를 연결해준다.

 

 

- 우리가 아답타에서 정의한 인터페이스를 직접 구현한다.

 

 

- 메소드가 오버라이드된다. index 값이 전달된다.

 

 

 

- adapter, members 2개의 값을 필드로 만들어준다.

 

 

adapter.notifyDataSetChanged()

- clicked 메소드 안에서 아답타 출력 작업을 해주면 완료...!

 

 

 

- 특정 아이템을 클릭하면 index 번호+clicked! 로 바뀐다.

 

 

 

 

- Wear OS 기기에서 확인해보면, 우리가 만든 앱도 이렇게 앱 목록에 들어가 있는 것을 볼 수 있다.