前言


  

通过上两篇关于自定view的文章,在自定义view基础上,今天给大家带来怎么代码自定义九宫格子,并且显示支付宝一样的九宫格效果:

导读:

AndroidUI之绘图机制和原理

AndroidUI之View的加载机制(二)。

目标效果

自定义GridView

继承ViewGroup ,我们复写测量,绘制,布局方法,并且将此类定义为抽象类,用于绘制基础的宫格视图。

onMeasure



  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  4.  
  5. // 获取给定尺寸
  6. int width = MeasureSpec.getSize(widthMeasureSpec);
  7. int nChildCount = getIconViewCount();
  8.  
  9. // 计算总行数和总列数
  10. mColCount = getColCount();
  11. int row = nChildCount / mColCount;
  12. int col = nChildCount % mColCount;
  13. mRowCount = ((col == 0) ? row : row + 1);
  14.  
  15. // 计算单元格尺寸
  16. int dividerW = getDividerWidth() * (mColCount + 1);
  17. mCellWidth = 1.0f * (width - getPaddingLeft() - getPaddingRight() - dividerW) / mColCount;
  18.  
  19. // 遍历子view的尺寸设置
  20. for (int i = 0; i < nChildCount; i++) {
  21. View child = getIconView(i);
  22. child.measure((int) mCellWidth, (int) mCellHeight);
  23. }
  24.  
  25. // slot
  26. int slotHeightMeasureSpec =
  27. getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
  28. mSlotView.measure(widthMeasureSpec, slotHeightMeasureSpec);
  29. int slotRow = getSlotRow();
  30. mSlotOffsetY = (int) (slotRow * mCellHeight + (slotRow + 1) * getDividerWidth());
  31.  
  32. // 计算总尺寸
  33. int nViewHeight = Math.round(mRowCount * mCellHeight + (mRowCount + 1) * getDividerWidth());
  34. nViewHeight = nViewHeight + mSlotView.getMeasuredHeight();
  35. if (slotRow < mRowCount && mSlotView.getMeasuredHeight() > 0) {
  36. nViewHeight = nViewHeight + getDividerWidth();
  37. }
  38. nViewHeight = nViewHeight + getPaddingTop() + getPaddingBottom();
  39.  
  40. // 设置总尺寸
  41. setMeasuredDimension(width, nViewHeight);
  42.  
  43. // 分割线初始化
  44. initDividerData();
  45. }

onLayout



  1. @Override
  2. protected void onLayout(boolean change, int l, int t, int r, int b) {
  3. // banner视图
  4. mSlotView.layout(0, mSlotOffsetY,
  5. mSlotView.getMeasuredWidth(), mSlotOffsetY + mSlotView.getMeasuredHeight());
  6.  
  7. // 单元视图
  8. int count = getIconViewCount();
  9. for (int i = 0; i < count; i++) {
  10. View childView = getIconView(i);
  11. BdAnimInfo animInfo = getViewHolder(childView).getAnimInfo();
  12. layoutItem(childView, i, animInfo.isMove());
  13. }
  14. }

onDraw

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4.  
  5. // 分割线
  6. if (mIsDividerEnable) {
  7. drawDivider(canvas);
  8. }
  9. }

完成了基本的测量和layout,和整体item绘制后,接着继续画分割线:

  1. /**
  2. * 绘制分割线
  3. *
  4. * @param aCanvas 画布
  5. * @param aPaint 画笔
  6. * @param aLineOffset 偏移
  7. * @param aLineWidth 线宽度
  8. */
  9. public void drawCrossLine(Canvas aCanvas, Paint aPaint, int aLineOffset, int aLineWidth) {
  10. // 变量
  11. int max;
  12. int offset;
  13.  
  14. int lastCol = getIconViewCount() % mColCount;
  15. int bannerRow = getSlotRow();
  16. Drawable divider = getDivider();
  17.  
  18. // Horizontal line
  19. max = mDividerRow;
  20.  
  21. int left = getPaddingLeft();
  22. int right = getMeasuredWidth() - getPaddingRight();
  23. offset = getPaddingTop() + aLineOffset;
  24.  
  25. for (int i = 0; i < max; i++) {
  26. if (i == max - 1) { // 最后一行停留在某个格子右边
  27. // 右边终点重新确定
  28. if (lastCol > 0) {
  29. right = (int) (getPaddingLeft() + lastCol * mCellWidth + (lastCol + 1) * getDividerWidth());
  30. }
  31. } else if (i == bannerRow && mSlotView.getMeasuredHeight() > 0) { // banner特殊处理
  32. divider.setBounds(left, offset, right, offset + aLineWidth);
  33. divider.draw(aCanvas);
  34.  
  35. offset += getDividerWidth() + mSlotView.getMeasuredHeight();
  36. }
  37. // 绘制
  38. divider.setBounds(left, offset, right, offset + aLineWidth);
  39. divider.draw(aCanvas);
  40.  
  41. offset += mCellHeight + getDividerWidth();
  42. }
  43.  
  44. // Vertical line
  45. max = mDividerCol;
  46.  
  47. int top = getPaddingTop();
  48. int bottom = 0;
  49.  
  50. for (int i = 0; i < max; i++) {
  51. offset = (int) (getPaddingLeft() + aLineOffset + i * (mCellWidth + getDividerWidth()));
  52. if (i > lastCol && lastCol > 0) {
  53. bottom = (int) (getPaddingTop() + mCellHeight * (mRowCount - 1) + getDividerWidth() * mRowCount);
  54. } else {
  55. bottom = (int) (getPaddingTop() + mCellHeight * mRowCount + getDividerWidth() * (mRowCount + 1));
  56. }
  57. if (bottom >= mSlotOffsetY && mSlotView.getMeasuredHeight() > 0) {
  58. int midBottom = mSlotOffsetY - getDividerWidth();
  59. int midTop = mSlotOffsetY + mSlotView.getMeasuredHeight();
  60. bottom += mSlotView.getMeasuredHeight();
  61.  
  62. divider.setBounds(offset, top, offset + aLineWidth, midBottom);
  63. divider.draw(aCanvas);
  64.  
  65. divider.setBounds(offset, midTop, offset + aLineWidth, bottom);
  66. divider.draw(aCanvas);
  67. } else {
  68. divider.setBounds(offset, top, offset + aLineWidth, bottom);
  69. divider.draw(aCanvas);
  70. }
  71. }
  72. }

以上便是最核心的三个方法了,其他代码可以下面的完整代码,

  1. * 宫格视图抽象类 其子类为baseGridview
  2. * 提供最基本的绘制和属性初始化工作,添加删除工作以及条目
  3. * @author LIUYONGKUI
  4. */
  5. public abstract class PaAbsGridView extends ViewGroup {
  6.  
  7. /**
  8. * 视图holder的标志,用于标记View中的tag
  9. */
  10. public static final int VIEW_HOLDER_TAG = 0x0fffff00;
  11.  
  12. /**
  13. * 默认列数
  14. */
  15. public static final int DEF_COLCOUNT = 3;
  16.  
  17. /**
  18. * 行数
  19. */
  20. private int mRowCount;
  21. /**
  22. * 列数
  23. */
  24. private int mColCount;
  25. /**
  26. * 宫格宽度
  27. */
  28. private float mCellWidth;
  29. /**
  30. * 宫格高度
  31. */
  32. private int mCellHeight;
  33. /**
  34. * 临时区域
  35. */
  36. private Rect mAssistRect = new Rect();
  37. /**
  38. * 插槽
  39. */
  40. private FrameLayout mSlotView;
  41. /**
  42. * 插槽的偏移量: y
  43. */
  44. private int mSlotOffsetY;
  45. /**
  46. * 插槽的偏移行
  47. */
  48. private int mSlotOffsetRow;
  49. /**
  50. * 分割线是否显示
  51. */
  52. private boolean mIsDividerEnable;
  53. /**
  54. * 分割线
  55. */
  56. private int mDividerWidth;
  57. /**
  58. * 交叉线行数
  59. */
  60. private int mDividerRow;
  61. /**
  62. * 交叉线列数
  63. */
  64. private int mDividerCol;
  65. /**
  66. * 分割线图片(白天)
  67. */
  68. private Drawable mDividerDay;
  69. /**
  70. * 分割线图片(夜晚)
  71. */
  72. private Drawable mDividerNight;
  73. /**
  74. * 数据适配器
  75. */
  76. private BaseAdapter mAdapter;
  77.  
  78. /**
  79. * 构造函数
  80. *
  81. * @param context 上下文
  82. * @param aAdapter adapter
  83. * @param aColCount 列数
  84. */
  85. public PaAbsGridView(Context context, BaseAdapter aAdapter, int aColCount) {
  86. super(context);
  87.  
  88. // 基本属性
  89. this.setWillNotDraw(false);
  90. mColCount = aColCount;
  91. mAdapter = aAdapter;
  92. mAdapter.registerDataSetObserver(new BdCellDataObserver());
  93.  
  94. // 视图属性
  95. mCellHeight = getCellHeight();
  96. mDividerWidth = getDividerWidthDef();
  97. mDividerDay = getDividerDay();
  98. mDividerNight = getDividerDay();
  99.  
  100. // 刷新视图
  101. refreshViews();
  102. }
  103.  
  104. /**
  105. * 初始化视图
  106. */
  107. public void refreshViews() {
  108. // 初始化banner插槽
  109. if (mSlotView == null) {
  110. mSlotView = new FrameLayout(getContext());
  111. mSlotOffsetRow = 3;
  112. }
  113.  
  114. final List<View> cacheList = new ArrayList<View>();
  115. for (int i = 0; i < getIconViewCount(); i++) {
  116. cacheList.add(getIconView(i));
  117. }
  118.  
  119. removeAllIconViews();
  120.  
  121. // 添加单元项
  122. for (int i = 0; i < mAdapter.getCount(); i++) {
  123. addItemView(queryCacheItemView(cacheList, i), false);
  124. }
  125.  
  126. cacheList.clear();
  127. }
  128.  
  129. /**
  130. * getDrviderDay
  131. * @return
  132. */
  133. protected Drawable getDividerDay() {
  134. return getResources().getDrawable(R.drawable.home_divider_day);
  135. }
  136.  
  137. /**
  138. * getDrivderNight
  139. * @return
  140. */
  141. protected Drawable getDividerNight() {
  142. return getResources().getDrawable(R.drawable.home_divider_night);
  143. }
  144.  
  145. /**
  146. * @return 适配器
  147. */
  148. public BaseAdapter getAdapter() {
  149. return mAdapter;
  150. }
  151.  
  152. /**
  153. * @param aIsEnable 分割线是否有效
  154. */
  155. public void setIsDividerEnable(boolean aIsEnable) {
  156. mIsDividerEnable = aIsEnable;
  157. }
  158.  
  159. /**
  160. * 获取缓存的单元视图
  161. *
  162. * @param aCacheList 缓存列表
  163. * @param aDataIndex 数据索引
  164. * @return 单元视图
  165. */
  166. private View queryCacheItemView(List<View> aCacheList, int aDataIndex) {
  167. final int count = ((aCacheList != null) ? aCacheList.size() : 0);
  168.  
  169. View cacheView = null;
  170.  
  171. // 寻找数据项匹配的单元视图
  172. Object item = mAdapter.getItem(aDataIndex);
  173. if (item != null) {
  174. for (int i = 0; i < count; i++) {
  175. View itemView = aCacheList.get(i);
  176. Object itemData = getViewHolder(itemView).getData();
  177. if ((itemData != null) && (itemData.equals(item))) {
  178. aCacheList.remove(i);
  179. cacheView = itemView;
  180. break;
  181. }
  182. }
  183. }
  184.  
  185. // 寻找类型匹配的单元视图
  186. View targetView = mAdapter.getView(aDataIndex, cacheView, null);
  187. getViewHolder(targetView).setData(item);
  188. return targetView;
  189. }
  190.  
  191. /**
  192. * 往插槽里填充视图
  193. *
  194. * @param aView 视图
  195. */
  196. public void addToSlot(View aView) {
  197. if ((aView != null) && (mSlotView.indexOfChild(aView) < 0)) {
  198. mSlotView.addView(aView,
  199. new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
  200. }
  201. }
  202.  
  203. /**
  204. * 添加单元格视图
  205. *
  206. * @param aIconView 图标单元格视图
  207. */
  208. public void addIconView(View aIconView) {
  209. addView(aIconView);
  210. }
  211.  
  212. /**
  213. * 添加单元格视图
  214. *
  215. * @param aIconView 单元格视图
  216. * @param aIconIndex 位置
  217. */
  218. public void addIconView(View aIconView, int aIconIndex) {
  219. addView(aIconView, aIconIndex + 1);
  220. }
  221.  
  222. /**
  223. * 移除单元格视图
  224. *
  225. * @param aIconView 单元格视图
  226. */
  227. public void removeIconView(View aIconView) {
  228. removeView(aIconView);
  229. }
  230.  
  231. /**
  232. * @return 单元格视图的总个数
  233. */
  234. public int getIconViewCount() {
  235. return getChildCount() - 1;
  236. }
  237.  
  238. /**
  239. * @param aIndex 索引
  240. * @return 单元格视图
  241. */
  242. public View getIconView(int aIndex) {
  243. return getChildAt(aIndex + 1);
  244. }
  245.  
  246. /**
  247. * 移除所有的单元格视图
  248. */
  249. public void removeAllIconViews() {
  250. removeAllViews();
  251. addView(mSlotView);
  252. }
  253.  
  254. /**
  255. * 释放所有视图资源
  256. */
  257. public void releaseAllViews() {
  258. removeAllViews();
  259. }
  260.  
  261. @Override
  262. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  263. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  264.  
  265. // 获取给定尺寸
  266. int width = MeasureSpec.getSize(widthMeasureSpec);
  267. int nChildCount = getIconViewCount();
  268.  
  269. // 计算总行数和总列数
  270. mColCount = getColCount();
  271. int row = nChildCount / mColCount;
  272. int col = nChildCount % mColCount;
  273. mRowCount = ((col == 0) ? row : row + 1);
  274.  
  275. // 计算单元格尺寸
  276. int dividerW = getDividerWidth() * (mColCount + 1);
  277. mCellWidth = 1.0f * (width - getPaddingLeft() - getPaddingRight() - dividerW) / mColCount;
  278.  
  279. // 遍历子view的尺寸设置
  280. for (int i = 0; i < nChildCount; i++) {
  281. View child = getIconView(i);
  282. child.measure((int) mCellWidth, (int) mCellHeight);
  283. }
  284.  
  285. // slot
  286. int slotHeightMeasureSpec =
  287. getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
  288. mSlotView.measure(widthMeasureSpec, slotHeightMeasureSpec);
  289. int slotRow = getSlotRow();
  290. mSlotOffsetY = (int) (slotRow * mCellHeight + (slotRow + 1) * getDividerWidth());
  291.  
  292. // 计算总尺寸
  293. int nViewHeight = Math.round(mRowCount * mCellHeight + (mRowCount + 1) * getDividerWidth());
  294. nViewHeight = nViewHeight + mSlotView.getMeasuredHeight();
  295. if (slotRow < mRowCount && mSlotView.getMeasuredHeight() > 0) {
  296. nViewHeight = nViewHeight + getDividerWidth();
  297. }
  298. nViewHeight = nViewHeight + getPaddingTop() + getPaddingBottom();
  299.  
  300. // 设置总尺寸
  301. setMeasuredDimension(width, nViewHeight);
  302.  
  303. // 分割线初始化
  304. initDividerData();
  305. }
  306.  
  307. @Override
  308. protected void onLayout(boolean change, int l, int t, int r, int b) {
  309. // banner视图
  310. mSlotView.layout(0, mSlotOffsetY,
  311. mSlotView.getMeasuredWidth(), mSlotOffsetY + mSlotView.getMeasuredHeight());
  312.  
  313. // 单元视图
  314. int count = getIconViewCount();
  315. for (int i = 0; i < count; i++) {
  316. View childView = getIconView(i);
  317. BdAnimInfo animInfo = getViewHolder(childView).getAnimInfo();
  318. layoutItem(childView, i, animInfo.isMove());
  319. }
  320. }
  321.  
  322. @Override
  323. protected void onDraw(Canvas canvas) {
  324. super.onDraw(canvas);
  325.  
  326. // 分割线
  327. if (mIsDividerEnable) {
  328. drawDivider(canvas);
  329. }
  330. }
  331.  
  332. /**
  333. * 初始化分割线数据
  334. */
  335. public void initDividerData() {
  336. // 计算绘制行数
  337. int row = getIconViewCount() / mColCount;
  338. int col = getIconViewCount() % mColCount;
  339. mDividerRow = ((col == 0) ? row + 1 : row + 2);
  340.  
  341. // 计算绘制列
  342. mDividerCol = mColCount + 1;
  343. }
  344.  
  345. /**
  346. * 绘制分割线
  347. *
  348. * @param aCanvas 画布
  349. */
  350. public void drawDivider(Canvas aCanvas) {
  351. // 绘制交叉线
  352. drawCrossLine(aCanvas, null, 0, getDividerWidth());
  353. }
  354.  
  355. /**
  356. * @return 分割线图片
  357. */
  358. public Drawable getDivider() {
  359. /* if (BdThemeManager.getInstance().isNightT5()) {
  360. return mDividerNight;
  361. } else {
  362. return mDividerDay;
  363. }*/
  364.  
  365. return mDividerDay;
  366. }
  367.  
  368. /**
  369. * 绘制分割线
  370. *
  371. * @param aCanvas 画布
  372. * @param aPaint 画笔
  373. * @param aLineOffset 偏移
  374. * @param aLineWidth 线宽度
  375. */
  376. public void drawCrossLine(Canvas aCanvas, Paint aPaint, int aLineOffset, int aLineWidth) {
  377. // 变量
  378. int max;
  379. int offset;
  380.  
  381. int lastCol = getIconViewCount() % mColCount;
  382. int bannerRow = getSlotRow();
  383. Drawable divider = getDivider();
  384.  
  385. // Horizontal line
  386. max = mDividerRow;
  387.  
  388. int left = getPaddingLeft();
  389. int right = getMeasuredWidth() - getPaddingRight();
  390. offset = getPaddingTop() + aLineOffset;
  391.  
  392. for (int i = 0; i < max; i++) {
  393. if (i == max - 1) { // 最后一行停留在某个格子右边
  394. // 右边终点重新确定
  395. if (lastCol > 0) {
  396. right = (int) (getPaddingLeft() + lastCol * mCellWidth + (lastCol + 1) * getDividerWidth());
  397. }
  398. } else if (i == bannerRow && mSlotView.getMeasuredHeight() > 0) { // banner特殊处理
  399. divider.setBounds(left, offset, right, offset + aLineWidth);
  400. divider.draw(aCanvas);
  401.  
  402. offset += getDividerWidth() + mSlotView.getMeasuredHeight();
  403. }
  404. // 绘制
  405. divider.setBounds(left, offset, right, offset + aLineWidth);
  406. divider.draw(aCanvas);
  407.  
  408. offset += mCellHeight + getDividerWidth();
  409. }
  410.  
  411. // Vertical line
  412. max = mDividerCol;
  413.  
  414. int top = getPaddingTop();
  415. int bottom = 0;
  416.  
  417. for (int i = 0; i < max; i++) {
  418. offset = (int) (getPaddingLeft() + aLineOffset + i * (mCellWidth + getDividerWidth()));
  419. if (i > lastCol && lastCol > 0) {
  420. bottom = (int) (getPaddingTop() + mCellHeight * (mRowCount - 1) + getDividerWidth() * mRowCount);
  421. } else {
  422. bottom = (int) (getPaddingTop() + mCellHeight * mRowCount + getDividerWidth() * (mRowCount + 1));
  423. }
  424. if (bottom >= mSlotOffsetY && mSlotView.getMeasuredHeight() > 0) {
  425. int midBottom = mSlotOffsetY - getDividerWidth();
  426. int midTop = mSlotOffsetY + mSlotView.getMeasuredHeight();
  427. bottom += mSlotView.getMeasuredHeight();
  428.  
  429. divider.setBounds(offset, top, offset + aLineWidth, midBottom);
  430. divider.draw(aCanvas);
  431.  
  432. divider.setBounds(offset, midTop, offset + aLineWidth, bottom);
  433. divider.draw(aCanvas);
  434. } else {
  435. divider.setBounds(offset, top, offset + aLineWidth, bottom);
  436. divider.draw(aCanvas);
  437. }
  438. }
  439. }
  440.  
  441. /**
  442. * 增加数据项对应的子项
  443. *
  444. * @param aItemView item view
  445. * @param isAfterInit init
  446. */
  447. private void addItemView(View aItemView, boolean isAfterInit) {
  448. if (isAfterInit) {
  449. addIconView(aItemView, getIconViewCount() - 1);
  450. } else {
  451. addIconView(aItemView);
  452. }
  453. }
  454.  
  455. /**
  456. * 布局子项;可重载,允许子类有实现其它功能的机会
  457. *
  458. * @param aChild 子项
  459. * @param aPos 位置
  460. * @param aIsAnim 是否做动画
  461. */
  462. public void layoutItem(View aChild, int aPos, boolean aIsAnim) {
  463. // 获取视图的动画信息
  464. BdViewHolder holder = getViewHolder(aChild);
  465. BdAnimInfo animInfo = holder.getAnimInfo();
  466.  
  467. // 获取相应位置的矩形区域
  468. final Rect r = mAssistRect;
  469. getChildArea(r, aChild, aPos);
  470.  
  471. // 如果动画进行中,重新定义动画属性; 否则直接布局
  472. if (aIsAnim) {
  473. animInfo.beginMove(aChild.getLeft(), aChild.getTop(), r.left, r.top);
  474. } else {
  475. aChild.layout(r.left, r.top, r.right, r.bottom);
  476. }
  477.  
  478. // 重新定义视图位置
  479. holder.setPosition(aPos);
  480. }
  481.  
  482. /**
  483. * 更新动画中子项的状态
  484. */
  485. public void updateAnimInfo(float aFactor) {
  486. // 更新移动项
  487. int count = getIconViewCount();
  488. for (int i = 0; i < count; i++) {
  489. View itemView = getIconView(i);
  490. BdAnimInfo animInfo = getViewHolder(itemView).getAnimInfo();
  491. if (animInfo.isMove()) {
  492. animInfo.move(aFactor);
  493. }
  494. }
  495. }
  496.  
  497. /**
  498. * 更新动画中子项的布局
  499. */
  500. public void updateAnimLayout(float aFactor) {
  501. // 重新布局
  502. int count = getIconViewCount();
  503. for (int i = 0; i < count; i++) {
  504. View itemView = getIconView(i);
  505. BdAnimInfo animInfo = getViewHolder(itemView).getAnimInfo();
  506. if (animInfo.isMove()) {
  507. itemView.layout(animInfo.getX(), animInfo.getY(),
  508. animInfo.getX() + itemView.getMeasuredWidth(),
  509. animInfo.getY() + itemView.getMeasuredHeight());
  510.  
  511. // 结束标记
  512. if (aFactor == 1.0f) {
  513. animInfo.endMove();
  514. }
  515. }
  516. }
  517. }
  518.  
  519. /**
  520. * 获取给定位置上的子项区域
  521. *
  522. * @param outRect 存储区域
  523. * @param child 子项
  524. * @param pos 子项位置
  525. */
  526. public void getChildArea(Rect outRect, View child, int pos) {
  527. getCellArea(outRect, pos);
  528.  
  529. int offsetX = (int) (outRect.left + (mCellWidth - child.getMeasuredWidth()) / 2);
  530. int offsetY = (int) (outRect.top + (mCellHeight - child.getMeasuredHeight()) / 2);
  531.  
  532. outRect.set(offsetX, offsetY, offsetX + child.getMeasuredWidth(), offsetY + child.getMeasuredHeight());
  533. }
  534.  
  535. /**
  536. * 获取单元格的区域
  537. *
  538. * @param outRect 所在区域
  539. * @param pos 单元格位置
  540. */
  541. public void getCellArea(Rect outRect, int pos) {
  542. int row = pos / mColCount;
  543. int col = pos % mColCount;
  544.  
  545. int bannerRow = (mRowCount > mSlotOffsetRow ? mSlotOffsetRow : mRowCount);
  546. int startX = (int) (getPaddingLeft() + (col + 1) * getDividerWidth() + col * mCellWidth);
  547. int startY = (int) (getPaddingTop() + (row + 1) * getDividerWidth() + row * mCellHeight);
  548. // 如果banner在宫格的上方存在,那么要多加一条分割线
  549. if ((row >= bannerRow) && mSlotView.getMeasuredHeight() > 0) {
  550. startY = startY + mSlotView.getMeasuredHeight() + getDividerWidth();
  551. }
  552. outRect.set(startX, startY, (int) (startX + mCellWidth), (int) (startY + mCellHeight));
  553. }
  554.  
  555. /**
  556. * @return banner所在行
  557. */
  558. public int getSlotRow() {
  559. return (mRowCount > mSlotOffsetRow ? mSlotOffsetRow : mRowCount);
  560. }
  561.  
  562. /**
  563. * 快速定位位置
  564. *
  565. * @param x 当前坐标x
  566. * @param y 当前坐标y
  567. * @return 命中的单元格索引
  568. */
  569. public int getCellPosition(int x, int y) {
  570. // 先去除banner的偏移
  571. if (y > mSlotOffsetY) {
  572. y -= mSlotView.getMeasuredHeight();
  573. }
  574.  
  575. int row = (int) ((y - getPaddingTop()) / mCellHeight);
  576. int col = (int) ((x - getPaddingLeft()) / mCellWidth);
  577. return row * mColCount + col;
  578. }
  579.  
  580. /**
  581. * @return 单元格宽度
  582. */
  583. public int getCellWidth() {
  584. return (int) mCellWidth;
  585. }
  586.  
  587. /**
  588. * @return 单元格高度
  589. */
  590. public int getCellHeight() {
  591.  
  592. return mCellHeight == -1 ? getResources().getDimensionPixelSize(R.dimen.home_item_height): mCellHeight;
  593. }
  594.  
  595. /**
  596. * @return 分割线宽度
  597. */
  598. public int getDividerWidth() {
  599. return (mIsDividerEnable ? mDividerWidth : 0);
  600. }
  601.  
  602. /**
  603. * @return 默认分割线宽度
  604. */
  605. public int getDividerWidthDef() {
  606. return mDividerWidth == -1 ? getResources().getDimensionPixelOffset(R.dimen.home_divider_width): mDividerWidth;
  607. }
  608.  
  609. /**
  610. * @return 总行数
  611. */
  612. public int getRowCount() {
  613. return mRowCount;
  614. }
  615.  
  616. /**
  617. * @return 总列数
  618. */
  619. public int getColCount() {
  620. return mColCount == -1 ? DEF_COLCOUNT : mColCount;
  621. }
  622.  
  623. /**
  624. * 改变子项的位置
  625. *
  626. * @param aFromPosition 原始位置
  627. * @param aToPosition 终点位置
  628. * @param isAnimation 是否附带动画效果
  629. */
  630. public void changeItemPosition(int aFromPosition, int aToPosition, boolean isAnimation) {
  631. int nChildCount = getIconViewCount();
  632.  
  633. // 边界判断
  634. if ((aFromPosition < 0) || (aFromPosition >= nChildCount) || (aToPosition < 0)
  635. || (aToPosition >= nChildCount)) {
  636. return;
  637. }
  638.  
  639. // 寻找子项
  640. View fromView = null;
  641. View toView = null;
  642. for (int i = 0; i < nChildCount; i++) {
  643. View childView = getIconView(i);
  644. if (getViewPosition(childView) == aFromPosition) {
  645. fromView = childView;
  646. }
  647. if (getViewPosition(childView) == aToPosition) {
  648. toView = childView;
  649. }
  650. }
  651.  
  652. // 设置新的项
  653. if (fromView == toView) {
  654. layoutItem(fromView, aToPosition, isAnimation);
  655. } else {
  656. layoutItem(fromView, aToPosition, isAnimation);
  657. layoutItem(toView, aFromPosition, isAnimation);
  658. }
  659. }
  660.  
  661. /**
  662. * @param aView 视图
  663. * @return 视图位置
  664. */
  665. public int getViewPosition(View aView) {
  666. return getViewHolder(aView).getPosition();
  667. }
  668.  
  669. /**
  670. * @param aView 视图
  671. * @param aPos 视图位置
  672. */
  673. public void setViewPosition(View aView, int aPos) {
  674. getViewHolder(aView).setPosition(aPos);
  675. }
  676.  
  677. /**
  678. * @param aView 视图
  679. * @return holder
  680. */
  681. private BdViewHolder getViewHolder(View aView) {
  682. BdViewHolder holder = (BdViewHolder) aView.getTag(VIEW_HOLDER_TAG);
  683. if (holder == null) {
  684. holder = new BdViewHolder();
  685. aView.setTag(VIEW_HOLDER_TAG, holder);
  686. }
  687. return holder;
  688. }
  689.  
  690. /**
  691. * 数据更新类
  692. */
  693. class BdCellDataObserver extends DataSetObserver {
  694. @Override
  695. public void onChanged() {
  696. super.onChanged();
  697.  
  698. // 刷新视图
  699. refreshViews();
  700. }
  701. }
  702.  
  703. /**
  704. * 视图holder
  705. */
  706. class BdViewHolder {
  707. /**
  708. * 动画信息
  709. */
  710. BdAnimInfo mAnimInfo;
  711. /**
  712. * 视图数据
  713. */
  714. Object mViewData;
  715. /**
  716. * 视图位置
  717. */
  718. int mViewPosition;
  719.  
  720. /**
  721. * 构造函数
  722. */
  723. public BdViewHolder() {
  724. mAnimInfo = new BdAnimInfo();
  725. }
  726.  
  727. /**
  728. * @return 动画信息
  729. */
  730. public BdAnimInfo getAnimInfo() {
  731. return mAnimInfo;
  732. }
  733.  
  734. /**
  735. * @param aData 数据
  736. */
  737. public void setData(Object aData) {
  738. mViewData = aData;
  739. }
  740.  
  741. /**
  742. * @return 数据
  743. */
  744. public Object getData() {
  745. return mViewData;
  746. }
  747.  
  748. /**
  749. * @param aPosition 位置
  750. */
  751. public void setPosition(int aPosition) {
  752. mViewPosition = aPosition;
  753. }
  754.  
  755. /**
  756. * @return 位置
  757. */
  758. public int getPosition() {
  759. return mViewPosition;
  760. }
  761.  
  762. }
  763.  
  764. /**
  765. * 动画信息
  766. */
  767. class BdAnimInfo {
  768.  
  769. /**
  770. * 目标坐标
  771. */
  772. int mStartX;
  773.  
  774. /**
  775. * 目标坐标
  776. */
  777. int mStartY;
  778.  
  779. /**
  780. * 目标坐标
  781. */
  782. int mTargetX;
  783.  
  784. /**
  785. * 目标坐标
  786. */
  787. private int mTargetY;
  788.  
  789. /**
  790. * 目标坐标
  791. */
  792. private int mMoveX;
  793.  
  794. /**
  795. * 目标坐标
  796. */
  797. private int mMoveY;
  798.  
  799. /**
  800. * 目标坐标
  801. */
  802. private boolean mIsMove;
  803.  
  804. /**
  805. * 开始移动
  806. *
  807. * @param aStartX 起始x值
  808. * @param aStartY 起始y值
  809. * @param aTargetX 目标x值
  810. * @param aTargetY 目标y值
  811. */
  812. public void beginMove(int aStartX, int aStartY, int aTargetX, int aTargetY) {
  813. setIsMove(true);
  814.  
  815. mTargetX = aTargetX;
  816. mTargetY = aTargetY;
  817.  
  818. mStartX = aStartX;
  819. mStartY = aStartY;
  820. }
  821.  
  822. /**
  823. * 结束移动
  824. */
  825. public void endMove() {
  826. setIsMove(false);
  827. }
  828.  
  829. /**
  830. * @param aFactor 比例
  831. */
  832. public void move(float aFactor) {
  833. if (isMove()) {
  834. mMoveX = mStartX + (int) ((mTargetX - mStartX) * aFactor);
  835. mMoveY = mStartY + (int) ((mTargetY - mStartY) * aFactor);
  836. }
  837. }
  838.  
  839. /**
  840. * 设置是否在移动
  841. *
  842. * @param isMove 标志
  843. */
  844. public void setIsMove(boolean isMove) {
  845. mIsMove = isMove;
  846. }
  847.  
  848. /**
  849. * 判断是否在移动
  850. *
  851. * @return 判断结果
  852. */
  853. public boolean isMove() {
  854. return mIsMove;
  855. }
  856.  
  857. /**
  858. * @return x
  859. */
  860. public int getX() {
  861. return mMoveX;
  862. }
  863.  
  864. /**
  865. * @return y
  866. */
  867. public int getY() {
  868. return mMoveY;
  869. }
  870.  
  871. }
  872.  
  873. }

BaseGridView

接着我们继续实现定义好的抽象类,完成上面必须实现的俩抽象方法:

此类的作用时将事件绑定到我们的宫格视图上,因此我抽象出了来方法:长按事件和短按事件,便于上层实现

  1. /**
  2. * 基础 baseGridview
  3. * 提供事件绑定操作
  4. * Created by LIUYONGKUI726 on 2016-03-03.
  5. */
  6. public abstract class BaseGridView extends AbsGridView implements OnClickListener, OnLongClickListener{
  7.  
  8. /**
  9. * @param context 上下文
  10. * @param adapter 适配器
  11. */
  12. public BaseGridView(Context context, PaAbsAdapter adapter) {
  13. this(context, adapter, -1);
  14. }
  15.  
  16. /**
  17. * @param context 上下文
  18. * @param adapter 适配器
  19. * @param aCount 列数(默认3)
  20. */
  21. public BaseGridView(Context context, PaAbsAdapter adapter, int aCount) {
  22.  
  23. super(context, adapter, aCount <= 0 ? -1 : aCount);
  24.  
  25. }
  26.  
  27. @Override
  28. public void layoutItem(View aChild, int aPos, boolean aIsAnim) {
  29. super.layoutItem(aChild, aPos, aIsAnim);
  30. aChild.setOnClickListener(this);
  31. aChild.setOnLongClickListener(this);
  32. }
  33.  
  34. @Override
  35. public void onClick(View v) {
  36. onItemClick(v);
  37. }
  38.  
  39. @Override
  40. public boolean onLongClick(View v) {
  41. return onItemLongClick(v);
  42. }
  43.  
  44. /**
  45. * item 短按事件
  46. * @param v
  47. */
  48. public abstract void onItemClick(View v);
  49.  
  50. /**
  51. * item 长按事件
  52. * @param v
  53. */
  54. public abstract boolean onItemLongClick(View v);
  55.  
  56. }

AbsAdapter

接着我们还要写一个抽象泛型适配器,方便上层数据和view的绑定

  1. public abstract class AbsAdapter<T> extends BaseAdapter {
  2. public ArrayList<T> mList = new ArrayList<T>();
  3. public Context mContext;
  4.  
  5. public PaAbsAdapter(Context context, List<T> list) {
  6. mContext = context;
  7. if (list != null) {
  8. mList.addAll(list);
  9. }
  10. }
  11.  
  12. @Override
  13. public int getCount() {
  14. return mList == null ? 0 : mList.size();
  15. }
  16.  
  17. @Override
  18. public Object getItem(int position) {
  19. return mList == null ? null : mList.get(position);
  20. }
  21.  
  22. @Override
  23. public long getItemId(int position) {
  24. return position;
  25. }
  26.  
  27. @Override
  28. public abstract View getView(int position, View convertView, ViewGroup parent);
  29.  
  30. /**
  31. * 更新ListView
  32. *
  33. * @param list
  34. */
  35. public void notifyDataSetChanged(ArrayList<T> list) {
  36. if (mList != list) {
  37. mList.clear();
  38. if (list != null) {
  39. mList.addAll(list);
  40. }
  41. }
  42. if (mContext != null) {
  43. ((Activity)mContext).runOnUiThread(new Runnable() {
  44.  
  45. @Override
  46. public void run() {
  47. notifyDataSetChanged();
  48. }
  49. });
  50. }
  51. }
  52. }

具体使用

PulginGridView



由于我的项目需要用这个做插件功能,而且微信中的钱包其实集成了很多插件,因此我将这个gridview命名为pluginView



  1. **
  2. * Created by liuyongkui on 2016-09-02.
  3. */
  4. public class PluginGridview2 extends BaseGridView {
  5.  
  6. public PluginGridview2(Context context, PaAbsAdapter adapter, int aCount) {
  7. super(context, adapter, aCount);
  8. }
  9.  
  10. public PluginGridview2(Context context, PaAbsAdapter adapter) {
  11. super(context, adapter);
  12. }
  13.  
  14. @Override
  15. public void onItemClick(View v) {
  16.  
  17. }
  18.  
  19. @Override
  20. public boolean onItemLongClick(View v) {
  21. return false;
  22. }
  23. }

PluginGridViewItem

一看标题你既知道这个gridview的item,这里我也没用xml;直接用纯java代码实现,直接继承RelativeLayout,看看了效果图,你就知道里面必定有个imageview和一行文字。

接着代码, 并且做了点击item的背景效果,

  1. public class PluginGridViewItem extends RelativeLayout {
  2.  
  3. /** 完全不透明度 */
  4. private static final int FULL_ALPHA = 255;
  5.  
  6. /** 半不透明度 */
  7. private static final int HALF_ALPHA = 128;
  8.  
  9. /** icon名称文字的大小,单位dp */
  10. private static final int SUG_NAME_TEXT_SIZE = 12;
  11.  
  12. /** ICON 视图的ID */
  13. private static final int SUG_ICON_ID = 0x0101;
  14.  
  15. /** 数据 */
  16. private PluginConfigModle mGuideModle;
  17.  
  18. /** ICON */
  19. private ImageView mSugIcon;
  20.  
  21. /** 名称 */
  22. private TextView mSugName;
  23.  
  24. /** 上下文 */
  25. private Context mContext;
  26.  
  27. /** 图片加载器 */
  28. private Picasso mImageLoader;
  29.  
  30. /** 该视图宽度 */
  31. private int mWidth;
  32.  
  33. /** 该视图高度 */
  34. private int mHeight;
  35.  
  36. /** ICON 的宽高 */
  37. private int mIconWidth;
  38.  
  39. /** SUG 名称视图的高度 */
  40. private int mTextHeight;
  41.  
  42. /** SUG 名称视图的宽度 */
  43. private int mTextWidth;
  44.  
  45. /** 是否按下 */
  46. private boolean mIsPressed;
  47.  
  48. /** 按下时的背景 */
  49. private Drawable mCellPressDrawable;
  50.  
  51. /** 夜间模式下按下时的背景 */
  52. private Drawable mNightCellPressDrawable;
  53.  
  54. /**
  55. * 默认图片,使用static减少对象数
  56. */
  57. private static Bitmap mDefaultIcon;
  58.  
  59. /** 点击事件监听器 */
  60. private OnItemClickListener mItemClickListener;
  61.  
  62. private PluginGridViewItem(Context aContext, AttributeSet aAttrs) {
  63. super(aContext, aAttrs);
  64. }
  65.  
  66. private PluginGridViewItem(Context aContext) {
  67. this(aContext, null);
  68. }
  69.  
  70. public PluginGridViewItem(Context aContext,
  71. PluginConfigModle aGuideModle, Picasso aImageLoader) {
  72. this(aContext);
  73. mGuideModle = aGuideModle;
  74. mContext = aContext;
  75. mImageLoader = aImageLoader;
  76. init();
  77. }
  78.  
  79. public PluginConfigModle getPluginModle() {
  80. return mGuideModle;
  81. }
  82.  
  83. public void setPluginModle(PluginConfigModle mGuideModle) {
  84. this.mGuideModle = mGuideModle;
  85. }
  86.  
  87. /***
  88. * 检查屏幕的方向.
  89. *
  90. * @return false
  91. */
  92. private boolean isLandscape() {
  93. if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
  94. return true;
  95. } else {
  96. return false;
  97. }
  98. }
  99.  
  100. /**
  101. * 计算ICON的宽度,以及该视图的宽高
  102. */
  103. private void initCellWidth() {
  104. int screenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
  105. int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
  106.  
  107. if (screenWidth > screenHeight) {
  108. int temp = screenWidth;
  109. screenWidth = screenHeight;
  110. screenHeight = temp;
  111. }
  112.  
  113. if (isLandscape()) {
  114.  
  115. int m = PluginGridView.mColCount;
  116. int n = m - 1;
  117.  
  118. int padding = (int) (PluginGridView.PADDING_LANDSCAPE * screenHeight / 1280);
  119. int spacing = (int) (PluginGridView.ICON_SPACING_LANDSCAPE
  120. * screenHeight / 1280);
  121. mIconWidth = (screenHeight - n * spacing - 2 * padding) / m ;
  122.  
  123. spacing = (int) ((PluginGridView.ICON_SPACING_LANDSCAPE - PluginGridView.PADDING_LANDSCAPE)
  124. * screenHeight / 1280);
  125. mWidth = (screenHeight - n * spacing - padding) / m;
  126.  
  127. } else {
  128.  
  129. int m = PluginGridView.mColCount;
  130. int n = m - 1;
  131. int padding = (int) (PluginGridView.PADDING_PORTRAIT * screenWidth / 720);
  132. int spacing = (int) (PluginGridView.ICON_SPACING_PORTRAIT
  133. * screenWidth / 720);
  134. mIconWidth = (screenWidth - n * spacing - 2 * padding) / m;
  135.  
  136. padding = (int) (PluginGridView.PADDING_PORTRAIT * screenWidth / 720) / 2;
  137. spacing = 0;
  138. mWidth = (screenWidth - padding * 2) / m;
  139. }
  140. }
  141.  
  142. /**
  143. * 初始化视图
  144. */
  145. private void init() {
  146.  
  147. mCellPressDrawable = getResources().getDrawable(
  148. R.drawable.home_item_bg);
  149.  
  150. mNightCellPressDrawable = getResources().getDrawable(
  151. R.drawable.home_item_night_bg);
  152.  
  153. mSugIcon = new ImageView(mContext);
  154. //mSugIcon.setImageBitmap(mDefaultIcon);
  155. //mSugIcon.setId(SUG_ICON_ID);
  156. mSugIcon.setScaleType(ImageView.ScaleType.FIT_XY);
  157. mSugIcon.setMaxHeight(mWidth);
  158.  
  159. mImageLoader.load(Uri.parse(mGuideModle.getAppIcon())).into(mSugIcon);
  160.  
  161. LayoutParams params = new LayoutParams(
  162. mWidth, mWidth);
  163. params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
  164. params.topMargin = mContext.getResources().getDimensionPixelSize(
  165. R.dimen.sug_item_icon_top_margin);
  166. this.addView(mSugIcon, params);
  167.  
  168. // init the name view
  169. mSugName = new TextView(mContext);
  170. mSugName.setMaxLines(1);
  171. mSugName.setText(mGuideModle.getPluginName());
  172. mSugName.setTextSize(TypedValue.COMPLEX_UNIT_SP, SUG_NAME_TEXT_SIZE);
  173. mSugName.setGravity(Gravity.CENTER);
  174. mSugName.setTextColor(mContext.getResources().getColor(
  175. R.color.sug_item_name_color));
  176. LayoutParams txtParams = new LayoutParams(
  177. LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  178. txtParams.topMargin = mContext.getResources().getDimensionPixelSize(
  179. R.dimen.sug_item_text_top_margin);
  180. txtParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
  181. R.dimen.sug_item_text_bottom_margin);
  182. txtParams.addRule(RelativeLayout.BELOW, SUG_ICON_ID);
  183. txtParams
  184. .addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
  185. mSugName.setLayoutParams(txtParams);
  186. this.addView(mSugName);
  187.  
  188. mTextHeight = (int) (mSugName.getPaint().getFontMetrics().descent - mSugName
  189. .getPaint().getFontMetrics().top);
  190. mTextWidth = (int) mSugName.getPaint().measureText(
  191. mGuideModle.getPluginName());
  192.  
  193. //onThemeChanged(0);
  194. }
  195.  
  196. /**
  197. * 设置点击事件监听器
  198. *
  199. * @param aClickListener
  200. * 点击事件监听器
  201. */
  202. public void setOnItemClickListener(OnItemClickListener aClickListener) {
  203. mItemClickListener = aClickListener;
  204. }
  205.  
  206. @Override
  207. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  208. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  209. initCellWidth();
  210.  
  211. mHeight = mIconWidth
  212. + mContext.getResources().getDimensionPixelSize(
  213. R.dimen.sug_item_icon_top_margin)
  214. + mContext.getResources().getDimensionPixelSize(
  215. R.dimen.sug_item_text_top_margin)
  216. + mTextHeight
  217. + mContext.getResources().getDimensionPixelSize(
  218. R.dimen.sug_item_text_bottom_margin);
  219.  
  220. setMeasuredDimension(mWidth, mHeight);
  221. }
  222.  
  223. @Override
  224. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  225. if (mSugIcon != null) {
  226. mSugIcon.layout(
  227. (mWidth - mIconWidth) / 2,
  228. mContext.getResources().getDimensionPixelSize(
  229. R.dimen.sug_item_icon_top_margin),
  230. (mWidth + mIconWidth) / 2,
  231. mContext.getResources().getDimensionPixelSize(
  232. R.dimen.sug_item_icon_top_margin)
  233. + mIconWidth);
  234. }
  235.  
  236. if (mSugName != null) {
  237. int top = mContext.getResources().getDimensionPixelSize(
  238. R.dimen.sug_item_icon_top_margin)
  239. + mIconWidth
  240. + mContext.getResources().getDimensionPixelSize(
  241. R.dimen.sug_item_text_top_margin);
  242. int bottom = mHeight
  243. - mContext.getResources().getDimensionPixelSize(
  244. R.dimen.sug_item_text_bottom_margin);
  245.  
  246. if (mTextWidth > mWidth) {
  247. mSugName.layout(0, top, mWidth, bottom);
  248. } else {
  249. mSugName.layout((mWidth - mTextWidth) / 2, top,
  250. (mWidth + mTextWidth) / 2, bottom);
  251. }
  252.  
  253. }
  254. }
  255.  
  256. /**
  257. * 处理手势.
  258. *
  259. * @see android.view.View#onTouchEvent(MotionEvent)
  260. */
  261. @Override
  262. public boolean onTouchEvent(MotionEvent event) {
  263. switch (event.getAction()) {
  264. case MotionEvent.ACTION_DOWN:
  265.  
  266. mIsPressed = true;
  267. if (mItemClickListener != null) {
  268. mItemClickListener.onPressDown(PluginGridViewItem.this);
  269. }
  270. break;
  271.  
  272. case MotionEvent.ACTION_UP:
  273. mIsPressed = false;
  274. if (mItemClickListener != null) {
  275. mItemClickListener.onClick(PluginGridViewItem.this,
  276. mGuideModle);
  277. }
  278. break;
  279.  
  280. case MotionEvent.ACTION_CANCEL:
  281. mIsPressed = false;
  282. if (mItemClickListener != null) {
  283. mItemClickListener.onPressUp(PluginGridViewItem.this);
  284. }
  285. break;
  286.  
  287. default:
  288. break;
  289. }
  290.  
  291. return super.onTouchEvent(event);
  292. }
  293.  
  294. /**
  295. * Item点击监听.
  296. */
  297. public interface OnItemClickListener {
  298.  
  299. /**
  300. * 点击.
  301. */
  302. void onClick(PluginGridViewItem v, PluginConfigModle model);
  303.  
  304. /**
  305. * 按下.
  306. */
  307. void onPressDown(PluginGridViewItem v);
  308.  
  309. /**
  310. * 弹起.
  311. */
  312. void onPressUp(PluginGridViewItem v);
  313. }
  314.  
  315. public void setBackground() {
  316. if (mIsPressed) {
  317. setBackgroundDrawable(mCellPressDrawable);
  318. } else {
  319. setBackgroundDrawable(null);
  320. }
  321. }

activity

初始化我们的PluginGridView, ,并将我们的数据数据适配器设置给girdview, 并将data,和item set上

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_main);
  5.  
  6. mContext = MainActivity.this;
  7. mPicasso = Picasso.with(mContext);
  8. init();
  9.  
  10. addContentView(mPluginsView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
  11.  
  12. }
  13.  
  14. private void init() {
  15.  
  16. mModles = loadPluginData();
  17. mPluginAdapter = new PluginAdapter(mContext, mPicasso, mModles);
  18. mPluginsView = new PluginGridview2(mContext, mPluginAdapter, 3);
  19. mPluginsView.setIsDividerEnable(true);
  20. }

结束





通过上面四个步骤,我们就轻松的实现了类似微信的就九宫格效果,项目中图片有来自网络的,因此我其中使用了picasso做图片加载库,这段代码不做过渡介绍

谢谢阅读!

Android 高逼格纯代码实现类似微信钱包带分割线的GridView的更多相关文章

  1. 下拉tableView实现类似微信中带图的灰色背景

    UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(, -, ScreenWidth, )]; UIImageView *iconIma ...

  2. 打造一个高逼格的android开源项目——小白全攻略 (转)

    转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...

  3. iOS高仿app源码:纯代码打造高仿优质《内涵段子》

    iOS高仿app源码:纯代码打造高仿优质<内涵段子>收藏下来 字数1950 阅读4999 评论173 喜欢133 Github 地址 https://github.com/Charlesy ...

  4. android高仿微信UI点击头像显示大图片效果

    用过微信的朋友朋友都见过微信中点击对方头像显示会加载大图,先贴两张图片说明下: 这种UI效果对用户的体验不错,今天突然有了灵感,试着去实现,结果就出来了.. 下面说说我的思路: 1.点击图片时跳转到另 ...

  5. VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启)

    VopSdk一个高逼格微信公众号开发SDK(源码下载) VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启) 针对第一版,我们搞了第二版本,老规矩先定个目标. 一 我们的目标 a.移 ...

  6. Android高级控件(五)——如何打造一个企业级应用对话列表,以QQ,微信为例

    Android高级控件(五)--如何打造一个企业级应用对话列表,以QQ,微信为例 看标题这么高大上,实际上,还是运用我么拿到listview去扩展,我们讲什么呢,就是研究一下QQ,微信的这种对话列表, ...

  7. VopSdk一个高逼格微信公众号开发SDK(源码下载)

    看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...

  8. Android中如何在代码中设置View的宽和高?

    Android中如何在代码中设置View的宽和高?https://zhidao.baidu.com/question/536302117.htmlhttps://blog.csdn.net/u0141 ...

  9. android 实现类似微信缓存和即时更新好友头像

    引言 使用微信时我们会发现,首次进入微信的好友列表时,会加载好友头像,但是再次进入时,就不用重新加载了,而且其他页面都不用重新加载,说明微信的好友头像是缓存在本地的,然后好友修改头像后,又会及时的更新 ...

随机推荐

  1. [ExtJS5学习笔记]第十七节 Extjs5的panel组件增加accodion成为折叠导航栏

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39102335 官方例子:http://dev.sencha.com/ext/5.0.1 ...

  2. 《java入门第一季》之对文件和字符串进行MD5加密工具类

    上一篇介绍了MD5加密算法,之前写的代码有些冗余,而且可读性很差.今天把对文本数据的加密,以及获取文件的md5值做一个封装类.代码如下: package com.itydl.utils; import ...

  3. 在javascript里 string 和 int 类型转换

    string 转换为int 类型 (1)tostring()方法 var   x=10    a   =   x.toString() //输出为string类型 alert(typeof(a)); ...

  4. 学习TensorFlow,concat连接两个(或多个)通道

    深度学习中,我们经常要使用的技术之一,连接连个通道作为下一个网络层的输入,那么在tensorflow怎么来实现呢? 我查看了tensorflow的API,找到了这个函数: tf.concat(conc ...

  5. Android开发学习之路--UI之ListView

    这里再学习写android的ListView,其实我们都使用过ListView,就像手机的联系人,就是用的ListView了.下面就实现下简单的ListView吧,首先是xml文件中添加相关的代码: ...

  6. 【一天一道LeetCode】#160. Intersection of Two Linked Lists

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...

  7. Java五道输出易错题解析(进来挑战下)

    转自:http://blog.csdn.net/lanxuezaipiao/article/details/41985243 收集了几个易错的或好玩的Java输出题,分享给大家,以后在编程学习中稍微注 ...

  8. 【一天一道LeetCode】#106. Construct Binary Tree from Inorder and Postorder Traversall

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 来源:http ...

  9. nginx root、alias、location指令使用方法

    一.nginx root指令 1. Nginx配置 相关配置如下图: 通过配置root目录到"/wwwroot/html/"位置 在用虚拟主机方法,主机名称是test,需要大家配置 ...

  10. python复杂网络库networkx:基础

    http://blog.csdn.net/pipisorry/article/details/49839251 其它复杂网络绘图库 [SNAP for python] [ArcGIS,Python,网 ...