卡片滚动条

借助 Glass,你可以与卡片进行丰富的互动,例如滚动浏览 和动画。

在活动中滚动卡片

Glass 显示屏和触控板非常适合显示可滑动的卡片, 就像 Google Glass 时间轴中一样如果您正在构建 Activity,则可以创建 使用 CardScrollView 微件。

  1. 实施 CardScrollAdapter 向 Pod 提供 CardScrollView。 您可以自行构建标准视图层次结构,也可以使用 CardBuilder 类。
  2. 创建 CardScrollView 使用 CardScrollAdapter 作为卡片的供应商
  3. 将 activity 的内容视图设置为 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 您可以实现标准的 Android 监听器

  1. 调用继承的 你的CardScrollView上的setOnItemClickListener()
  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 枚举的值。
  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()通知您 方法。
  • Attached(已附加)- 卡片滚动视图向适配器请求卡片,其中包含 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);
}

实现稳定的卡 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. 修改 activity 中的 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));
    }