您的位置:首页 > 移动开发 > 微信开发

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流式布局控件也同样可以实现。

本文各链接地址需自备梯子。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图片 微信 布局 动态
相关文章推荐