RecyclerView
を使用して作業したアプリケーションに対して、このUIを複製する必要がありました。
各行は水平のLinearLayout
であり、アイコン、ライン、右側のビューが含まれます。線は背景が丸いFrameLayout
で、半透明の円はView
sです。
行間にスペースがないため、線の単一部分が結合されているように見えます。
アイテムのレイアウトは次のようになります。
<LinearLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="horizontal"
Android:layout_width="match_parent"
Android:layout_height="wrap_content">
<!-- the circular icon on the left -->
<ImageView
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:src="@drawable/ic_place"
Android:tint="@Android:color/white"
Android:layout_marginRight="24dp"
Android:padding="4dp"
Android:background="@drawable/circle_bg"/>
<!-- the blue line -->
<FrameLayout
Android:layout_width="15dp"
Android:layout_height="match_parent"
Android:padding="2dp"
Android:id="@+id/item_line">
<!-- the semi transparent circle on the line -->
<View
Android:layout_width="11dp"
Android:layout_height="11dp"
Android:background="@drawable/circle"/>
</FrameLayout>
<!-- views at the right of the blue line -->
<LinearLayout
Android:layout_width="0dp"
Android:layout_weight="1"
Android:layout_height="wrap_content"
Android:orientation="vertical"
Android:paddingLeft="24dp"
Android:paddingBottom="32dp"
Android:clickable="true"
Android:background="?android:attr/selectableItemBackground">
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:singleLine="true"
Android:ellipsize="end"
Android:textAppearance="@style/TextAppearance.AppCompat.Title"
Android:id="@+id/item_title"/>
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:id="@+id/item_subtitle"/>
<!-- other views -->
</LinearLayout>
</LinearLayout>
上のキャップ、中央のキャップ、最後のキャップの線のキャップを異なる方法でレンダリングする便利な方法は、Adapter
で位置関連のitemViewTypesを使用することです。
private static final int VIEW_TYPE_TOP = 0;
private static final int VIEW_TYPE_MIDDLE = 1;
private static final int VIEW_TYPE_BOTTOM = 2;
private List<Item> mItems;
// ...
class ViewHolder extends RecyclerView.ViewHolder {
TextView mItemTitle;
TextView mItemSubtitle;
FrameLayout mItemLine;
public ViewHolder(View itemView) {
super(itemView);
mItemTitle = (TextView) itemView.findViewById(R.id.item_title);
mItemSubtitle = (TextView) itemView.findViewById(R.id.item_subtitle);
mItemLine = (FrameLayout) itemView.findViewById(R.id.item_line);
}
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
Item item = mItems.get(position);
// Populate views...
switch(holder.getItemViewType()) {
case VIEW_TYPE_TOP:
// The top of the line has to be rounded
holder.mItemLine.setBackground(R.drawable.line_bg_top);
break;
case VIEW_TYPE_MIDDLE:
// Only the color could be enough
// but a drawable can be used to make the cap rounded also here
holder.mItemLine.setBackground(R.drawable.line_bg_middle);
break;
case VIEW_TYPE_BOTTOM:
holder.mItemLine.setBackground(R.drawable.line_bg_bottom);
break;
}
}
@Override
public int getItemViewType(int position) {
if(position == 0) {
return VIEW_TYPE_TOP;
else if(position == mItems.size() - 1) {
return VIEW_TYPE_BOTTOM;
}
return VIEW_TYPE_MIDDLE;
}
背景のドロアブルは次のように定義できます。
<!-- line_bg_top.xml -->
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
<solid Android:color="@color/colorPrimary"/>
<corners
Android:topLeftRadius="15dp"
Android:topRightRadius="15dp"/>
<!-- this has to be at least half of line FrameLayout's
width to appear completely rounded -->
</shape>
<!-- line_bg_middle.xml -->
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
<solid Android:color="@color/colorPrimary"/>
</shape>
<!-- line_bg_bottom.xml -->
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
<solid Android:color="@color/colorPrimary"/>
<corners
Android:bottomLeftRadius="15dp"
Android:bottomRightRadius="15dp"/>
</shape>
もちろん、ListView
を使用することもできます。また、ステップが常に数個であることがわかっている場合は、単純な垂直LinearLayout
で十分です。
悲しいことに、Google Maps Androidアプリはオープンソースではありません。公式な方法を知るのはそれほど簡単ではありません... Material Designの解釈!