本篇主要讲解如何实现一个简易的选择上传图片时的展示控件,该自定义控件继承自ViewGroup,支持网格排列,以及横向排列。最终效果如下图: 上图中每个ImageView的右上角都有一个删除按钮,我们可以通过组合View或者自定义View的方式去实现,这里选择自定义方式,自定义 我们知道自定义View一般流程为: 由于我们只是想在原图上画一个删除按钮,因此只需重写 上述代码实现了画灰色背景和两条交叉钱,此外,还可以根据实际情况,直接使用 接下来,我们给这个删除按钮加上点击事件,通过接口的形式对外提供按钮点击功能。 流程一般如下,但由于实际需求不同,并不是每个步骤都需要重写。 由于我们需要实现两种排列方式,所以 网格布局的代码如下: 当图片显示超过ViewGroup的宽度时,为了使交互体验更友好,需要加入滑动功能。在实现之前,我们务必理清楚ViewGroup中 当检测到水平布局进行了水平滑动时,应当拦截事件。 重写 最后,当我们给这个ViewGroup设置图片数据时,刷新布局代码如下: 完整代码见:https://github.com/maplejaw/GridImageView 使用时直接拷贝以下三个文件到工程中即可:
自定义View
GridItemView
继承自ImageView
。
onMeasure
中测量宽高,如有需要务必考虑支持padding属性和wrap_content,margin不用管,它是由父布局控制的。onDraw
来进行绘制,以达到我们所需要的效果。onDraw
即可。 private void init(){ mPaint=new Paint(Paint.ANTI_ALIAS_FLAG); mDelBound=new Rect(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int foregroundWidth=30; int foregroundHeight=30; int delBoundPadding=5; //先画灰色背景 mPaint.setColor(0x88000000); mDelBound.set(getWidth()-getPaddingRight()-foregroundWidth-delBoundPadding*2 ,getPaddingTop() ,getWidth()-getPaddingRight() ,getPaddingTop()+foregroundHeight+delBoundPadding*2); canvas.drawRect(mDelBound,mPaint); //再画两条交叉线 mPaint.setColor(0xffffffff); canvas.drawLine(mDelBound.left+delBoundPadding ,mDelBound.top+delBoundPadding ,mDelBound.right-delBoundPadding ,mDelBound.bottom-delBoundPadding,mPaint); canvas.drawLine(mDelBound.left+delBoundPadding ,mDelBound.bottom-delBoundPadding ,mDelBound.right-delBoundPadding ,mDelBound.top+delBoundPadding,mPaint); }
drawBitmap
来画按钮。 @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { boolean touchable = event.getX() > mDelBound.left && event.getX() < mDelBound.right&&event.getY()>mDelBound.top&&event.getY()<mDelBound.bottom; if (touchable&&mDelClickL!=null) {//点击删除键 mDelClickL.onDelClickL(); return true; } } return super.onTouchEvent(event); }
自定义ViewGroup
onMeasure
和onLayout
的代码如下: @Override protected void (int widthMeasureSpec, int heightMeasureSpec) {//测量 if(mShowStyle==STYLE_HORIZONTAL){ measureHorizontal(widthMeasureSpec,heightMeasureSpec); }else{ measureVertical(widthMeasureSpec,heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if(mShowStyle==STYLE_HORIZONTAL){ layoutHorizontal(changed,l,t,r,b); }else { layoutVertical(changed,l,t,r,b); } }
measure
的计算比较简单,主要是计算一下高度。 private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec){ int width = MeasureSpec.getSize(widthMeasureSpec); int height; int totalWidth = width - getPaddingLeft() - getPaddingRight(); mGridSize = (totalWidth - mGap * (mColumnCount - 1)) / mColumnCount; //算出每个条目的大小,以宽度为标准。 height = mGridSize + getPaddingTop() + getPaddingBottom();//计算出高度 setMeasuredDimension(width, height); } private void measureVertical(int widthMeasureSpec, int heightMeasureSpec){ int width = MeasureSpec.getSize(widthMeasureSpec); int height; int totalWidth = width - getPaddingLeft() - getPaddingRight(); int totalCount=mImgDataList.size()+1;//把mAddView也给算进去 mGridSize = (totalWidth - mGap * (mColumnCount - 1)) / mColumnCount; //算出每个条目的大小,以宽度为标准。 int mRowCount= (int) Math.ceil((totalCount*1.0)/mColumnCount);//算出行数 height = mGridSize * mRowCount + mGap * (mRowCount - 1) + getPaddingTop() + getPaddingBottom();//计算出高度 setMeasuredDimension(width, height); }
layout
中,横向布局当图片显示超过控件宽度时,我们希望能自动移动到最右边,代码如下,主要是通过scrollTo
方法实现的,当mRightBorder-mLeftBorder>getWidth()
时,即可认为图片显示大于控件宽度。 //水平布局 private void layoutHorizontal(boolean changed, int l, int t, int r, int b) { int childrenCount = mImgDataList.size(); for (int i = 0; i < childrenCount; i++) { ImageView childrenView = (ImageView) getChildAt(i); if (mAdapter != null) { mAdapter.onDisplayImage(getContext(), childrenView, mImgDataList.get(i)); } int left = (mGridSize + mGap) * i + getPaddingLeft(); int top = getPaddingTop(); int right = left + mGridSize; int bottom = top + mGridSize; childrenView.layout(left, top, right, bottom); } int left = (mGridSize + mGap) * childrenCount + getPaddingLeft(); int top = getPaddingTop(); int right = left + mGridSize; int bottom = top + mGridSize; mAddView.layout(left, top, right, bottom);//调整mAddView的位置 // 初始化左右边界值 mLeftBorder=getChildAt(0).getLeft(); mRightBorder=getChildAt(childrenCount).getRight(); if(mRightBorder-mLeftBorder>getWidth()){ scrollTo(mRightBorder - getWidth(),0); }else{ scrollTo(mLeftBorder,0); } }
private void layoutVertical(boolean changed, int l, int t, int r, int b) { int childrenCount = mImgDataList.size(); for (int i = 0; i < childrenCount; i++) { ImageView childrenView = (ImageView) getChildAt(i); if (mAdapter != null) { mAdapter.onDisplayImage(getContext(), childrenView, mImgDataList.get(i)); } int rowNum = i / mColumnCount; int columnNum = i % mColumnCount; int left = (mGridSize + mGap) * columnNum + getPaddingLeft(); int top = (mGridSize + mGap) * rowNum + getPaddingTop(); int right = left + mGridSize; int bottom = top + mGridSize; childrenView.layout(left, top, right, bottom); } int rowNum = (childrenCount) / mColumnCount; int columnNum = (childrenCount) % mColumnCount; int left = (mGridSize + mGap) * columnNum + getPaddingLeft(); int top = (mGridSize + mGap) * rowNum + getPaddingTop(); int right = left + mGridSize; int bottom = top + mGridSize; mAddView.layout(left, top, right, bottom);//调整mAddView的位置 }
自定义滑动事件
dispatchTouchEvent
,onInterceptTouchEvent
,onTouchEvent
中的作用以及事件传递机制。 @Override public boolean onInterceptTouchEvent(MotionEvent event) { float x = event.getX(); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: if(mScroller != null){ if(!mScroller.isFinished()){ mScroller.abortAnimation(); } } mLastX = x; //记住开始落下的屏幕点 break; case MotionEvent.ACTION_MOVE: int detaX = (int) (x-mLastX); if(Math.abs(detaX)>mTouchSlop&&mShowStyle==STYLE_HORIZONTAL){ return true; } break; } return super.onInterceptTouchEvent(event); }
onTouchEvent
。在MotionEvent.ACTION_MOVE
中通过scrollBy来实现滑动效果,在MotionEvent.ACTION_UP
中使用Scroller
加入惯性滑动效果以增强交互体验。 private float mLastX;//记录上次滑动的位置 @Override public boolean onTouchEvent(MotionEvent event) { boolean isTouch=false; acquireVelocityTracker(event); //触摸点 float x = event.getX(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: if(mScroller != null){ if(!mScroller.isFinished()){ mScroller.abortAnimation(); } } mLastX = x ; isTouch=false; break ; case MotionEvent.ACTION_MOVE: int detaX = (int)(mLastX-x); //每次滑动屏幕,屏幕应该移动的距离 if (getScrollX() + detaX < mLeftBorder) {//判断有没有划出边界,如果划出便还原。 scrollTo(mLeftBorder,0); }else if (getScrollX() + getWidth() + detaX > mRightBorder) { if(mRightBorder-mLeftBorder>getWidth()){ scrollTo(mRightBorder - getWidth(),0); }else{ scrollTo(mLeftBorder,0); } }else{ scrollBy(detaX, 0); } mLastX = x ; isTouch=true; break ; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isTouch=false; mVelocityTracker.computeCurrentVelocity(1000,maxFlingSpeed); int speed= (int) mVelocityTracker.getXVelocity(); if(Math.abs(speed)>minFlingSpeed){ mScroller.fling(getScrollX(), 0, -speed, 0, mLeftBorder, mRightBorder-getWidth(), 0, 0); invalidate(); } releaseVelocityTracker(); break; } return isTouch; } private void acquireVelocityTracker(MotionEvent event) { if(null == mVelocityTracker) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } private void releaseVelocityTracker() { if(null != mVelocityTracker) { mVelocityTracker.clear(); mVelocityTracker.recycle(); mVelocityTracker = null; } }
private void refreshDataSet() {//根据数据,调整ImageView的数量 int oldViewCount = getChildCount()-1;//上次的item个数,由于多了个mAddView,因此要减去1 int newViewCount = mImgDataList.size();//这次的item个数 if (oldViewCount > newViewCount) { removeViews(newViewCount, oldViewCount - newViewCount); } else if (oldViewCount < newViewCount) { for (int i = oldViewCount; i < newViewCount; i++) { ImageView iv = getImageView(i); addView(iv, i,generateDefaultLayoutParams()); } } requestLayout(); }
最后
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算