Android 高逼格纯代码实现类似微信钱包带分割线的GridView
前言
通过上两篇关于自定view的文章,在自定义view基础上,今天给大家带来怎么代码自定义九宫格子,并且显示支付宝一样的九宫格效果:
导读:
目标效果:
自定义GridView
继承ViewGroup ,我们复写测量,绘制,布局方法,并且将此类定义为抽象类,用于绘制基础的宫格视图。
onMeasure
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // 获取给定尺寸
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int nChildCount = getIconViewCount();
- // 计算总行数和总列数
- mColCount = getColCount();
- int row = nChildCount / mColCount;
- int col = nChildCount % mColCount;
- mRowCount = ((col == 0) ? row : row + 1);
- // 计算单元格尺寸
- int dividerW = getDividerWidth() * (mColCount + 1);
- mCellWidth = 1.0f * (width - getPaddingLeft() - getPaddingRight() - dividerW) / mColCount;
- // 遍历子view的尺寸设置
- for (int i = 0; i < nChildCount; i++) {
- View child = getIconView(i);
- child.measure((int) mCellWidth, (int) mCellHeight);
- }
- // slot
- int slotHeightMeasureSpec =
- getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
- mSlotView.measure(widthMeasureSpec, slotHeightMeasureSpec);
- int slotRow = getSlotRow();
- mSlotOffsetY = (int) (slotRow * mCellHeight + (slotRow + 1) * getDividerWidth());
- // 计算总尺寸
- int nViewHeight = Math.round(mRowCount * mCellHeight + (mRowCount + 1) * getDividerWidth());
- nViewHeight = nViewHeight + mSlotView.getMeasuredHeight();
- if (slotRow < mRowCount && mSlotView.getMeasuredHeight() > 0) {
- nViewHeight = nViewHeight + getDividerWidth();
- }
- nViewHeight = nViewHeight + getPaddingTop() + getPaddingBottom();
- // 设置总尺寸
- setMeasuredDimension(width, nViewHeight);
- // 分割线初始化
- initDividerData();
- }
onLayout
- @Override
- protected void onLayout(boolean change, int l, int t, int r, int b) {
- // banner视图
- mSlotView.layout(0, mSlotOffsetY,
- mSlotView.getMeasuredWidth(), mSlotOffsetY + mSlotView.getMeasuredHeight());
- // 单元视图
- int count = getIconViewCount();
- for (int i = 0; i < count; i++) {
- View childView = getIconView(i);
- BdAnimInfo animInfo = getViewHolder(childView).getAnimInfo();
- layoutItem(childView, i, animInfo.isMove());
- }
- }
onDraw
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 分割线
- if (mIsDividerEnable) {
- drawDivider(canvas);
- }
- }
完成了基本的测量和layout,和整体item绘制后,接着继续画分割线:
- /**
- * 绘制分割线
- *
- * @param aCanvas 画布
- * @param aPaint 画笔
- * @param aLineOffset 偏移
- * @param aLineWidth 线宽度
- */
- public void drawCrossLine(Canvas aCanvas, Paint aPaint, int aLineOffset, int aLineWidth) {
- // 变量
- int max;
- int offset;
- int lastCol = getIconViewCount() % mColCount;
- int bannerRow = getSlotRow();
- Drawable divider = getDivider();
- // Horizontal line
- max = mDividerRow;
- int left = getPaddingLeft();
- int right = getMeasuredWidth() - getPaddingRight();
- offset = getPaddingTop() + aLineOffset;
- for (int i = 0; i < max; i++) {
- if (i == max - 1) { // 最后一行停留在某个格子右边
- // 右边终点重新确定
- if (lastCol > 0) {
- right = (int) (getPaddingLeft() + lastCol * mCellWidth + (lastCol + 1) * getDividerWidth());
- }
- } else if (i == bannerRow && mSlotView.getMeasuredHeight() > 0) { // banner特殊处理
- divider.setBounds(left, offset, right, offset + aLineWidth);
- divider.draw(aCanvas);
- offset += getDividerWidth() + mSlotView.getMeasuredHeight();
- }
- // 绘制
- divider.setBounds(left, offset, right, offset + aLineWidth);
- divider.draw(aCanvas);
- offset += mCellHeight + getDividerWidth();
- }
- // Vertical line
- max = mDividerCol;
- int top = getPaddingTop();
- int bottom = 0;
- for (int i = 0; i < max; i++) {
- offset = (int) (getPaddingLeft() + aLineOffset + i * (mCellWidth + getDividerWidth()));
- if (i > lastCol && lastCol > 0) {
- bottom = (int) (getPaddingTop() + mCellHeight * (mRowCount - 1) + getDividerWidth() * mRowCount);
- } else {
- bottom = (int) (getPaddingTop() + mCellHeight * mRowCount + getDividerWidth() * (mRowCount + 1));
- }
- if (bottom >= mSlotOffsetY && mSlotView.getMeasuredHeight() > 0) {
- int midBottom = mSlotOffsetY - getDividerWidth();
- int midTop = mSlotOffsetY + mSlotView.getMeasuredHeight();
- bottom += mSlotView.getMeasuredHeight();
- divider.setBounds(offset, top, offset + aLineWidth, midBottom);
- divider.draw(aCanvas);
- divider.setBounds(offset, midTop, offset + aLineWidth, bottom);
- divider.draw(aCanvas);
- } else {
- divider.setBounds(offset, top, offset + aLineWidth, bottom);
- divider.draw(aCanvas);
- }
- }
- }
以上便是最核心的三个方法了,其他代码可以下面的完整代码,
- * 宫格视图抽象类 其子类为baseGridview
- * 提供最基本的绘制和属性初始化工作,添加删除工作以及条目
- * @author LIUYONGKUI
- */
- public abstract class PaAbsGridView extends ViewGroup {
- /**
- * 视图holder的标志,用于标记View中的tag
- */
- public static final int VIEW_HOLDER_TAG = 0x0fffff00;
- /**
- * 默认列数
- */
- public static final int DEF_COLCOUNT = 3;
- /**
- * 行数
- */
- private int mRowCount;
- /**
- * 列数
- */
- private int mColCount;
- /**
- * 宫格宽度
- */
- private float mCellWidth;
- /**
- * 宫格高度
- */
- private int mCellHeight;
- /**
- * 临时区域
- */
- private Rect mAssistRect = new Rect();
- /**
- * 插槽
- */
- private FrameLayout mSlotView;
- /**
- * 插槽的偏移量: y
- */
- private int mSlotOffsetY;
- /**
- * 插槽的偏移行
- */
- private int mSlotOffsetRow;
- /**
- * 分割线是否显示
- */
- private boolean mIsDividerEnable;
- /**
- * 分割线
- */
- private int mDividerWidth;
- /**
- * 交叉线行数
- */
- private int mDividerRow;
- /**
- * 交叉线列数
- */
- private int mDividerCol;
- /**
- * 分割线图片(白天)
- */
- private Drawable mDividerDay;
- /**
- * 分割线图片(夜晚)
- */
- private Drawable mDividerNight;
- /**
- * 数据适配器
- */
- private BaseAdapter mAdapter;
- /**
- * 构造函数
- *
- * @param context 上下文
- * @param aAdapter adapter
- * @param aColCount 列数
- */
- public PaAbsGridView(Context context, BaseAdapter aAdapter, int aColCount) {
- super(context);
- // 基本属性
- this.setWillNotDraw(false);
- mColCount = aColCount;
- mAdapter = aAdapter;
- mAdapter.registerDataSetObserver(new BdCellDataObserver());
- // 视图属性
- mCellHeight = getCellHeight();
- mDividerWidth = getDividerWidthDef();
- mDividerDay = getDividerDay();
- mDividerNight = getDividerDay();
- // 刷新视图
- refreshViews();
- }
- /**
- * 初始化视图
- */
- public void refreshViews() {
- // 初始化banner插槽
- if (mSlotView == null) {
- mSlotView = new FrameLayout(getContext());
- mSlotOffsetRow = 3;
- }
- final List<View> cacheList = new ArrayList<View>();
- for (int i = 0; i < getIconViewCount(); i++) {
- cacheList.add(getIconView(i));
- }
- removeAllIconViews();
- // 添加单元项
- for (int i = 0; i < mAdapter.getCount(); i++) {
- addItemView(queryCacheItemView(cacheList, i), false);
- }
- cacheList.clear();
- }
- /**
- * getDrviderDay
- * @return
- */
- protected Drawable getDividerDay() {
- return getResources().getDrawable(R.drawable.home_divider_day);
- }
- /**
- * getDrivderNight
- * @return
- */
- protected Drawable getDividerNight() {
- return getResources().getDrawable(R.drawable.home_divider_night);
- }
- /**
- * @return 适配器
- */
- public BaseAdapter getAdapter() {
- return mAdapter;
- }
- /**
- * @param aIsEnable 分割线是否有效
- */
- public void setIsDividerEnable(boolean aIsEnable) {
- mIsDividerEnable = aIsEnable;
- }
- /**
- * 获取缓存的单元视图
- *
- * @param aCacheList 缓存列表
- * @param aDataIndex 数据索引
- * @return 单元视图
- */
- private View queryCacheItemView(List<View> aCacheList, int aDataIndex) {
- final int count = ((aCacheList != null) ? aCacheList.size() : 0);
- View cacheView = null;
- // 寻找数据项匹配的单元视图
- Object item = mAdapter.getItem(aDataIndex);
- if (item != null) {
- for (int i = 0; i < count; i++) {
- View itemView = aCacheList.get(i);
- Object itemData = getViewHolder(itemView).getData();
- if ((itemData != null) && (itemData.equals(item))) {
- aCacheList.remove(i);
- cacheView = itemView;
- break;
- }
- }
- }
- // 寻找类型匹配的单元视图
- View targetView = mAdapter.getView(aDataIndex, cacheView, null);
- getViewHolder(targetView).setData(item);
- return targetView;
- }
- /**
- * 往插槽里填充视图
- *
- * @param aView 视图
- */
- public void addToSlot(View aView) {
- if ((aView != null) && (mSlotView.indexOfChild(aView) < 0)) {
- mSlotView.addView(aView,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- }
- }
- /**
- * 添加单元格视图
- *
- * @param aIconView 图标单元格视图
- */
- public void addIconView(View aIconView) {
- addView(aIconView);
- }
- /**
- * 添加单元格视图
- *
- * @param aIconView 单元格视图
- * @param aIconIndex 位置
- */
- public void addIconView(View aIconView, int aIconIndex) {
- addView(aIconView, aIconIndex + 1);
- }
- /**
- * 移除单元格视图
- *
- * @param aIconView 单元格视图
- */
- public void removeIconView(View aIconView) {
- removeView(aIconView);
- }
- /**
- * @return 单元格视图的总个数
- */
- public int getIconViewCount() {
- return getChildCount() - 1;
- }
- /**
- * @param aIndex 索引
- * @return 单元格视图
- */
- public View getIconView(int aIndex) {
- return getChildAt(aIndex + 1);
- }
- /**
- * 移除所有的单元格视图
- */
- public void removeAllIconViews() {
- removeAllViews();
- addView(mSlotView);
- }
- /**
- * 释放所有视图资源
- */
- public void releaseAllViews() {
- removeAllViews();
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // 获取给定尺寸
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int nChildCount = getIconViewCount();
- // 计算总行数和总列数
- mColCount = getColCount();
- int row = nChildCount / mColCount;
- int col = nChildCount % mColCount;
- mRowCount = ((col == 0) ? row : row + 1);
- // 计算单元格尺寸
- int dividerW = getDividerWidth() * (mColCount + 1);
- mCellWidth = 1.0f * (width - getPaddingLeft() - getPaddingRight() - dividerW) / mColCount;
- // 遍历子view的尺寸设置
- for (int i = 0; i < nChildCount; i++) {
- View child = getIconView(i);
- child.measure((int) mCellWidth, (int) mCellHeight);
- }
- // slot
- int slotHeightMeasureSpec =
- getChildMeasureSpec(heightMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
- mSlotView.measure(widthMeasureSpec, slotHeightMeasureSpec);
- int slotRow = getSlotRow();
- mSlotOffsetY = (int) (slotRow * mCellHeight + (slotRow + 1) * getDividerWidth());
- // 计算总尺寸
- int nViewHeight = Math.round(mRowCount * mCellHeight + (mRowCount + 1) * getDividerWidth());
- nViewHeight = nViewHeight + mSlotView.getMeasuredHeight();
- if (slotRow < mRowCount && mSlotView.getMeasuredHeight() > 0) {
- nViewHeight = nViewHeight + getDividerWidth();
- }
- nViewHeight = nViewHeight + getPaddingTop() + getPaddingBottom();
- // 设置总尺寸
- setMeasuredDimension(width, nViewHeight);
- // 分割线初始化
- initDividerData();
- }
- @Override
- protected void onLayout(boolean change, int l, int t, int r, int b) {
- // banner视图
- mSlotView.layout(0, mSlotOffsetY,
- mSlotView.getMeasuredWidth(), mSlotOffsetY + mSlotView.getMeasuredHeight());
- // 单元视图
- int count = getIconViewCount();
- for (int i = 0; i < count; i++) {
- View childView = getIconView(i);
- BdAnimInfo animInfo = getViewHolder(childView).getAnimInfo();
- layoutItem(childView, i, animInfo.isMove());
- }
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 分割线
- if (mIsDividerEnable) {
- drawDivider(canvas);
- }
- }
- /**
- * 初始化分割线数据
- */
- public void initDividerData() {
- // 计算绘制行数
- int row = getIconViewCount() / mColCount;
- int col = getIconViewCount() % mColCount;
- mDividerRow = ((col == 0) ? row + 1 : row + 2);
- // 计算绘制列
- mDividerCol = mColCount + 1;
- }
- /**
- * 绘制分割线
- *
- * @param aCanvas 画布
- */
- public void drawDivider(Canvas aCanvas) {
- // 绘制交叉线
- drawCrossLine(aCanvas, null, 0, getDividerWidth());
- }
- /**
- * @return 分割线图片
- */
- public Drawable getDivider() {
- /* if (BdThemeManager.getInstance().isNightT5()) {
- return mDividerNight;
- } else {
- return mDividerDay;
- }*/
- return mDividerDay;
- }
- /**
- * 绘制分割线
- *
- * @param aCanvas 画布
- * @param aPaint 画笔
- * @param aLineOffset 偏移
- * @param aLineWidth 线宽度
- */
- public void drawCrossLine(Canvas aCanvas, Paint aPaint, int aLineOffset, int aLineWidth) {
- // 变量
- int max;
- int offset;
- int lastCol = getIconViewCount() % mColCount;
- int bannerRow = getSlotRow();
- Drawable divider = getDivider();
- // Horizontal line
- max = mDividerRow;
- int left = getPaddingLeft();
- int right = getMeasuredWidth() - getPaddingRight();
- offset = getPaddingTop() + aLineOffset;
- for (int i = 0; i < max; i++) {
- if (i == max - 1) { // 最后一行停留在某个格子右边
- // 右边终点重新确定
- if (lastCol > 0) {
- right = (int) (getPaddingLeft() + lastCol * mCellWidth + (lastCol + 1) * getDividerWidth());
- }
- } else if (i == bannerRow && mSlotView.getMeasuredHeight() > 0) { // banner特殊处理
- divider.setBounds(left, offset, right, offset + aLineWidth);
- divider.draw(aCanvas);
- offset += getDividerWidth() + mSlotView.getMeasuredHeight();
- }
- // 绘制
- divider.setBounds(left, offset, right, offset + aLineWidth);
- divider.draw(aCanvas);
- offset += mCellHeight + getDividerWidth();
- }
- // Vertical line
- max = mDividerCol;
- int top = getPaddingTop();
- int bottom = 0;
- for (int i = 0; i < max; i++) {
- offset = (int) (getPaddingLeft() + aLineOffset + i * (mCellWidth + getDividerWidth()));
- if (i > lastCol && lastCol > 0) {
- bottom = (int) (getPaddingTop() + mCellHeight * (mRowCount - 1) + getDividerWidth() * mRowCount);
- } else {
- bottom = (int) (getPaddingTop() + mCellHeight * mRowCount + getDividerWidth() * (mRowCount + 1));
- }
- if (bottom >= mSlotOffsetY && mSlotView.getMeasuredHeight() > 0) {
- int midBottom = mSlotOffsetY - getDividerWidth();
- int midTop = mSlotOffsetY + mSlotView.getMeasuredHeight();
- bottom += mSlotView.getMeasuredHeight();
- divider.setBounds(offset, top, offset + aLineWidth, midBottom);
- divider.draw(aCanvas);
- divider.setBounds(offset, midTop, offset + aLineWidth, bottom);
- divider.draw(aCanvas);
- } else {
- divider.setBounds(offset, top, offset + aLineWidth, bottom);
- divider.draw(aCanvas);
- }
- }
- }
- /**
- * 增加数据项对应的子项
- *
- * @param aItemView item view
- * @param isAfterInit init
- */
- private void addItemView(View aItemView, boolean isAfterInit) {
- if (isAfterInit) {
- addIconView(aItemView, getIconViewCount() - 1);
- } else {
- addIconView(aItemView);
- }
- }
- /**
- * 布局子项;可重载,允许子类有实现其它功能的机会
- *
- * @param aChild 子项
- * @param aPos 位置
- * @param aIsAnim 是否做动画
- */
- public void layoutItem(View aChild, int aPos, boolean aIsAnim) {
- // 获取视图的动画信息
- BdViewHolder holder = getViewHolder(aChild);
- BdAnimInfo animInfo = holder.getAnimInfo();
- // 获取相应位置的矩形区域
- final Rect r = mAssistRect;
- getChildArea(r, aChild, aPos);
- // 如果动画进行中,重新定义动画属性; 否则直接布局
- if (aIsAnim) {
- animInfo.beginMove(aChild.getLeft(), aChild.getTop(), r.left, r.top);
- } else {
- aChild.layout(r.left, r.top, r.right, r.bottom);
- }
- // 重新定义视图位置
- holder.setPosition(aPos);
- }
- /**
- * 更新动画中子项的状态
- */
- public void updateAnimInfo(float aFactor) {
- // 更新移动项
- int count = getIconViewCount();
- for (int i = 0; i < count; i++) {
- View itemView = getIconView(i);
- BdAnimInfo animInfo = getViewHolder(itemView).getAnimInfo();
- if (animInfo.isMove()) {
- animInfo.move(aFactor);
- }
- }
- }
- /**
- * 更新动画中子项的布局
- */
- public void updateAnimLayout(float aFactor) {
- // 重新布局
- int count = getIconViewCount();
- for (int i = 0; i < count; i++) {
- View itemView = getIconView(i);
- BdAnimInfo animInfo = getViewHolder(itemView).getAnimInfo();
- if (animInfo.isMove()) {
- itemView.layout(animInfo.getX(), animInfo.getY(),
- animInfo.getX() + itemView.getMeasuredWidth(),
- animInfo.getY() + itemView.getMeasuredHeight());
- // 结束标记
- if (aFactor == 1.0f) {
- animInfo.endMove();
- }
- }
- }
- }
- /**
- * 获取给定位置上的子项区域
- *
- * @param outRect 存储区域
- * @param child 子项
- * @param pos 子项位置
- */
- public void getChildArea(Rect outRect, View child, int pos) {
- getCellArea(outRect, pos);
- int offsetX = (int) (outRect.left + (mCellWidth - child.getMeasuredWidth()) / 2);
- int offsetY = (int) (outRect.top + (mCellHeight - child.getMeasuredHeight()) / 2);
- outRect.set(offsetX, offsetY, offsetX + child.getMeasuredWidth(), offsetY + child.getMeasuredHeight());
- }
- /**
- * 获取单元格的区域
- *
- * @param outRect 所在区域
- * @param pos 单元格位置
- */
- public void getCellArea(Rect outRect, int pos) {
- int row = pos / mColCount;
- int col = pos % mColCount;
- int bannerRow = (mRowCount > mSlotOffsetRow ? mSlotOffsetRow : mRowCount);
- int startX = (int) (getPaddingLeft() + (col + 1) * getDividerWidth() + col * mCellWidth);
- int startY = (int) (getPaddingTop() + (row + 1) * getDividerWidth() + row * mCellHeight);
- // 如果banner在宫格的上方存在,那么要多加一条分割线
- if ((row >= bannerRow) && mSlotView.getMeasuredHeight() > 0) {
- startY = startY + mSlotView.getMeasuredHeight() + getDividerWidth();
- }
- outRect.set(startX, startY, (int) (startX + mCellWidth), (int) (startY + mCellHeight));
- }
- /**
- * @return banner所在行
- */
- public int getSlotRow() {
- return (mRowCount > mSlotOffsetRow ? mSlotOffsetRow : mRowCount);
- }
- /**
- * 快速定位位置
- *
- * @param x 当前坐标x
- * @param y 当前坐标y
- * @return 命中的单元格索引
- */
- public int getCellPosition(int x, int y) {
- // 先去除banner的偏移
- if (y > mSlotOffsetY) {
- y -= mSlotView.getMeasuredHeight();
- }
- int row = (int) ((y - getPaddingTop()) / mCellHeight);
- int col = (int) ((x - getPaddingLeft()) / mCellWidth);
- return row * mColCount + col;
- }
- /**
- * @return 单元格宽度
- */
- public int getCellWidth() {
- return (int) mCellWidth;
- }
- /**
- * @return 单元格高度
- */
- public int getCellHeight() {
- return mCellHeight == -1 ? getResources().getDimensionPixelSize(R.dimen.home_item_height): mCellHeight;
- }
- /**
- * @return 分割线宽度
- */
- public int getDividerWidth() {
- return (mIsDividerEnable ? mDividerWidth : 0);
- }
- /**
- * @return 默认分割线宽度
- */
- public int getDividerWidthDef() {
- return mDividerWidth == -1 ? getResources().getDimensionPixelOffset(R.dimen.home_divider_width): mDividerWidth;
- }
- /**
- * @return 总行数
- */
- public int getRowCount() {
- return mRowCount;
- }
- /**
- * @return 总列数
- */
- public int getColCount() {
- return mColCount == -1 ? DEF_COLCOUNT : mColCount;
- }
- /**
- * 改变子项的位置
- *
- * @param aFromPosition 原始位置
- * @param aToPosition 终点位置
- * @param isAnimation 是否附带动画效果
- */
- public void changeItemPosition(int aFromPosition, int aToPosition, boolean isAnimation) {
- int nChildCount = getIconViewCount();
- // 边界判断
- if ((aFromPosition < 0) || (aFromPosition >= nChildCount) || (aToPosition < 0)
- || (aToPosition >= nChildCount)) {
- return;
- }
- // 寻找子项
- View fromView = null;
- View toView = null;
- for (int i = 0; i < nChildCount; i++) {
- View childView = getIconView(i);
- if (getViewPosition(childView) == aFromPosition) {
- fromView = childView;
- }
- if (getViewPosition(childView) == aToPosition) {
- toView = childView;
- }
- }
- // 设置新的项
- if (fromView == toView) {
- layoutItem(fromView, aToPosition, isAnimation);
- } else {
- layoutItem(fromView, aToPosition, isAnimation);
- layoutItem(toView, aFromPosition, isAnimation);
- }
- }
- /**
- * @param aView 视图
- * @return 视图位置
- */
- public int getViewPosition(View aView) {
- return getViewHolder(aView).getPosition();
- }
- /**
- * @param aView 视图
- * @param aPos 视图位置
- */
- public void setViewPosition(View aView, int aPos) {
- getViewHolder(aView).setPosition(aPos);
- }
- /**
- * @param aView 视图
- * @return holder
- */
- private BdViewHolder getViewHolder(View aView) {
- BdViewHolder holder = (BdViewHolder) aView.getTag(VIEW_HOLDER_TAG);
- if (holder == null) {
- holder = new BdViewHolder();
- aView.setTag(VIEW_HOLDER_TAG, holder);
- }
- return holder;
- }
- /**
- * 数据更新类
- */
- class BdCellDataObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- super.onChanged();
- // 刷新视图
- refreshViews();
- }
- }
- /**
- * 视图holder
- */
- class BdViewHolder {
- /**
- * 动画信息
- */
- BdAnimInfo mAnimInfo;
- /**
- * 视图数据
- */
- Object mViewData;
- /**
- * 视图位置
- */
- int mViewPosition;
- /**
- * 构造函数
- */
- public BdViewHolder() {
- mAnimInfo = new BdAnimInfo();
- }
- /**
- * @return 动画信息
- */
- public BdAnimInfo getAnimInfo() {
- return mAnimInfo;
- }
- /**
- * @param aData 数据
- */
- public void setData(Object aData) {
- mViewData = aData;
- }
- /**
- * @return 数据
- */
- public Object getData() {
- return mViewData;
- }
- /**
- * @param aPosition 位置
- */
- public void setPosition(int aPosition) {
- mViewPosition = aPosition;
- }
- /**
- * @return 位置
- */
- public int getPosition() {
- return mViewPosition;
- }
- }
- /**
- * 动画信息
- */
- class BdAnimInfo {
- /**
- * 目标坐标
- */
- int mStartX;
- /**
- * 目标坐标
- */
- int mStartY;
- /**
- * 目标坐标
- */
- int mTargetX;
- /**
- * 目标坐标
- */
- private int mTargetY;
- /**
- * 目标坐标
- */
- private int mMoveX;
- /**
- * 目标坐标
- */
- private int mMoveY;
- /**
- * 目标坐标
- */
- private boolean mIsMove;
- /**
- * 开始移动
- *
- * @param aStartX 起始x值
- * @param aStartY 起始y值
- * @param aTargetX 目标x值
- * @param aTargetY 目标y值
- */
- public void beginMove(int aStartX, int aStartY, int aTargetX, int aTargetY) {
- setIsMove(true);
- mTargetX = aTargetX;
- mTargetY = aTargetY;
- mStartX = aStartX;
- mStartY = aStartY;
- }
- /**
- * 结束移动
- */
- public void endMove() {
- setIsMove(false);
- }
- /**
- * @param aFactor 比例
- */
- public void move(float aFactor) {
- if (isMove()) {
- mMoveX = mStartX + (int) ((mTargetX - mStartX) * aFactor);
- mMoveY = mStartY + (int) ((mTargetY - mStartY) * aFactor);
- }
- }
- /**
- * 设置是否在移动
- *
- * @param isMove 标志
- */
- public void setIsMove(boolean isMove) {
- mIsMove = isMove;
- }
- /**
- * 判断是否在移动
- *
- * @return 判断结果
- */
- public boolean isMove() {
- return mIsMove;
- }
- /**
- * @return x
- */
- public int getX() {
- return mMoveX;
- }
- /**
- * @return y
- */
- public int getY() {
- return mMoveY;
- }
- }
- }
BaseGridView
接着我们继续实现定义好的抽象类,完成上面必须实现的俩抽象方法:
此类的作用时将事件绑定到我们的宫格视图上,因此我抽象出了来方法:长按事件和短按事件,便于上层实现
- /**
- * 基础 baseGridview
- * 提供事件绑定操作
- * Created by LIUYONGKUI726 on 2016-03-03.
- */
- public abstract class BaseGridView extends AbsGridView implements OnClickListener, OnLongClickListener{
- /**
- * @param context 上下文
- * @param adapter 适配器
- */
- public BaseGridView(Context context, PaAbsAdapter adapter) {
- this(context, adapter, -1);
- }
- /**
- * @param context 上下文
- * @param adapter 适配器
- * @param aCount 列数(默认3)
- */
- public BaseGridView(Context context, PaAbsAdapter adapter, int aCount) {
- super(context, adapter, aCount <= 0 ? -1 : aCount);
- }
- @Override
- public void layoutItem(View aChild, int aPos, boolean aIsAnim) {
- super.layoutItem(aChild, aPos, aIsAnim);
- aChild.setOnClickListener(this);
- aChild.setOnLongClickListener(this);
- }
- @Override
- public void onClick(View v) {
- onItemClick(v);
- }
- @Override
- public boolean onLongClick(View v) {
- return onItemLongClick(v);
- }
- /**
- * item 短按事件
- * @param v
- */
- public abstract void onItemClick(View v);
- /**
- * item 长按事件
- * @param v
- */
- public abstract boolean onItemLongClick(View v);
- }
AbsAdapter
接着我们还要写一个抽象泛型适配器,方便上层数据和view的绑定
- public abstract class AbsAdapter<T> extends BaseAdapter {
- public ArrayList<T> mList = new ArrayList<T>();
- public Context mContext;
- public PaAbsAdapter(Context context, List<T> list) {
- mContext = context;
- if (list != null) {
- mList.addAll(list);
- }
- }
- @Override
- public int getCount() {
- return mList == null ? 0 : mList.size();
- }
- @Override
- public Object getItem(int position) {
- return mList == null ? null : mList.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public abstract View getView(int position, View convertView, ViewGroup parent);
- /**
- * 更新ListView
- *
- * @param list
- */
- public void notifyDataSetChanged(ArrayList<T> list) {
- if (mList != list) {
- mList.clear();
- if (list != null) {
- mList.addAll(list);
- }
- }
- if (mContext != null) {
- ((Activity)mContext).runOnUiThread(new Runnable() {
- @Override
- public void run() {
- notifyDataSetChanged();
- }
- });
- }
- }
- }
具体使用
PulginGridView
由于我的项目需要用这个做插件功能,而且微信中的钱包其实集成了很多插件,因此我将这个gridview命名为pluginView
- **
- * Created by liuyongkui on 2016-09-02.
- */
- public class PluginGridview2 extends BaseGridView {
- public PluginGridview2(Context context, PaAbsAdapter adapter, int aCount) {
- super(context, adapter, aCount);
- }
- public PluginGridview2(Context context, PaAbsAdapter adapter) {
- super(context, adapter);
- }
- @Override
- public void onItemClick(View v) {
- }
- @Override
- public boolean onItemLongClick(View v) {
- return false;
- }
- }
PluginGridViewItem
一看标题你既知道这个gridview的item,这里我也没用xml;直接用纯java代码实现,直接继承RelativeLayout,看看了效果图,你就知道里面必定有个imageview和一行文字。
接着代码, 并且做了点击item的背景效果,
- public class PluginGridViewItem extends RelativeLayout {
- /** 完全不透明度 */
- private static final int FULL_ALPHA = 255;
- /** 半不透明度 */
- private static final int HALF_ALPHA = 128;
- /** icon名称文字的大小,单位dp */
- private static final int SUG_NAME_TEXT_SIZE = 12;
- /** ICON 视图的ID */
- private static final int SUG_ICON_ID = 0x0101;
- /** 数据 */
- private PluginConfigModle mGuideModle;
- /** ICON */
- private ImageView mSugIcon;
- /** 名称 */
- private TextView mSugName;
- /** 上下文 */
- private Context mContext;
- /** 图片加载器 */
- private Picasso mImageLoader;
- /** 该视图宽度 */
- private int mWidth;
- /** 该视图高度 */
- private int mHeight;
- /** ICON 的宽高 */
- private int mIconWidth;
- /** SUG 名称视图的高度 */
- private int mTextHeight;
- /** SUG 名称视图的宽度 */
- private int mTextWidth;
- /** 是否按下 */
- private boolean mIsPressed;
- /** 按下时的背景 */
- private Drawable mCellPressDrawable;
- /** 夜间模式下按下时的背景 */
- private Drawable mNightCellPressDrawable;
- /**
- * 默认图片,使用static减少对象数
- */
- private static Bitmap mDefaultIcon;
- /** 点击事件监听器 */
- private OnItemClickListener mItemClickListener;
- private PluginGridViewItem(Context aContext, AttributeSet aAttrs) {
- super(aContext, aAttrs);
- }
- private PluginGridViewItem(Context aContext) {
- this(aContext, null);
- }
- public PluginGridViewItem(Context aContext,
- PluginConfigModle aGuideModle, Picasso aImageLoader) {
- this(aContext);
- mGuideModle = aGuideModle;
- mContext = aContext;
- mImageLoader = aImageLoader;
- init();
- }
- public PluginConfigModle getPluginModle() {
- return mGuideModle;
- }
- public void setPluginModle(PluginConfigModle mGuideModle) {
- this.mGuideModle = mGuideModle;
- }
- /***
- * 检查屏幕的方向.
- *
- * @return false
- */
- private boolean isLandscape() {
- if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * 计算ICON的宽度,以及该视图的宽高
- */
- private void initCellWidth() {
- int screenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
- int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
- if (screenWidth > screenHeight) {
- int temp = screenWidth;
- screenWidth = screenHeight;
- screenHeight = temp;
- }
- if (isLandscape()) {
- int m = PluginGridView.mColCount;
- int n = m - 1;
- int padding = (int) (PluginGridView.PADDING_LANDSCAPE * screenHeight / 1280);
- int spacing = (int) (PluginGridView.ICON_SPACING_LANDSCAPE
- * screenHeight / 1280);
- mIconWidth = (screenHeight - n * spacing - 2 * padding) / m ;
- spacing = (int) ((PluginGridView.ICON_SPACING_LANDSCAPE - PluginGridView.PADDING_LANDSCAPE)
- * screenHeight / 1280);
- mWidth = (screenHeight - n * spacing - padding) / m;
- } else {
- int m = PluginGridView.mColCount;
- int n = m - 1;
- int padding = (int) (PluginGridView.PADDING_PORTRAIT * screenWidth / 720);
- int spacing = (int) (PluginGridView.ICON_SPACING_PORTRAIT
- * screenWidth / 720);
- mIconWidth = (screenWidth - n * spacing - 2 * padding) / m;
- padding = (int) (PluginGridView.PADDING_PORTRAIT * screenWidth / 720) / 2;
- spacing = 0;
- mWidth = (screenWidth - padding * 2) / m;
- }
- }
- /**
- * 初始化视图
- */
- private void init() {
- mCellPressDrawable = getResources().getDrawable(
- R.drawable.home_item_bg);
- mNightCellPressDrawable = getResources().getDrawable(
- R.drawable.home_item_night_bg);
- mSugIcon = new ImageView(mContext);
- //mSugIcon.setImageBitmap(mDefaultIcon);
- //mSugIcon.setId(SUG_ICON_ID);
- mSugIcon.setScaleType(ImageView.ScaleType.FIT_XY);
- mSugIcon.setMaxHeight(mWidth);
- mImageLoader.load(Uri.parse(mGuideModle.getAppIcon())).into(mSugIcon);
- LayoutParams params = new LayoutParams(
- mWidth, mWidth);
- params.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
- params.topMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_icon_top_margin);
- this.addView(mSugIcon, params);
- // init the name view
- mSugName = new TextView(mContext);
- mSugName.setMaxLines(1);
- mSugName.setText(mGuideModle.getPluginName());
- mSugName.setTextSize(TypedValue.COMPLEX_UNIT_SP, SUG_NAME_TEXT_SIZE);
- mSugName.setGravity(Gravity.CENTER);
- mSugName.setTextColor(mContext.getResources().getColor(
- R.color.sug_item_name_color));
- LayoutParams txtParams = new LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- txtParams.topMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_top_margin);
- txtParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_bottom_margin);
- txtParams.addRule(RelativeLayout.BELOW, SUG_ICON_ID);
- txtParams
- .addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
- mSugName.setLayoutParams(txtParams);
- this.addView(mSugName);
- mTextHeight = (int) (mSugName.getPaint().getFontMetrics().descent - mSugName
- .getPaint().getFontMetrics().top);
- mTextWidth = (int) mSugName.getPaint().measureText(
- mGuideModle.getPluginName());
- //onThemeChanged(0);
- }
- /**
- * 设置点击事件监听器
- *
- * @param aClickListener
- * 点击事件监听器
- */
- public void setOnItemClickListener(OnItemClickListener aClickListener) {
- mItemClickListener = aClickListener;
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- initCellWidth();
- mHeight = mIconWidth
- + mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_icon_top_margin)
- + mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_top_margin)
- + mTextHeight
- + mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_bottom_margin);
- setMeasuredDimension(mWidth, mHeight);
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (mSugIcon != null) {
- mSugIcon.layout(
- (mWidth - mIconWidth) / 2,
- mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_icon_top_margin),
- (mWidth + mIconWidth) / 2,
- mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_icon_top_margin)
- + mIconWidth);
- }
- if (mSugName != null) {
- int top = mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_icon_top_margin)
- + mIconWidth
- + mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_top_margin);
- int bottom = mHeight
- - mContext.getResources().getDimensionPixelSize(
- R.dimen.sug_item_text_bottom_margin);
- if (mTextWidth > mWidth) {
- mSugName.layout(0, top, mWidth, bottom);
- } else {
- mSugName.layout((mWidth - mTextWidth) / 2, top,
- (mWidth + mTextWidth) / 2, bottom);
- }
- }
- }
- /**
- * 处理手势.
- *
- * @see android.view.View#onTouchEvent(MotionEvent)
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mIsPressed = true;
- if (mItemClickListener != null) {
- mItemClickListener.onPressDown(PluginGridViewItem.this);
- }
- break;
- case MotionEvent.ACTION_UP:
- mIsPressed = false;
- if (mItemClickListener != null) {
- mItemClickListener.onClick(PluginGridViewItem.this,
- mGuideModle);
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- mIsPressed = false;
- if (mItemClickListener != null) {
- mItemClickListener.onPressUp(PluginGridViewItem.this);
- }
- break;
- default:
- break;
- }
- return super.onTouchEvent(event);
- }
- /**
- * Item点击监听.
- */
- public interface OnItemClickListener {
- /**
- * 点击.
- */
- void onClick(PluginGridViewItem v, PluginConfigModle model);
- /**
- * 按下.
- */
- void onPressDown(PluginGridViewItem v);
- /**
- * 弹起.
- */
- void onPressUp(PluginGridViewItem v);
- }
- public void setBackground() {
- if (mIsPressed) {
- setBackgroundDrawable(mCellPressDrawable);
- } else {
- setBackgroundDrawable(null);
- }
- }
activity
初始化我们的PluginGridView, ,并将我们的数据数据适配器设置给girdview, 并将data,和item set上
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mContext = MainActivity.this;
- mPicasso = Picasso.with(mContext);
- init();
- addContentView(mPluginsView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- }
- private void init() {
- mModles = loadPluginData();
- mPluginAdapter = new PluginAdapter(mContext, mPicasso, mModles);
- mPluginsView = new PluginGridview2(mContext, mPluginAdapter, 3);
- mPluginsView.setIsDividerEnable(true);
- }
结束
通过上面四个步骤,我们就轻松的实现了类似微信的就九宫格效果,项目中图片有来自网络的,因此我其中使用了picasso做图片加载库,这段代码不做过渡介绍
谢谢阅读!
Android 高逼格纯代码实现类似微信钱包带分割线的GridView的更多相关文章
- 下拉tableView实现类似微信中带图的灰色背景
UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(, -, ScreenWidth, )]; UIImageView *iconIma ...
- 打造一个高逼格的android开源项目——小白全攻略 (转)
转自:打造一个高逼格的android开源项目 小引子 在平时的开发过程中,我们经常会查阅很多的资料,最常参考的是 github 的开源项目.通常在项目的主页面能看到项目的简介和基本使用,并且时不时能看 ...
- iOS高仿app源码:纯代码打造高仿优质《内涵段子》
iOS高仿app源码:纯代码打造高仿优质<内涵段子>收藏下来 字数1950 阅读4999 评论173 喜欢133 Github 地址 https://github.com/Charlesy ...
- android高仿微信UI点击头像显示大图片效果
用过微信的朋友朋友都见过微信中点击对方头像显示会加载大图,先贴两张图片说明下: 这种UI效果对用户的体验不错,今天突然有了灵感,试着去实现,结果就出来了.. 下面说说我的思路: 1.点击图片时跳转到另 ...
- VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启)
VopSdk一个高逼格微信公众号开发SDK(源码下载) VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启) 针对第一版,我们搞了第二版本,老规矩先定个目标. 一 我们的目标 a.移 ...
- Android高级控件(五)——如何打造一个企业级应用对话列表,以QQ,微信为例
Android高级控件(五)--如何打造一个企业级应用对话列表,以QQ,微信为例 看标题这么高大上,实际上,还是运用我么拿到listview去扩展,我们讲什么呢,就是研究一下QQ,微信的这种对话列表, ...
- VopSdk一个高逼格微信公众号开发SDK(源码下载)
看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...
- Android中如何在代码中设置View的宽和高?
Android中如何在代码中设置View的宽和高?https://zhidao.baidu.com/question/536302117.htmlhttps://blog.csdn.net/u0141 ...
- android 实现类似微信缓存和即时更新好友头像
引言 使用微信时我们会发现,首次进入微信的好友列表时,会加载好友头像,但是再次进入时,就不用重新加载了,而且其他页面都不用重新加载,说明微信的好友头像是缓存在本地的,然后好友修改头像后,又会及时的更新 ...
随机推荐
- [ExtJS5学习笔记]第十七节 Extjs5的panel组件增加accodion成为折叠导航栏
本文地址:http://blog.csdn.net/sushengmiyan/article/details/39102335 官方例子:http://dev.sencha.com/ext/5.0.1 ...
- 《java入门第一季》之对文件和字符串进行MD5加密工具类
上一篇介绍了MD5加密算法,之前写的代码有些冗余,而且可读性很差.今天把对文本数据的加密,以及获取文件的md5值做一个封装类.代码如下: package com.itydl.utils; import ...
- 在javascript里 string 和 int 类型转换
string 转换为int 类型 (1)tostring()方法 var x=10 a = x.toString() //输出为string类型 alert(typeof(a)); ...
- 学习TensorFlow,concat连接两个(或多个)通道
深度学习中,我们经常要使用的技术之一,连接连个通道作为下一个网络层的输入,那么在tensorflow怎么来实现呢? 我查看了tensorflow的API,找到了这个函数: tf.concat(conc ...
- Android开发学习之路--UI之ListView
这里再学习写android的ListView,其实我们都使用过ListView,就像手机的联系人,就是用的ListView了.下面就实现下简单的ListView吧,首先是xml文件中添加相关的代码: ...
- 【一天一道LeetCode】#160. Intersection of Two Linked Lists
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...
- Java五道输出易错题解析(进来挑战下)
转自:http://blog.csdn.net/lanxuezaipiao/article/details/41985243 收集了几个易错的或好玩的Java输出题,分享给大家,以后在编程学习中稍微注 ...
- 【一天一道LeetCode】#106. Construct Binary Tree from Inorder and Postorder Traversall
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 来源:http ...
- nginx root、alias、location指令使用方法
一.nginx root指令 1. Nginx配置 相关配置如下图: 通过配置root目录到"/wwwroot/html/"位置 在用虚拟主机方法,主机名称是test,需要大家配置 ...
- python复杂网络库networkx:基础
http://blog.csdn.net/pipisorry/article/details/49839251 其它复杂网络绘图库 [SNAP for python] [ArcGIS,Python,网 ...