카드 스크롤러

Glass를 사용하면 스크롤 등 카드와 다양한 상호작용을 할 수 있습니다. 살펴보겠습니다.

활동에서 카드 스크롤

Glass 디스플레이와 터치패드는 스와이프할 수 있는 카드를 표시하는 데 적합하며 볼 수 있습니다. 활동을 빌드하는 경우 동일한 유형의 효과를 CardScrollView 드림 위젯에 추가합니다.

  1. 구현 CardScrollAdapter 드림 코드를 사용하여 CardScrollView 표준 뷰 계층 구조를 직접 빌드하거나 CardBuilder 드림 클래스에 대해 자세히 알아보세요.
  2. 만들기 CardScrollView 드림 는 CardScrollAdapter 공급업체로 지정할 수 있습니다.
  3. 활동의 콘텐츠 뷰를 CardScrollView 드림 또는 CardScrollView 드림 있습니다.

다음은 카드 3개를 스크롤하는 간단한 구현입니다.

public class CardScrollActivity extends Activity {

    private List<CardBuilder> mCards;
    private CardScrollView mCardScrollView;
    private ExampleCardScrollAdapter mAdapter;

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

        createCards();

        mCardScrollView = new CardScrollView(this);
        mAdapter = new ExampleCardScrollAdapter();
        mCardScrollView.setAdapter(mAdapter);
        mCardScrollView.activate();
        setContentView(mCardScrollView);
    }

    private void createCards() {
        mCards = new ArrayList<CardBuilder>();

        mCards.add(new CardBuilder(this, CardBuilder.Layout.TEXT)
                .setText("This card has a footer.")
                .setFootnote("I'm the footer!"));

        mCards.add(new CardBuilder(this, CardBuilder.Layout.CAPTION)
                .setText("This card has a puppy background image.")
                .setFootnote("How can you resist?")
                .addImage(R.drawable.puppy_bg));

        mCards.add(new CardBuilder(this, CardBuilder.Layout.COLUMNS)
                .setText("This card has a mosaic of puppies.")
                .setFootnote("Aren't they precious?")
                .addImage(R.drawable.puppy_small_1);
                .addImage(R.drawable.puppy_small_2);
                .addImage(R.drawable.puppy_small_3));
    }

    private class ExampleCardScrollAdapter extends CardScrollAdapter {

        @Override
        public int getPosition(Object item) {
            return mCards.indexOf(item);
        }

        @Override
        public int getCount() {
            return mCards.size();
        }

        @Override
        public Object getItem(int position) {
            return mCards.get(position);
        }

        @Override
        public int getViewTypeCount() {
            return CardBuilder.getViewTypeCount();
        }

        @Override
        public int getItemViewType(int position){
            return mCards.get(position).getItemViewType();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return mCards.get(position).getView(convertView, parent);
        }
    }
}

스크롤 카드와 상호작용

이후 CardScrollView 드림 AdapterView 확장 표준 Android 리스너를 구현할 수 있습니다.

  1. 상속된 CardScrollViewsetOnItemClickListener()
  2. onItemClick() 드림 핸들러에 전달합니다.

다음은 탭 사운드를 재생하는 이전 예를 확장한 것입니다. 카드를 탭할 때:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        setupClickListener();
        setContentView(mCardScrollView);
    }

    private void setupClickListener() {
        mCardScrollView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                am.playSoundEffect(Sounds.TAP);
            }
        });
    }

스크롤 카드 애니메이션

스크롤 카드에는 탐색, 삽입, 삭제라는 세 가지 애니메이션을 사용할 수 있습니다.

  1. 카드 세트의 지정된 위치에 있는 카드에 삽입 또는 삭제 작업을 구현합니다.
  2. 전화걸기 animate() 드림 CardScrollView.Animation enum의 값을 사용합니다.
  3. 애니메이션을 좀 더 매끄럽게 표시하려면 notifyDataSetChanged() animate() 메서드가 데이터 세트 뷰 업데이트를 처리합니다.

    private class ExampleCardScrollAdapter extends CardScrollAdapter {
        ...
    
        // Inserts a card into the adapter, without notifying.
        public void insertCardWithoutNotification(int position, CardBuilder card) {
            mCards.add(position, card);
        }
    }
    
    private void insertNewCard(int position, CardBuilder card) {
        // Insert new card in the adapter, but don't call
        // notifyDataSetChanged() yet. Instead, request proper animation
        // to inserted card from card scroller, which will notify the
        // adapter at the right time during the animation.
        mAdapter.insertCardWithoutNotification(position, card);
        mCardScrollView.animate(position, CardScrollView.Animation.INSERTION);
    }
    

카드 스크롤을 위한 성능 및 구현 도움말

앱 생성 시 다음과 같은 디자인과 성능에 미치는 영향을 염두에 두세요. 카드 스크롤러

카드 수명 주기

성능을 높이기 위해 CardScrollView 현재 페이지에서 실행 중인 CardScrollAdapter 제공합니다 (일반적으로 사용자에게 표시되는 확장 프로그램 등). 따라서 카드는 다음과 같은 네 가지 일반적인 상태 중 하나일 수 있습니다.

  • 분리됨 - 현재 카드 스크롤 뷰에는 이 카드가 필요하지 않습니다. 카드의 onDetachedToWindow()를 통해 알림이 전송됩니다. 메서드를 호출합니다.
  • 연결됨: 카드 스크롤 뷰가 어댑터에 카드를 요청합니다. getView(): 카드가 곧 '활성화'되기 때문입니다. 이 경우 카드의 onAttachedToWindow() 메서드를 통해 알림을 받습니다.
  • Activated(활성화됨): 카드가 사용자에게 부분적으로 표시되지만 카드는 스크롤됩니다. 뷰가 '선택'되지 않음 사용자에게 표시할 카드를 선택합니다. 이 'isActivated()' 메서드 이 경우 true을 반환합니다.
  • 선택됨 - 카드가 사용자의 볼 수 있습니다 getSelectedView()를 호출하면 현재 선택된 카드가 반환됩니다. isSelected() 메서드는 다음을 반환합니다. true를 반환합니다.

카드의 뷰를 애니메이션 처리하거나 다른 비용이 많이 드는 작업을 수행하는 경우 포드 내에서 작업을 시작하고 onAttachedToWindow() 드림 및 onDetachedToWindow() 리소스를 절약할 수 있습니다

카드 재활용

카드가 연결된 상태에서 분리되면 카드를 재활용하고 부착하는 카드에서 사용할 수 있습니다. 업데이트된 정보로 뷰를 재활용하는 것이 새 뷰를 만듭니다

카드 재활용을 활용하려면 getItemViewType()님, getViewTypeCount(), 및 getView() 메서드의 CardScrollAdapter 클래스에 대해 자세히 알아보세요. 그런 다음 CardBuilder 클래스를 사용하여 CardScrollAdapter에서 재활용을 구현합니다. 예를 들면 다음과 같습니다.

private List<CardBuilder> mCards;
...
/**
 * Returns the number of view types for the CardBuilder class. The
 * CardBuilder class has a convenience method that returns this value for
 * you.
 */
@Override
public int getViewTypeCount() {
    return CardBuilder.getViewTypeCount();
}

/**
 * Returns the view type of this card, so the system can figure out
 * if it can be recycled. The CardBuilder.getItemViewType() method
 * returns it's own type.
 */
@Override
public int getItemViewType(int position){
    return mCards.get(position).getItemViewType();
}

/**
 * When requesting a card from the adapter, recycle the view if possible.
 * The CardBuilder.getView() method automatically recycles the convertView
 * it receives, if possible, or creates a new view if convertView is null or
 * of the wrong type.
 */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return  mCards.get(position).getView(convertView, parent);
}

안정적인 카드 ID 구현

카드가 선택되어 사용자에게 표시될 때 기본 어댑터 변경이 사용자에게 표시되는 카드에 영향을 미치길 원함 확인할 수 있습니다. 예를 들어 사용자가 선택한 카드를 보고 있고 사용자가 해당 카드를 제거하면 왜냐하면 화면은 왼쪽으로 이동할 수 있기 때문입니다. CardScrollAdapter 드림 는 기본적으로 변경이 발생할 때 기본 데이터 세트에 ID를 재할당합니다.

카드에 고유 ID를 할당하는 것이 논리적으로 타당하다면 기본 데이터 세트에 일관된 ID를 제공하여 앞서 언급한 문제를 방지합니다. 이렇게 하려면 hasStableIds() 드림 그리고 true를 반환합니다. 이것은 시스템에서 CardScrollAdapter 드림 데이터 세트가 변경되더라도 안정적인 ID를 유지합니다 또한 getItemId() 드림 를 호출하여 어댑터의 카드에 적절한 고유 ID를 반환합니다. 기본 구현은 어댑터에서 카드의 위치 색인을 반환합니다. 이는 본질적으로 불안정합니다

비어 있는 CardScrollAdapter

어댑터에 대한 데이터 세트가 비어 있는 경우 기본 뷰는 검은색 화면이 표시됩니다. 이러한 경우에 다른 보기를 표시하려면 setEmptyView()를 사용하지 않습니다. 대신 CardScrollAdapter에서 단일 카드를 만드세요.

가로로 밀기 피드백

Glass에 내장된 여러 몰입형 기능으로 '눌림'을 유발합니다. 뒤로 스와이프할 때 피드백을 작업을 수행하지 않는다는 점입니다 예를 들어 사진을 찍은 후 스와이프하면 안 됩니다.

몰입도가 가로로 스와이프하는 동작을 사용하지 않는 경우 애플리케이션별 함수를 사용하려면 레이아웃은 CardScrollView 드림 광고가 표시됩니다.

  1. 다음 도우미 클래스를 프로젝트에 복사합니다.

    public class TuggableView extends CardScrollView {
    
        private final View mContentView;
    
        /**
         * Initializes a TuggableView that uses the specified layout
         * resource for its user interface.
         */
        public TuggableView(Context context, int layoutResId) {
            this(context, LayoutInflater.from(context)
                    .inflate(layoutResId, null));
        }
    
        /**
         * Initializes a TuggableView that uses the specified view
         * for its user interface.
         */
        public TuggableView(Context context, View view) {
            super(context);
    
            mContentView = view;
            setAdapter(new SingleCardAdapter());
            activate();
        }
    
        /**
         * Overridden to return false so that all motion events still
         * bubble up to the activity's onGenericMotionEvent() method after
         * they are handled by the card scroller. This allows the activity
         * to handle TAP gestures using a GestureDetector instead of the
         * card scroller's OnItemClickedListener.
         */
        @Override
        protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
            super.dispatchGenericFocusedEvent(event);
            return false;
        }
    
        /** Holds the single "card" inside the card scroll view. */
        private class SingleCardAdapter extends CardScrollAdapter {
    
            @Override
            public int getPosition(Object item) {
                return 0;
            }
    
            @Override
            public int getCount() {
                return 1;
            }
    
            @Override
            public Object getItem(int position) {
                return mContentView;
            }
    
            @Override
            public View getView(int position, View recycleView,
                    ViewGroup parent) {
                return mContentView;
            }
        }
    }
    
  2. 액티비티의 onCreate 메서드를 수정하여 CardScrollView 에는 레이아웃이 포함되어 있습니다.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // was: setContentView(R.layout.main_activity);
        setContentView(new TuggableView(this, R.layout.main_activity));
    }