仿微信朋友圈【九宫格的实现】
仿微信朋友圈【九宫格的实现】
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
最近有个想法,想用环信的sdk去做个社交类的小demo玩。在此之前,先来模仿下微信的朋友圈九宫格效果。同时也兼容了QQ的做法,如果数据集大于九张时,就在最后一张图片上显示一层遮罩效果,并显示剩余图片的数量。之后的计划是仿微信的朋友圈评论、回复这方面的效果,在实际开发中还是比较实用的。
老规矩,先来张效果图(录制的图片太大满足不了神经的CSDN上传要求,压缩又不清晰,所以还是放几张静态图吧)
需求分析
- 单张的情况,我们需要考虑图片的宽度与我们自定义九宫格控件的宽度,如果图片的宽度大于控件的宽度,那么我们就用控件的宽度作为图片的宽度
- 2 X 2的情况,微信朋友圈对于4张的图片,采用的是2 X 2的布局方式
- 当图片集大于9张时,我们需要在最后一张图片上显示一层遮罩,并显示出剩余的图片数量
- 最后一点需要特别注意,就是关于我们自定义控件的复用。比如说我们滑出屏幕的一个item的布局是7张图片,这时滑进屏幕的item是6张图片的布局,难道我们还需要再重新new出来六个imageview?当然不是,我们完全可以复用滑出屏幕的那个布局,同时移除一个imageview即可。反之如果当前滑入的item是9张,那么复用后就只需要再new出来两个imageview控件即可。其实是跟ListView的复用机制思想差不多。
根据上面的分析,实现起来应该相对有些思路了。下面就开启自定义模式了
自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="NineGridView"><attr name="nine_gv_spacing" format="dimension"/><attr name="nine_maxImageNum" format="integer"/><attr name="nine_single_image" format="dimension"/></declare-styleable>
</resources>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在我们自定义类的构造方法中去获取我们的自定义属性
public NineGridView(Context context) {this(context, null);}public NineGridView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public NineGridView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//单位转换mNineGridViewSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mNineGridViewSpacing, context.getResources().getDisplayMetrics());mSingleImageSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mSingleImageSize, context.getResources().getDisplayMetrics());//获取自定义属性TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NineGridView, defStyleAttr, 0);int count = typedArray.getIndexCount();for (int i=0; i<count; i++){int attr = typedArray.getIndex(i);switch (attr){case R.styleable.NineGridView_nine_gv_spacing:mNineGridViewSpacing = (int) typedArray.getDimension(attr, mNineGridViewSpacing);break;case R.styleable.NineGridView_nine_maxImageNum:mMaxImageNum = typedArray.getInt(attr, mMaxImageNum);break;case R.styleable.NineGridView_nine_single_image:mSingleImageSize = typedArray.getDimensionPixelSize(attr, mSingleImageSize);break;}}typedArray.recycle();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
测量 onMeasure
测量我们控件的宽高等,这里根据上面的分析可知我们需要对单张图片以及非单张图片进行判断。如果是多张图片的话,我们需要根据行、列个数以及每行每列之间的间距值来算出最终的宽、高
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize;int totalWidth = widthSize - getPaddingLeft() - getPaddingRight();if(imagesDatasList != null && imagesDatasList.size() > 0){if(imagesDatasList.size() == 1){//说明是单张图片mWidth = mSingleImageSize > totalWidth ? (int)(totalWidth * 0.8) : mSingleImageSize;mHeight = mWidth;//进一步根据高度来调整显示,控制最大显示范围if(mHeight > mSingleImageSize){float ratio = mSingleImageSize * 1.0f / mHeight;mWidth = (int) (mWidth * ratio);mHeight = mSingleImageSize;}}else{//说明不止一张mWidth = mHeight = (totalWidth - mNineGridViewSpacing*(columnCount - 1)) / columnCount;}widthSize = mWidth * columnCount + mNineGridViewSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();heightSize = mHeight * rowCount + mNineGridViewSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();setMeasuredDimension(widthSize, heightSize);}else{heightSize = widthSize;setMeasuredDimension(widthSize, heightSize);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
其实,实际开发中可能服务器返回的还有图片的宽高比例,那么我们可以根据这个宽高比例还算出图片的高度等等,具体情况根据业务来定。
确定位置 onLayout
既然是自定义ViewGroup,那么onLayout()方法肯定少不了。它是用来确定子view的位置的
@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {if(imagesDatasList == null) return;int childCount = imagesDatasList.size() > mMaxImageNum ? mMaxImageNum : imagesDatasList.size();for(int i = 0; i < childCount; i++){ImageView childView = (ImageView) getChildAt(i);if(mAdapter != null){mAdapter.onDisplayImage(getContext(), childView, imagesDatasList.get(i));//得到图片数组中的每一张图片}//通过此方式来确定宽高是否累加、换行,一并判断了int columnNum = i % columnCount;int rowNum = i / columnCount;left = (mWidth + mNineGridViewSpacing ) * columnNum + getPaddingLeft();//根据i来决定left, i=0 left=getPaddingLeft i=1表示第二个childView的left=第一个child的宽+间距+内间距top = (mHeight + mNineGridViewSpacing) * rowNum + getPaddingTop();right = left + mWidth;bottom = top + mHeight;childView.layout(left, top, right, bottom);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
接下来就是我们的adapter跟这个自定义控件的交互了
public void setData(List<String> mDataLists){//有无数据决定着九宫格控件的显示与隐藏if(mDataLists == null || mDataLists.isEmpty()){this.setVisibility(View.GONE);return;}else{this.setVisibility(View.VISIBLE);}//获取图片数量,图片的数量有可能大于规定的最大数量9张int newImgCount = mDataLists.size() > mMaxImageNum ? mMaxImageNum : mDataLists.size();//给rowCount、columnCount行列赋值。对图片的分布特殊处理,比如 四张 2 X 2 分布setRowAndColumn(newImgCount);//复用if(imagesDatasList == null){for(int i = 0; i < newImgCount; i++){ImageView iv = imageViewHolder(i);if(iv == null) return;addView(iv, generateDefaultLayoutParams());}} else {int oldImgCount = imagesDatasList.size() > mMaxImageNum ? mMaxImageNum : imagesDatasList.size();//原来的图片数据数量if(newImgCount < oldImgCount){//说明可以复用原来的imageview 移除后面多余的view(imageview)布局removeViews(newImgCount,oldImgCount - newImgCount);}else if(newImgCount > oldImgCount){//说明需要再新new几个imageview提供多余的数据使用for(int i=oldImgCount; i < newImgCount; i++){ImageView iv = imageViewHolder(i);if(iv == null) return;addView(iv, generateDefaultLayoutParams());//将imageview添加到默认宽高的布局中}}}//如果是最后一张,并且图片的数据集总数大于九张,那么就在最后一张图片上展示还剩图片的数量if (mDataLists.size() > mMaxImageNum){View child = getChildAt(mMaxImageNum - 1);//九宫格的最后一张图片if(child instanceof MyGridViewItemImageView){MyGridViewItemImageView imageView = (MyGridViewItemImageView) child;imageView.setImagesCount(mDataLists.size());}}imagesDatasList = mDataLists;//当view布局内容发生改变后调用此方法会重新走onMeasure()和onLayout()方法,重新调整布局//requestLayout(); //因为addViews()方法内部已经有requestLayout()了}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
我们需要在展示数据的adapter中去调用此方法。这里为了调用的简洁,我们额外定义了一个抽象类。
public abstract class NineGridViewAdapter {protected abstract void onDisplayImage(Context context, ImageView iv, String url);protected void onItemImageClick(Context context, ImageView iv, int position, List<String> list){}protected ImageView generateImageView(Context context){MyGridViewItemImageView imageView = new MyGridViewItemImageView(context);//设置图片的点击背景颜色变化效果imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);return imageView;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
这里需要特别说明的是generateImageView()方法,这里面我们new出来我们的九宫格中的一张张图片。同时,还就点击图片变暗的点击效果以及超过9张后的效果处理。下面就具体看看
/*** 设置图片点击时有个背景色,松手后移除背景色 类似XML文件设置selector效果*/public class MyGridViewItemImageView extends ImageView{private int textColor = Color.parseColor("#FFFFFF");private int textSize;private int imageViewBg = 0x88000000;private int imagesCount;//总的数据集private String textDesc;//要绘制的文字private Paint paint;public MyGridViewItemImageView(Context context) {this(context, null);}public MyGridViewItemImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyGridViewItemImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 32, context.getResources().getDisplayMetrics());//初始化画笔iniPaint();}private void iniPaint() {paint = new Paint();paint.setAntiAlias(true);paint.setDither(true);paint.setColor(textColor);paint.setTextSize(textSize);paint.setTextAlign(Paint.Align.CENTER);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if(imagesCount > 9){canvas.drawColor(imageViewBg);//背景颜色Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();float baseLine = getHeight() / 2 - (fontMetrics.bottom + fontMetrics.top) / 2;canvas.drawText(textDesc, getWidth() / 2, baseLine, paint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Drawable drawable = getDrawable();if(drawable != null){//drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);drawable.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);ViewCompat.postInvalidateOnAnimation(this);}break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:Drawable drawableUp = getDrawable();if(drawableUp != null){//drawableUp.mutate().clearColorFilter();drawableUp.clearColorFilter();ViewCompat.postInvalidateOnAnimation(this);}break;}return super.onTouchEvent(event);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();//将drawable对象置空setImageDrawable(null);}public int getImagesCount() {return imagesCount;}public void setImagesCount(int imagesCount) {this.imagesCount = imagesCount;textDesc = "+"+(imagesCount - 9);invalidate();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
需要特别说明的一点是drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);这个方法。根据jeasonlzy大神的解释是,如果这样写的话在部分机型上会出问题,所以他给出了一个解决方案。由于现有测试机种类有限,目前还没有出现他说的这种问题。不管了,先给出两种实现方式。
接下来,再来看看我们的adapter是如何调用交互的
/*** 展示数据的适配器 adapter*/public class RecyclerViewDatasAdapter extends RecyclerView.Adapter<RecyclerViewDatasAdapter.ImageViewHolder>{private Context context;private List<ImagesBean> lists;private LayoutInflater inflater;public RecyclerViewDatasAdapter(Context context, List<ImagesBean> lists) {this.context = context;this.lists = lists;inflater = LayoutInflater.from(context);}@Overridepublic ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return new ImageViewHolder(inflater.inflate(R.layout.item_layout, parent, false));}@Overridepublic void onBindViewHolder(ImageViewHolder holder, int position) {holder.iv.setImageResource(R.mipmap.ic_launcher);holder.tvName.setText(lists.get(position).getName());holder.tvDesc.setText(lists.get(position).getDesc());holder.nineGridView.setData(lists.get(position).getImgsUrl());//将图片集合传到我们的自定义九宫格控件中}@Overridepublic int getItemCount() {return null != lists ? lists.size() : 0;}public class ImageViewHolder extends RecyclerView.ViewHolder{private ImageView iv;private TextView tvName;private TextView tvDesc;private NineGridView nineGridView;private NineGridViewAdapter nineGridViewAdapter = new NineGridViewAdapter() {@Overrideprotected void onDisplayImage(Context context, ImageView iv, String url) {//Glide.with(context).load(url).into(iv);Picasso.with(context).load(url).into(iv);}@Overrideprotected ImageView generateImageView(Context context) {return super.generateImageView(context);}@Overrideprotected void onItemImageClick(Context context, ImageView iv, int position, List<String> list) {Toast.makeText(context, "你点击了 position = " + position, Toast.LENGTH_SHORT).show();//super.onItemImageClick(context, iv, position, list);}};public ImageViewHolder(View itemView) {super(itemView);iv = (ImageView) itemView.findViewById(R.id.iv);tvName = (TextView) itemView.findViewById(R.id.tv_name);tvDesc = (TextView) itemView.findViewById(R.id.tv_desc);nineGridView = (NineGridView) itemView.findViewById(R.id.nineGridView);nineGridView.setDataAdapter(nineGridViewAdapter);}}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
这里需要特别说明一下,大家可以看到这里我采用的是Glide加载图片,在测试中发现当图片大于九张时会出现图片部分被放大(也就是所谓的变形),开始我以为是自定义控件哪写的有问题,但是经过反复测试,发现是Glide加载的问题。按照网上说的方式,比如关掉加载动画等,发现并不能解决。Glide的源码着实太复杂,所以目前并不能很好的解决这个问题。以后有时间再继续研究吧,目前我换用了其它的图片加载框架就没问题了。
顺便把我们的实体类也贴出来吧
/*** 实体类*/public class ImagesBean implements Serializable{private static final long serialVersionUID = 370114387259948705L;private int imgs;private String name;private String desc;private ArrayList<String> imgsUrl;//图片数组集合public ImagesBean(String name, String desc, ArrayList<String> imgsUrl) {this.name = name;this.desc = desc;this.imgsUrl = imgsUrl;}public int getImgs() {return imgs;}public void setImgs(int imgs) {this.imgs = imgs;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public ArrayList<String> getImgsUrl() {return imgsUrl;}public void setImgsUrl(ArrayList<String> imgsUrl) {this.imgsUrl = imgsUrl;}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
最后是我们的MainActivity
public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private RecyclerViewDatasAdapter adapter;private List<ImagesBean> mDatas;private String[] imgsUrl = {"https://pic4.zhimg.com/02685b7a5f2d8cbf74e1fd1ae61d563b_xll.jpg","https://pic4.zhimg.com/fc04224598878080115ba387846eabc3_xll.jpg","https://pic3.zhimg.com/d1750bd47b514ad62af9497bbe5bb17e_xll.jpg","https://pic4.zhimg.com/da52c865cb6a472c3624a78490d9a3b7_xll.jpg","https://pic3.zhimg.com/0c149770fc2e16f4a89e6fc479272946_xll.jpg","https://pic1.zhimg.com/76903410e4831571e19a10f39717988c_xll.png","https://pic3.zhimg.com/33c6cf59163b3f17ca0c091a5c0d9272_xll.jpg","https://pic4.zhimg.com/02685b7a5f2d8cbf74e1fd1ae61d563b_xll.jpg","https://pic4.zhimg.com/fc04224598878080115ba387846eabc3_xll.jpg","https://pic3.zhimg.com/d1750bd47b514ad62af9497bbe5bb17e_xll.jpg","https://pic4.zhimg.com/da52c865cb6a472c3624a78490d9a3b7_xll.jpg","https://pic3.zhimg.com/0c149770fc2e16f4a89e6fc479272946_xll.jpg",};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);recyclerView = (RecyclerView) findViewById(R.id.recyclerview);recyclerView.setLayoutManager(new LinearLayoutManager(this));//测试数据mDatas = new ArrayList<>();for(int i=0; i < 12; i++){ArrayList<String> imgs = new ArrayList<>();imgs.addAll(Arrays.asList(imgsUrl).subList(0, i % 12 + 1));ImagesBean bean = new ImagesBean("我是bean", "测试九宫格图片,只是测试demo,只是测试demo",imgs);mDatas.add(bean);}adapter = new RecyclerViewDatasAdapter(this, mDatas);recyclerView.setAdapter(adapter);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
最后,非常感谢laobie大牛,此项目就是参考他的项目。如果大家觉得还有什么问题的话,欢迎留言交流。
仿微信朋友圈【九宫格的实现】相关推荐
- Android 实现仿微信朋友圈九宫格图片+NineGridView+ImageWatcher(图片查看:1.预览,2.拖动,3.放大,4.左右滑动,5.长按保存到手机)的功能
一.测试 实现: 二.添加依赖包: implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.recycl ...
- Android(安卓)高仿微信朋友圈九宫格列表
目前基本复现微信的朋友圈的浏览效果 直接上效果图吧! 感觉不错的话多多支持吧 由于代码比较多,目前只将核心的适配器代码放出来,如果需要全部的话,下载源码吧,源码地址在最后 适配器代码: private ...
- android从九宫格全屏预览,仿微信朋友圈展示图片的九宫格图片展示控件,支持点击图片全屏预览大图...
AssNineGridView 仿微信朋友圈展示图片的九宫格图片展示控件,支持点击图片全屏预览大图(可自定义). 写在前面 这是一个九宫格控件,本来是很久之前就写好了,现在才开源出来,也是看了很多优秀 ...
- Android实现仿微信朋友圈发布动态(拍照、图库选择、照片压缩、显示、保存、缩略图、点击缩略图删除对应文件等)
原址: http://blog.csdn.net/zhang3776813/article/details/52092591 /*** 仿微信朋友圈发布动态* 拍照或图库选择 * 压缩图片并保存**/ ...
- Android实现仿微信朋友圈发布动态(拍照、图库选择、照片压缩、显示、保存、缩略图、点击缩略图删除对应文件等)附源码
原创作品,转载请注明出处:http://blog.csdn.net/zhang3776813/article/details/52092591 最近项目需求中要用到类似微信朋友圈发布 ...
- Android 仿微信朋友圈添加图片
github地址(欢迎下载Demo) https://github.com/zhouxu88/WXCircleAddPic 老习惯,先上图,着急用的朋友,直接带走Demo,先拿来用吧,毕竟老板催的紧, ...
- IOS仿微信朋友圈的日期处理
IOS仿微信朋友圈的日期处理 经常刷微信朋友圈的朋友,都能看到该条信息是什么发送的,有刚刚,有昨天,有xxxx年xx月xx日发送的,今天我们就探究微信内部是怎么样去做的. 对于传入的时间,一般是从服务 ...
- android 仿微信朋友圈 评论,2020年android 仿微信朋友圈 评论
2020年android 仿微信朋友圈 评论 1.如果有人问我:那些艰难的岁月你是怎么熬过来的?我想我只有一句话回答:我有一种强大的精神力量支撑着我,这种力量名字叫"想死又不敢" ...
- php仿微信朋友圈网站源码,Smobiler仿微信朋友圈的消息代码实例
这篇文章主要介绍了.Net语言Smobiler开发平台如何仿微信朋友圈的消息样式?本文为大家揭晓答案 最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xam ...
最新文章
- python直方图hist用法参数详解
- Redis的复制详解
- 多态及其内部原理剖析
- 编译原理之正则表达式
- mysql平均值函数保留两位小数点_用sql的avg(score)求完平均值后,保存两位小数的方法(用于查询或视图)...
- 融于心而表于行 之 磁盘的管理方式
- 图片服务 - thumbor自定义检测
- contenteditable属性让div也可以当做输入框
- 树莓派3b接收USB串口数据并解析处理
- Git21天打卡day14-查看文件改动内容git diff
- 计算机控制液压同步顶升系统,PLC四点多点同步顶升系统,同步液压顶升系统
- 简单OR复杂?机器学习专家为你解密企业风险量化模型
- 贝叶斯 定理_贝叶斯定理:批判性思维框架
- SpringBoot集成Liquibase
- 配置环境变量path
- python定义匿名函数关键字_Python匿名函数
- pod重启策略和状态解释
- 新生研讨课作业 程序框图
- 太准了这十个心理暗示
- 2019年8月8日星期四(系统编程)
热门文章
- 病毒RNA提取:EpiQuik 病毒RNA提取纯化试剂盒方案
- 【存档】精确的过零检测电路
- 【小程序源码】视频壁纸支持多种分类短视频另外也有静态壁纸
- 使用车辆座椅上的压电传感器无创检测呼吸和心率
- 继电器分类及性能对比
- Docker/Docker-Compose部署Django
- 打包php程序为安装文件,zblogphp主题和插件怎么打包成.zba格式文件
- tipask 3.5 出错get_class() expects parameter 1 to be object 解决方案及说明
- Apollo 3.0来了!百度自动驾驶硬件系统全解读
- 华为云教程(云硬盘EVS)