스토리 안드로이드 앱의 화면 전환 애니메이션 적용기

 

1. Shared Elements Transition이란?

화면과 화면의 공유요소를 통해 하나의 액티비티에서 동작하는 듯한 좀더 자연스러운 사용자 경험을 줄 수 있다.

shared와 다른 효과의 차이는 영상을 통해 비교하여 확인이 가능하다(아래 링크있음)

 

 

첫번째 View1을 클릭하여 ActivityB에 전달한 후 View2로 전달하여 View1과 동일한 위치 및 크기를 설정하여

애니메이션을 시작한다. 마지막으로 ActivitiyC에서 View2의 원래위치 크기로 돌아가여 실행한다.

 

 

2. 적용 방법(Android 5.0이상부터 사용가능)

 

테마의 스타일을 정의할 때 Transition을 추가

sharedElementEnterTransition이 ActivityB에 대한 효과이고 sharedElementExitTransition은 AcitivityA에 대한 효과이다.

 

 

 스타일 테마 등록 후 -> 화면 전환에 사용되는 코드 작성

 

 

위에서 사용한 makeSceneTransitionAnimation의 함수 원형이다.

 

 

스타일 정의(MoveTransition Set)

각각 4가지 transitionSet의 의미:

위치와 크기 변경 / 배열과 회전을 변경 / 지정된 영역 변경 / 이미지 크기 및 배열 변경

 

 

 

마지막으로 이러한 형식으로 transition을 적용하면 된다.

 

 

 

 

3. 카카오스토리 적용 사례

 

FaceTransition은 얼굴 위주로 크롭하여 보여주는 기능(아래 링크 영상참조)-> 끊기지 않고 부드러워짐(매트릭스의 값이 달라지는 것을 막아줌)

 

 

 

 

현재 프레임을 캡쳐하여 비트맵으로 추출하여 사용한다.

 

 

프로필 진입(원에서 사각형으로 변경)

 

4. 이슈 및 해결

이슈1 -모서리의 라운드 이슈

원인 : 모서리의 라운드로 인하여 자연스럽지 않은 문제가 발생

해결 : 별도의 라운드 애니메이션을 적용한다(RoundTransition 생성)

 

 RoundTransition을 구현하기 위해서는 Transition을 상속받아 구현해야 한다

 

Capture(시작과 종료화면을 캡쳐)->저장했던 Transition value가 파라미터로 전달되어 Animator생성에 사용한다

Capture

 -public void captureStartValues(TransitionValues transitionValues)

 -public void captureEndValues(TransitionValues transitionValues)

Animator

 -public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues)



 

 

이슈2 - 연속클릭 이슈

원인 : 연속 클릭으로 인한 애니메이션 오류(startAcitivty 여러번 호출)

해결 : RxJava를 이용해 클릭 이벤트 흐름 제어

 

지속적으로 이벤트를 받아야 함으로 observer를 생성하고 여러개의 뷰에서 하나의 observable로 클릭이벤트를 전달하고

전달받은 함수를 throttleFirst를 통해 이슈 해결

 

 

이슈3 - 공유 뷰의 생성 지연

원인 : 공유 뷰의 생성 지연으로 애니메이션이 동작하지 못하고 화면 전환

해결 : 화면 전환 지연/ 화면 전환 시작 함수를 사용 

PostponedEnterTransition으로 화면 전환을 멈추고 이미지 로드가 완료되면 다시 함수를 호출해 사용하느 ㄴ형식이다.

 

 

 

이슈4 - 종료 위치 찾지 못하는 이슈

원인 : 종료 애니메이션이 위치를 찾지 못하는 문제

해결 : 공유에 사용한 뷰는 사라지지 않도록 관리(ViewPager adapter의 notifyDataSetChanged()를 쓰지 않고 직접

UI에 접근하여 갱신)

 

 

 

 


 

 

포스팅한 내용은 아래 동영상을 참고하여 만들었습니다.

 

 

출처 : if.kakao.com/session/109

 

if(kakao)2020

오늘도 카카오는 일상을 바꾸는 중

if.kakao.com

 

안드로이드 버전 카카오 T SDK 개발기

 

 

이번 주제는 카카오T에 주차서비스를 탑재하는 프로젝트를 다룰 것이다.

카카오 모빌리티에서 카카오T안드로이드 앱을 개발하고 있는 윌슨님의 프로젝트 진행과정에서

기술적인 문제와 문제 해결 방법을 설명하겠다.

카카오 내비에 카카오 모빌리티를 접목시키는 이유?

카카오 모빌리티는 "우리의 기술로 생활을 움직입니다"라는 비젼을 바탕으로 다양한 모빌리티 서비스를 제공한다.

카카오 내비가 제공하는 서비스를 생각해보자.

운전자는 목적지까지 원하는 경로로 이동한다. 하지만 주차 공간은 내비가 제공하지 못한다!

따라서 카카오 모빌리티의 또다른 서비스(주차)를 접목시킨다면 운행을 잘 마무리 할 수 있다.

 

 

 

카카오T주차는 사전예약과 자동정산의 서비스를 제공한다.

 

따라서 카카오 내비에 카카오T주차에 자동정산 서비스를 연동하는 프로젝트를 시행하였다.

이 프로젝트는 카카오T주차 서비스를 SDK로 만들어서 내비 앱에 탑재한다.

이를 통해 지속적으로 업데이트를 하여 배포할 수 있다.

 

 

SDK설계 과정

주차,대리,택시 모듈 : 개별 서비스 제공

플러그인 모듈 : 플랫폼성 기능에 인터페이스 선언

앱 모듈 : 인터페이스의 구현체를 제공

Base 모듈 : 각종 유틸리티 코드 

 

 

 

만약 주차 모듈이 동작하려면 주차 모듈이 의존하는 모듈( 플러그인,Base모듈)이 필요하다. 하지만 플러그인의 인터페이스를 실질적으로 구현하는 앱 모듈이 필요하다.

따라서 새로운 모듈인 코어 SDK 모듈이 필요하다.

1번은 빠르긴 하나 2번이 모듈로 재사용이 가능하기 때문에 유지보수하기에 적합하다.

 

 

 

주차 모듈 이외에 다른 모듈 또한 코어SDK 모듈을 사용할 가능성이 있기 때문에 직접 접근하는 방식이 아니라

주차SDK 모듈을 만든 후 연결시켜 주었다.

 

구현단계 문제

인증 문제 : 사용자 인증이 필요(로그인) 앱로그인과 별개로 SDK에서 또한 로그인이 필요하다. 따라서 앱에서 로그인한 정보를 이용해 SDK에서 사용자 인증을 연동시키게 한다. 카카오 계정을 기반으로 관리하기 때문에 두개의 매핑이 가능하다.

푸쉬 문제 : 자동정산 서비스는 주차장 입차/출차 문제를 푸쉬로 발송한다. 앱은 푸쉬를 하지만 sdk에서 푸쉬를 한다? 말이 안된다. 결국 내비 앱과 내비 서버를 의존한다. 주차서버가 내비 서버에 푸쉬발송요청 API를 호출한다.  그 이후 아래와 같은 절차를 밟는다.

 

 

이후 2019년 7월 말부터 앱을 배포하고 서비스를 시행했다.

 

서비스 시작 후 작업을 할 때마다 카카오T와 카카오T SDK를 이중으로 코드를 수정해야 함. 따라서 이와 같은 모듈을 분리하여 코드를 쉽게 유지보수할 수 있다.

 

 


 

포스팅한 내용은 아래의 동영상을 참고하여 만들었습니다.

 

 

출처 : if.kakao.com/session/58

 

if(kakao)2020

오늘도 카카오는 일상을 바꾸는 중

if.kakao.com

 

 

DatePickerDialog

- 날짜 정보를 보여준다.

TimePickerDialog

- 시간 정보를 보여준다.

 

각자 TIME 과 DATE 버튼을 눌렀을 때 PickerDialog가 동작하여 시간/날짜 설정후 TextView로 보여주는 과정

 

 

xml에서 android:onClick="mOnClick"을 넣어줘야 함

<?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">

    <TextView
        android:id="@+id/textResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Result"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnSelectData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="91dp"
        android:text="Date"
        android:onClick="mOnClick"
        app:layout_constraintBaseline_toBaselineOf="@+id/btnSelectTime"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/btnSelectTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="44dp"
        android:layout_marginEnd="44dp"
        android:text="Time"

        android:onClick="mOnClick"
        app:layout_constraintEnd_toStartOf="@+id/btnSelectData"
        app:layout_constraintTop_toBottomOf="@+id/textResult" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

updateResult()함수를 통해 textView에 결과값을 나타낸다.


public class MainActivity extends AppCompatActivity {

    private String mStrDate ="date";
    private String mStrTime ="time";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        updateResult();
        Button btnDate = (Button)findViewById(R.id.btnSelectData);
        Button btnTime = (Button)findViewById(R.id.btnSelectTime);
    }

    private void updateResult(){
        TextView textResult = (TextView)findViewById(R.id.textResult);
        textResult.setText("날짜 : "+mStrDate +"\n"+ "시간 : "+mStrTime);
    }
    public void mOnClick(View v){
        Calendar calendar = Calendar.getInstance();
        switch (v.getId()){
            case R.id.btnSelectData:
                int year = calendar.get(Calendar.YEAR);
                int month = calendar.get(Calendar.MONTH);
                int day = calendar.get(Calendar.DAY_OF_MONTH);
                new DatePickerDialog(this,mDateSetListener,year,month,day).show();
                break;
            case R.id.btnSelectTime:
                int hour = calendar.get(Calendar.HOUR_OF_DAY);
                int minute = calendar.get(Calendar.MINUTE);
                new TimePickerDialog(this,mTimeSetListener,hour,minute,true).show();
                break;
        }
    }
    private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener(){

        @Override
        public void onDateSet(DatePicker datePicker, int year, int month, int dayOfMonth) {
            mStrDate = String.format("%d년 %d월 %d일",year,month+1,dayOfMonth);
            updateResult();
        }
    };
    private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
        @Override
        public void onTimeSet(TimePicker timePicker, int hourOfDay, int minute) {
                  mStrTime= String.format("%d시 %d분",hourOfDay,minute);
                  updateResult();
        }
    };

}

 

 

처음 실행 모습

 

 

TIME버튼 눌렀을 때

 

 

 

DATE버튼 눌렀을 때

 

 

바뀐 TextView

 

+ Recent posts