แถบเลื่อนการ์ด

คุณสามารถใช้ Glass สร้างการโต้ตอบที่สมบูรณ์กับการ์ด เช่น การเลื่อนและภาพเคลื่อนไหว

การเลื่อนการ์ดในกิจกรรม

จอแสดงผลและทัชแพดของ 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 คุณจึงนํา Listener Android มาตรฐานมาใช้ได้

  1. โทรหา setOnItemClickListener() ที่รับค่ามาใน CardScrollView
  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);
            }
        });
    }

การสร้างการ์ดการเลื่อนแบบเคลื่อนไหว

การ์ดเลื่อนมีภาพเคลื่อนไหว 3 รายการ ได้แก่ การนําทาง การแทรก และการลบ

  1. แทรกหรือลบการ์ดในการ์ดในตําแหน่งที่ระบุในชุดการ์ด
  2. โทร animate() และใช้ค่าจาก Enum ของ CardScrollView.Animation
  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 มีให้ (โดยทั่วไปจะเป็นการ์ดที่ผู้ใช้มองเห็นได้และการ์ดอื่นๆ บางรายการ) ด้วยเหตุนี้ การ์ดอาจอยู่ในสถานะใดสถานะหนึ่งใน 4 สถานะต่อไปนี้

  • ถูกปลด - มุมมองการเลื่อนของการ์ดไม่จําเป็นต้องใช้การ์ดนี้ในตอนนี้ คุณจะได้รับการแจ้งเตือนโดยใช้เมธอด onDetachedToWindow() ของบัตร หากก่อนหน้านี้ได้แนบการ์ดไว้แล้วและปลดการ์ดออก
  • แนบ - มุมมองการเลื่อนของการ์ดจะขอการ์ดจากอะแดปเตอร์ด้วย getView() เนื่องจากการ์ดอยู่ใกล้กับ "เปิดใช้งานแล้ว" คุณจะได้รับการแจ้งเตือนโดยใช้เมธอด onAttachedToWindow() ของการ์ดเมื่อเกิดเหตุการณ์นี้
  • เปิดใช้งาน - ผู้ใช้จะเห็นการ์ดเพียงบางส่วน แต่มุมมองการเลื่อนการ์ดยังไม่ได้ "เลือก" การ์ดที่จะแสดงแก่ผู้ใช้ เมธอด 'isActivated()' แสดงผล true ในกรณีนี้
  • เลือกไว้ - การ์ดจะแสดงหน้าจอทั้งหมดของผู้ใช้ การโทร getSelectedView() จะแสดงบัตรที่เลือกในปัจจุบัน เมธอด isSelected() จะแสดงผลเป็น "จริง" ในกรณีนี้

หากสร้างภาพเคลื่อนไหวของมุมมองการ์ดหรือดําเนินการอื่นๆ ที่มีค่าใช้จ่าย ให้เริ่มและหยุดการดําเนินการใน 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);
}

การใช้รหัสบัตรแบบคงที่

เมื่อเลือกการ์ดและแสดงให้ผู้ใช้เห็น คุณอาจไม่ต้องการให้การเปลี่ยนแปลงอะแดปเตอร์ที่เกี่ยวข้องส่งผลต่อการ์ดที่ผู้ใช้เห็นในขณะนั้น เช่น หากผู้ใช้กําลังดูการ์ดที่เลือกไว้ และระบบได้นําการ์ดออกทางด้านซ้ายของการ์ดนั้นแล้ว การ์ดที่ผู้ใช้อาจดูทางด้านซ้ายอาจเปลี่ยนไป เนื่องจาก CardScrollAdapter จะกําหนดรหัสให้กับชุดข้อมูลที่จําเป็นอีกครั้งเมื่อมีการเปลี่ยนแปลง

หากสมเหตุสมผลในการมอบหมายรหัสที่ไม่ซ้ํากันของบัตร คุณจะรักษารหัสที่สอดคล้องกันในชุดข้อมูลที่สําคัญได้เพื่อป้องกันปัญหาที่กล่าวถึงข้างต้น หากต้องการดําเนินการ ให้ลบล้าง hasStableIds() และแสดงผล true ซึ่งจะกําหนดให้กับระบบที่ CardScrollAdapter คงรหัสแบบคงที่ในการเปลี่ยนแปลงชุดข้อมูลไว้ นอกจากนี้ในการใช้งาน getItemId() เพื่อแสดงรหัสที่ไม่ซ้ํากันที่เหมาะสมสําหรับการ์ดในอะแดปเตอร์ การใช้งานเริ่มต้นจะแสดงดัชนีตําแหน่งของการ์ดในอะแดปเตอร์ ซึ่งโดยพื้นฐานแล้วจะไม่เสถียร

ตัวเลื่อนการ์ดว่างเปล่า

หากคุณมีชุดข้อมูลที่ว่างเปล่าสําหรับอะแดปเตอร์ มุมมองเริ่มต้นคือการแสดงหน้าจอสีดํา หากต้องการแสดงมุมมองอื่นในกรณีเหล่านี้ ก็อย่าใช้ setEmptyView() ให้สร้างการ์ดเดี่ยวใน CardScrollAdapter แทน

ความคิดเห็นเกี่ยวกับการติดตามในแนวนอน

การดื่มด่ํากับระบบจํานวนมากใน Glass จะให้ความคิดเห็นแก่ "การดึง" เมื่อปัดไปข้างหลังและไม่ทํางาน เช่น คุณจะเห็นความคิดเห็นนี้เมื่อปัดหลังจากที่ถ่ายรูป

หากการแสดงผลไม่ได้ใช้การปัดแบบแนวนอนเพื่อใช้ฟังก์ชันเฉพาะแอปพลิเคชัน ให้ระบุเอฟเฟกต์แบบพุลนี้โดยรวมเลย์เอาต์ไว้ใน CardScrollView ที่มีการ์ด 1 ใบ

  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));
    }