利用 RecyclerView 实现垂直交错的网格
2015-12-06 23:37
441 查看
效果如下(关于 RecyclerView 见前文):
MainActivity.java :
需要让适配器将数据与视图关联起来:
SimpleItemAdapter.java :
交错网格,继承 SpanSizeLookup:
GridStaggerLookup.java :
设置页边距:
InsetDecoration.java :
连接线 ItemDecoration :
ConnectorDecoration.java :
collection_item.xml :
dimens.xml :
layout_options.xml :
MainActivity.java :
package com.crazy.simplerecyclerviewof; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements SimpleItemAdapter.OnItemClickListener{ private RecyclerView mRecyclerView; private SimpleItemAdapter mAdapter; /* 布局管理器 */ private GridLayoutManager mVerticalGridManager; /* 修饰 */ private ConnectorDecoration mConnectors; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRecyclerView = new RecyclerView(this); mVerticalGridManager = new GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false); // 垂直网格的连接线修饰 mConnectors = new ConnectorDecoration(this); // 交错垂直网格 mVerticalGridManager.setSpanSizeLookup(new GridStaggerLookup()); mAdapter = new SimpleItemAdapter(this); mAdapter.setOnItemClickListener(this); mRecyclerView.setAdapter(mAdapter); // 对所有连接应用边缘修饰 mRecyclerView.addItemDecoration(new InsetDecoration(this)); // 默认为垂直布局 selectLayoutManager(R.id.action_grid_vertical); setContentView(mRecyclerView); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.layout_options, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { return selectLayoutManager(item.getItemId()); } private boolean selectLayoutManager(int id) { switch (id){ case R.id.action_grid_vertical: mRecyclerView.setLayoutManager(mVerticalGridManager); mRecyclerView.addItemDecoration(mConnectors); return true; case R.id.action_add_item: // 插入新的项 mAdapter.insertItemAtIndex("百家姓:", 1); return true; case R.id.action_remove_item: // 删除第一项 mAdapter.removeItemAtIndex(1); return true; default: return false; } } @Override public void onItemClick(SimpleItemAdapter.ItemHolder item, int position) { Toast.makeText(this, item.getSummary(), Toast.LENGTH_SHORT).show(); } }
需要让适配器将数据与视图关联起来:
SimpleItemAdapter.java :
package com.crazy.simplerecyclerviewof; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SimpleItemAdapter extends RecyclerView.Adapter<SimpleItemAdapter.ItemHolder> { /** * 单击处理程序的接口,与 AdapterView 不同, * RecyclerView 没有自己的内部接口 */ public interface OnItemClickListener { public void onItemClick(ItemHolder item, int position); } private static final String[] ITEMS = { "赵...", "钱...", "孙...", "李...", "周...", "吴...", "郑...", "王...", "冯...", "陈...", "楮...", "卫..." }; private List<String> mItems; private OnItemClickListener mOnItemClickListener; private LayoutInflater mLayoutInflater; public SimpleItemAdapter(Context context) { mLayoutInflater = LayoutInflater.from(context); // 创建虚拟项的静态列表 mItems = new ArrayList<>(); mItems.addAll(Arrays.asList(ITEMS)); mItems.addAll(Arrays.asList(ITEMS)); } /** * 创建 */ @Override public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = mLayoutInflater.inflate(R.layout.collection_item, parent, false); // 创建新的视图 return new ItemHolder(itemView, this); } /** * 绑定 */ @Override public void onBindViewHolder(ItemHolder holder, int position) { // 把 position 位置处的数据附加到新的视图 holder.setTitle("Item #" + (position + 1)); holder.setSummary(mItems.get(position)); } @Override public int getItemCount() { return mItems.size(); } public OnItemClickListener getOnItemClickListener() { return mOnItemClickListener; } public void setOnItemClickListener(OnItemClickListener listener) { mOnItemClickListener = listener; } /* 管理数据集修改的方法 */ public void insertItemAtIndex(String item, int position) { mItems.add(position, item); // 通知视图触发变化动画 notifyItemInserted(position); } public void removeItemAtIndex(int position) { if (position >= mItems.size()) return; mItems.remove(position); // 通知视图触发变化动画 notifyItemRemoved(position); } /** * 用作与子项关联的元数据(例如当前位置和稳定的 ID)的存储位置, * 具体的实现通常还提供对视图内部字段的直接访问,从而尽量减少对 findViewById() 的重复调用 */ public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ private SimpleItemAdapter mParent; private TextView mTitleView, mSummaryView; public ItemHolder(View itemView, SimpleItemAdapter parent) { super(itemView); itemView.setOnClickListener(this); mParent = parent; mTitleView = (TextView)itemView.findViewById(R.id.text_title); mSummaryView = (TextView)itemView.findViewById(R.id.text_summary); } public void setTitle(CharSequence title) { mTitleView.setText(title); } public void setSummary(CharSequence summary) { mSummaryView.setText(summary); } public CharSequence getSummary() { return mSummaryView.getText(); } @Override public void onClick(View v) { // ViewHolder 处理子视图上的单击事件,并将其发送回在适配器上定义的公共监听接口 // 这样 ViewHolder 就可以在最后的监听器回调中添加位置数据 final OnItemClickListener listener = mParent.getOnItemClickListener(); if (listener != null) { listener.onItemClick(this, getPosition()); } } } }
交错网格,继承 SpanSizeLookup:
GridStaggerLookup.java :
package com.crazy.simplerecyclerviewof; import android.support.v7.widget.GridLayoutManager; /** * 该类用于生成交错效果,要么 1 行 2 列,要么 1 行 1 列 */ public class GridStaggerLookup extends GridLayoutManager.SpanSizeLookup { @Override public int getSpanSize(int position) { // 每隔 3 个位置占据 2 列,其他位置则占 1 列 int pos = position % 3 == 0 ? 2 : 1; return pos; } }
设置页边距:
InsetDecoration.java :
package com.crazy.simplerecyclerviewof; import android.content.Context; import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.view.View; /** * 为每个子项提供页边距 */ public class InsetDecoration extends RecyclerView.ItemDecoration { private int mInsetMargin; public InsetDecoration (Context context) { super(); mInsetMargin = context.getResources().getDimensionPixelOffset(R.dimen.inset_margin); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); // 对子视图的所有 4 个边界应用计算得出的页边距 outRect.set(mInsetMargin, mInsetMargin, mInsetMargin, mInsetMargin); } }
连接线 ItemDecoration :
ConnectorDecoration.java :
package com.crazy.simplerecyclerviewof; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ItemDecoration; import android.view.View; /** * 在主要和次要网格项之间绘制连接线 */ public class ConnectorDecoration extends ItemDecoration { private Paint mLinePaint; private int mLineLength; public ConnectorDecoration(Context context) { super(); mLineLength =context.getResources() .getDimensionPixelOffset(R.dimen.inset_margin); int connectorStroke = context.getResources() .getDimensionPixelOffset(R.dimen.connector_stroke); // 允许锯齿 mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLinePaint.setColor(Color.RED); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setStrokeWidth(connectorStroke); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { final RecyclerView.LayoutManager manager = parent.getLayoutManager(); // 遍历子视图,并在未占据两列的每个子项上绘制中心线 for (int i = 0; i < parent.getChildCount(); i++) { final View child = parent.getChildAt(i); boolean isLarge = parent .getChildViewHolder(child) .getPosition() % 3 == 0; // 得到视图当前所在的位置,以此绘制线段 if (!isLarge) { final int childLeft = manager.getDecoratedLeft(child); final int childRight = manager.getDecoratedRight(child); final int childTop = manager.getDecoratedTop(child); final int x = childLeft + ((childRight - childLeft) / 2); c.drawLine(x, childTop - mLineLength, x, childTop + mLineLength, mLinePaint); } } } }
collection_item.xml :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="8dp" android:background="#CCF" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/text_title" android:textAppearance="?android:textAppearanceLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/text_summary" android:textAppearance="?android:textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
dimens.xml :
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="fab_margin">16dp</dimen> <dimen name="inset_margin">8dp</dimen> <dimen name="connector_stroke">2dp</dimen> </resources>
layout_options.xml :
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context="com.crazy.extendibleview.MainActivity"> <!--app:showAsAction="ifRoom" 如果有位置才显示,不然就出现在右边的三个点中--> <item android:id="@+id/action_add_item" android:title="Add Item" android:icon="@android:drawable/ic_menu_add" app:showAsAction="ifRoom" /> <item android:id="@+id/action_remove_item" android:title="Remove Item" android:icon="@android:drawable/ic_menu_delete" android:showAsAction="ifRoom" /> <!--app:showAsAction="ifRoom" 不显示在界面上,只让出现在右边的三个点中--> <item android:id="@+id/action_grid_vertical" android:title="Vertical Grid" android:showAsAction="never" /> </menu>
相关文章推荐
- HDU 1207 汉诺塔II DP
- 怎样学习 C++ STL?
- React的Transaction浅析
- 20135210程涵——信息安全系统设计基础第十三周学习总结
- WebSocket的过程与理解
- Java String StringBuffer StringBuilder
- 在win10+Linux Ubuntu双系统下安装win xp三系统的痛苦经历
- 【Unix/Linux编程实践】文件系统:编写pwd
- hdu 5592 线段树 + 二分
- LeetCode 231判断2的幂
- KMP算法(下篇)
- 【郝斌数据结构自学笔记】53-56_一个函数为什么可以自己调用自己_递归必须满足三个条件_循环和递归的比较
- 一个Demo理解工厂设计模式
- js拖拽
- 轻量级mvvm Web开发框架 postby:http://zhutty.cnblogs.com
- HTML5 Canvas入门
- 【数据拾遗(java描述)】--- 哈夫曼树的基本实现
- html5 - 基础格式认识和标签用法(文本元素常用方法)
- 通过转移表练习函数指针的使用
- 睡前小dp-hdu3853-概率dp