Android开发-优雅的实现动态图片排版(类似微信图片展示效果)
2016-11-04 00:42
891 查看
介绍
效果展示
说明
上面的图片排版- 实现了动态布局,针对不同图片的数量展示不同的排版布局。效果类似微信朋友圈的图片排版,效果略有不同。
- 正方形的图片控件,高度会随着宽度一起变化。
实现这样的布局有很多种思路,只是什么样的方式更优雅。本文提供一种相对优雅的方式供大家参考。
思路
首先,优雅的方式肯定不是定义多种的布局,然后根据图片数量,判断显示那种布局。这样的代码不优雅。其次,也没有必要得到屏幕宽度或者父控件宽度,根据数量计算图片的宽高,然后代码调整子控件宽高。
针对上文提到的两个方面,分别的应对是:
动态代码生成控件填充。
父控件宽度固定高度动态,然后子控件宽度根据Weight权重进行均分。
代码
GridLayout
Android系统控件中,GridLayout(网格布局)能够轻松的实现上文的功能需求。先上代码:
<android.support.v7.widget.GridLayout android:id="@+id/gridlayout_post" android:layout_width="match_parent" android:layout_height="wrap_content" app:columnCount="3"> <ImageView android:layout_height="200dp" app:layout_columnWeight="1" tools:src="@android:color/dim_foreground_dark" /> <ImageView android:layout_height="200dp" app:layout_columnWeight="1" tools:src="@android:color/dim_foreground_dark" /> </android.support.v7.widget.GridLayout>
代码说明:
这里为了向下兼容使用的是v7support包中的GridLayout,所以使用到有关的参数配置是
app:开头的。
排版的关键在与
app:columnCount="列数"定义了列数,这样子控件就会自动换行到下一行。(行数 app:rowCount=”行数”这里没有使用到)
最关键的在于子控件的
app:layout_columnWeight="1",设置在列(水平方向上)上的权重。每个子控件都是1,这样就会当子控件数量为1时占满父控件,当数量为2时二等分,数量为3时三等分,3以上时占据下一行的三分之一。
特别说明:0dp
GridLayout的权重分配策略和LinearLayout是一样的。所以这就涉及到LinearLayout设置layout_weight时,使用layout_width(或者layout_height)的设置问题和相关的LinearLayout绘制性能问题,具体原因就自行google。直接给出结论:
Use a layout_width (layout_height)of 0dip instead of match_parent(wrap_content) for better performance
翻译:使用0dp会有更好的性能。(而且这样权重才是正真的,子控件weight值 / 父控件LinearLayout内所有控件的weight值的和)
所以上面的xml布局文件中:所有的子控件应该这写:
<ImageView android:layout_height="200dp" android:layout_width="0dp"//很关键的设置! app:layout_columnWeight="1" tools:src="@android:color/dim_foreground_dark" />
正方形控件
前文提到:正方形的图片控件,高度会随着宽度一起变化
刚才的
app:layout_columnWeight="1"已经能得到动态的控件宽度。想要正方形的图片控件,相信大家很容易就想到类似的代码:
imageView.getLayoutParams().height=imageView.getLayoutParams().height;
直接把宽赋值给高。
其实这是也是一个大坑。如果直接运行上面代码,结果不会如你所愿。
stackoverflow上有一个很好的问题
getWidth() and getHeight() of View returns 0会告诉你答案。
反正从stackoverflow上得知,你想要得到已经配置好的View控件宽度。需要写上一些繁琐但是很有必要的代码。
比如这样:
final View view=//smth; ... view.post(new Runnable() { @Override public void run() { view.getHeight(); //height is ready } });
其实如果只是需要正方形,我有更好的解决方案:重写控件
onMeasure()方法
/** * Created by 李可乐 on 2016/10/16 0016. * 正方形ImageView * 重写onMeasure方法 传入widthMeasureSpec宽作为heightMeasureSpec高 */ public class SquareImageView extends ImageView { public SquareImageView(Context context) { super(context); } public SquareImageView(Context context, AttributeSet attrs) { super(context, attrs); } public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //高度就是宽度值 super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
代码虽然多,但是很思路简单,很容易理解。不再赘述。
最终XML布局
通过上文各种说明,所以最终xml布局文件应该这样写:<android.support.v7.widget.GridLayout android:id="@+id/gridlayout_post" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:paddingLeft="-4.5dp" android:paddingRight="-4.5dp" app:columnCount="3"> <com.zaofeng.ui.SquareImageView android:layout_width="0dp" android:layout_margin="4dp" android:scaleType="centerCrop" app:layout_columnWeight="1" tools:src="@android:color/black" /> <com.zaofeng.ui.SquareImageView android:layout_width="0dp" android:layout_margin="4dp" android:scaleType="centerCrop" app:layout_columnWeight="1" tools:src="@android:color/black" /> <com.zaofeng.ui.SquareImageView android:layout_width="0dp" android:layout_margin="4dp" android:scaleType="centerCrop" app:layout_columnWeight="1" tools:src="@android:color/black"/> </android.support.v7.widget.GridLayout>
这样的固定xml布局已经实现了本文的基本思路。哪说好的动态添加。
动态添加
在xml布局文件中其实GridLayout已经帮我们安排好了子控件排版顺序。如果是动态添加的,就需要我们自己控制子控件的排版顺序。以及刚才说明的0dp问题。/** * 动态添加控件 * * @param imageModels 图片集合 */ public void updateViewGroup(ArrayList<ImageModel> imageModels) { gridlayoutPost.removeAllViews();//清空子视图 防止原有的子视图影响 int columnCount=gridlayoutPost.getColumnCount();//得到列数 int marginSize = PixelUtils.dp2px(mContext, 4);//得到经过dp转化的margin值 //遍历集合 动态添加 for (int i = 0, size = imageModels.size(); i < size; i++) { GridLayout.Spec rowSpec = GridLayout.spec(i / columnCount);//行数 GridLayout.Spec columnSpec = GridLayout.spec(i % columnCount, 1.0f);//列数 列宽的比例 weight=1 ImageView imageView = new SquareImageView(mContext); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); //由于宽(即列)已经定义权重比例 宽设置为0 保证均分 GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(new ViewGroup.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT)); layoutParams.rowSpec=rowSpec; layoutParams.columnSpec=columnSpec; layoutParams.setMargins(marginSize, marginSize, marginSize, marginSize); gridlayoutPost.addView(imageView, layoutParams); } }
说明:
columnCount列数,其实可以根据图片集合的数量,再动态设置,如果为2的倍数时,gridlayoutPost.setColumnCount(2),就可以实现类似微信的效果,4图为2行2列。
rowSpec/columnSpec分别是行/列配置对象,这里调用的时静态方法得到对象,其实源码中有很多的参数配置。这里分别用(除/模)得到子控件的(行/列)索引值,简单的数学就不用再说什么了。
关于Java代码动态添加View的性能
起初感觉相比Xml文件就定义好的视图树关系,我们手动的addView构造视图树可能会增加消耗。但是从LayoutInflater核心视图加载类来看,xml视图文件最终形成View对象和视图树关键还是:
1:反射得到View对象(内部维护构造函数的缓存)
2:addView把View对象添加到父ViewGroup中,也是一次次的add添加。
所以可以放心大胆的使用Java代码添加View对象。
总结
本文旨在合理的使用系统控件GridLayout搭配简单的自定义控件,优雅的实现动态图片排版。思路简单清晰,代码优雅简洁。类似的网格形式布局google推出FlexboxLayout流式布局控件也同样可以实现。
本文各链接地址需自备梯子。
相关文章推荐
- iOS开发之实现图片自动切换(类似android画廊效果)
- iOS开发之实现图片自动切换(类似android画廊效果)
- Android开发 自定义ViewGroup 实现微信九格图功能(图片不同排布不同) 和 一种图片点击变暗效果
- Android开发技巧 图片滚动效果实现
- android开发之滑动效果实现图片浏览_ViewFilpper的使用
- 【Android UI设计与开发】第14期:顶部标题栏(五)两种方式实现仿微信标题栏弹窗效果
- Android用GridView排列相册,实现微信选择图片效果
- Android开发--Matrix(一)--实现图片的动态放大缩小
- Android开发重写Animation实现下拉图片后弹射回去效果示例
- 【Android UI设计与开发】第14期:顶部标题栏(五)两种方式实现仿微信标题栏弹窗效果
- Android 使用ViewPager实现类似gallery画廊的效果(画廊效果之ViewPager显示多个图片)
- Android开发--Matrix(一)--实现图片的动态放大缩小
- android实现类似微信的开门效果
- Android应用开发之图片热点效果实现
- android实现图片右上方出现数字,类似邮箱的未读邮件效果
- 【Android UI设计与开发】第14期:顶部标题栏(五)两种方式实现仿微信标题栏弹窗效果
- 【Android UI设计与开发】第14期:顶部标题栏(五)两种方式实现仿微信标题栏弹窗效果
- Android 类似IPhone图片点击效果实现,点击logo变暗
- android开发之实现动态打勾效果(DrawHookView)
- Android开发模仿微信发表朋友圈图片多选及图片浏览实现-Assur