اسکرول کارت

با Glass، می توانید تعاملات غنی با کارت های خود مانند اسکرول و انیمیشن ها ایجاد کنید.

اسکرول کارت در فعالیت ها

صفحه نمایش شیشه ای و تاچ پد برای نمایش کارت های قابل کشیدن مانند جدول زمانی Glass عالی هستند. اگر در حال ساخت یک فعالیت هستید، می توانید همان نوع افکت را با ویجت CardScrollView ایجاد کنید.

  1. برای تامین کارت ها به CardScrollView یک CardScrollAdapter پیاده سازی کنید. می توانید خودتان یک سلسله مراتب نمای استاندارد بسازید یا از کلاس CardBuilder استفاده کنید.
  2. یک CardScrollView ایجاد کنید که از CardScrollAdapter به عنوان تامین کننده کارت ها استفاده می کند.
  3. نمای محتوای فعالیت خود را به صورت CardScrollView تنظیم کنید یا CardScrollView در یک طرح‌بندی نمایش دهید.

در اینجا یک پیاده سازی ساده است که از طریق سه کارت پیمایش می کند:

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 را گسترش می دهد، می توانید شنوندگان استاندارد اندروید را پیاده سازی کنید.

  1. setOnItemClickListener() به ارث رسیده را در CardScrollView خود فراخوانی کنید.
  2. یک کنترل کننده onItemClick() برای رویداد tap پیاده سازی کنید.

در اینجا یک افزونه برای مثال قبلی وجود دارد که وقتی روی کارت ضربه می‌زنید صدای ضربه را پخش می‌کند:

    @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 استفاده کنید.
  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() کارت مطلع می شوید.
  • فعال شده - کارت تا حدی برای کاربر قابل مشاهده است، اما نمای اسکرول کارت کارت را برای نمایش به کاربر "انتخاب" نکرده است. متد 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);
}

اجرای شناسه های ثابت کارت

وقتی کارتی انتخاب می‌شود و به کاربران نمایش داده می‌شود، ممکن است بخواهید تغییرات آداپتور اصلی روی کارتی که کاربران در آن لحظه می‌بینند، تأثیر نگذارد. به عنوان مثال، اگر کاربر در حال مشاهده یک کارت انتخابی باشد، و کارتی در سمت چپ آن کارت حذف شود، کارتی که کاربر مشاهده می‌کند به طور بالقوه می‌تواند به سمت چپ جابه‌جا شود، زیرا CardScrollAdapter زمانی که تغییرات رخ می‌دهد، شناسه‌ها را به مجموعه داده‌های زیرین اختصاص می‌دهد. ، به صورت پیش فرض.

اگر منطقاً اختصاص شناسه‌های منحصربه‌فرد به کارت‌هایتان منطقی است، می‌توانید یک شناسه ثابت در مجموعه داده‌های زیربنایی برای جلوگیری از مشکل فوق‌الذکر حفظ کنید. برای انجام این کار، hasStableIds() لغو کنید و true برگردانید. این به سیستم مشخص می‌کند که CardScrollAdapter شناسه‌های پایدار را در تغییرات مجموعه داده‌ها حفظ می‌کند. علاوه بر این، getItemId() برای بازگرداندن شناسه منحصر به فرد مناسب برای کارت های موجود در آداپتور خود پیاده سازی کنید. اجرای پیش‌فرض، شاخص موقعیت کارت را در آداپتور برمی‌گرداند که ذاتاً ناپایدار است.

کارت اسکرول آداپتور خالی

وقتی یک مجموعه داده خالی برای آداپتورها دارید، نمای پیش‌فرض نمایش یک صفحه سیاه است. اگر می خواهید نمای متفاوتی را در این موارد نشان دهید، از 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));
    }