Computer >> 컴퓨터 >  >> 시스템 >> Android

안드로이드 Visibility Listener(가시성 리스너) 사용법과 활용 이유

안드로이드 UI는 여러 개의 View(뷰)로 구성되며, 일반적인 애플리케이션에는 그 수가 상당히 많습니다. 사용자가 지금 화면에서 어떤 뷰를 보고 있는지 파악하려면 Visibility Listener(가시성 리스너)를 활용해야 합니다.

아래에서 뷰의 가시성 상태를 식별할 수 있는 다양한 방법을 소개합니다.

레이아웃에 뷰 추가하기

리스너가 정상적으로 동작하려면 먼저 해당 뷰가 레이아웃 계층 구조 안에 존재해야 합니다. 뷰가 레이아웃에 포함되는 방식은 두 가지입니다.

  1. XML 파일에 정의된 대로 레이아웃에 이미 포함되어 있는 경우
  2. 코드에서 뷰를 동적으로 생성한 뒤 addView 메서드로 추가하는 경우
public void addView (View child, ViewGroup.LayoutParams params)

여기서 child는 추가하려는 뷰를 의미하고, LayoutParams는 레이아웃 매개변수(예: 가로·세로 크기)를 나타냅니다.

뷰의 가시성 상태는 Integer 타입이며 다음 세 가지 값 중 하나를 가집니다.

  1. VISIBLE (0) – 사용자에게 뷰가 보이는 상태
  2. INVISIBLE (4) – 사용자에게는 보이지 않지만, 레이아웃에서 공간은 그대로 차지하는 상태
  3. GONE (8) – 보이지 않으며, 레이아웃에서도 공간을 차지하지 않는 상태

뷰가 레이아웃 계층에 포함된 이후에는, 가시성 변경 시점을 감지할 수 있도록 플랫폼에서 기본으로 제공하는 몇 가지 옵션을 사용할 수 있습니다.

onVisibilityChanged

protected void onVisibilityChanged (View changedView, int visibility)

이 메서드는 해당 뷰 자신 또는 그 조상(부모) 뷰의 가시성이 변경될 때 호출됩니다. 변경된 가시성 값은 visibility 매개변수로 전달됩니다.

onWindowVisibilityChanged

protected void onWindowVisibilityChanged (int visibility)

이 메서드는 뷰가 속한 윈도우(Window)의 가시성이 변경될 때 호출됩니다. 다만 윈도우가 가시 상태라고 해도, 다른 윈도우에 가려져 실제로는 사용자에게 보이지 않을 수 있다는 점에 유의해야 합니다.

실전 예제: 리스너 동작 확인하기

두 리스너의 동작을 직접 확인하기 위해 간단한 프로젝트를 만들어 보겠습니다. TextView와 버튼 하나를 담은 LinearLayout을 준비하고, 버튼을 클릭하면 커스텀 뷰가 레이아웃에 추가되도록 구현합니다.

안드로이드 Visibility Listener(가시성 리스너) 사용법과 활용 이유

커스텀 뷰 코드는 다음과 같습니다.

package com.tomerpacific.viewvisibility;

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import static android.view.Gravity.CENTER;

public class MyCustomView extends LinearLayout {

    private final String TAG = MyCustomView.class.getSimpleName();

    public MyCustomView(Context context) {
        super(context);
        this.setBackgroundColor(Color.GREEN);
        this.setGravity(CENTER);
        TextView myTextView = new TextView(context);
        myTextView.setText("My Custom View");
        addView(myTextView);
    }

    @Override
    public void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);

        Log.d(TAG, "View " + changedView + " changed visibility to " + visibility);
    }

    @Override
    public void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);

        Log.d(TAG, "Window visibility changed to " + visibility);
    }

}

마지막으로 MainActivity의 코드입니다.

package com.tomerpacific.viewvisibility;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    private Button addCustomViewBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        addCustomViewBtn = (Button) findViewById(R.id.addCustomViewBtn);

        addCustomViewBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LinearLayout mainLayout = (LinearLayout) findViewById(R.id.mainLayout);
                MyCustomView myCustomView = new MyCustomView(getApplicationContext());
                myCustomView.setLayoutParams(new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT, 
                    LinearLayout.LayoutParams.WRAP_CONTENT));

                mainLayout.addView(myCustomView);
            }
        });
    }
}

위 코드에서는 버튼에 OnClick 리스너를 연결하여, 클릭 시 커스텀 뷰가 생성되어 레이아웃에 추가되도록 했습니다.

앱을 실행하고 버튼을 누르면 로그를 통해 가시성 변경 이벤트가 어떻게 발생하는지 확인할 수 있습니다.

전체 샘플 프로젝트는 원문의 다운로드 링크에서 받을 수 있습니다.

ViewTreeObserver 활용하기

ViewTreeObserver는 뷰 트리에서 발생하는 다양한 가시성 변화를 통지받을 수 있는 여러 리스너를 제공하는 기본 객체입니다. 특히 주목할 만한 리스너는 다음과 같습니다.

  • OnGlobalLayoutListener
  • OnWindowAttachListener
  • OnWindowFocusChangeListener

ViewTreeObserver를 등록하는 방법은 다음과 같습니다.

LinearLayout linearLayout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);

ViewTreeObserver viewTreeObserver = linearLayout.getViewTreeObserver(); 
viewTreeObserver.addOnGlobalLayoutListener (new ViewTreeObserver.OnGlobalLayoutListener() { 
    
    @Override 
    public void onGlobalLayout() {
        linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
        //TODO Add Logic
    } 
});

참고로 removeOnGlobalLayoutListener는 API 15 이상에서 사용할 수 있습니다.

linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this) 코드는 리스너가 단 한 번만 호출되도록 보장하는 역할을 합니다. 레이아웃 변경 사항을 계속해서 감지하고 싶다면 이 줄을 제거하면 됩니다.

의견이나 제안이 있다면 언제든지 알려주세요.