android-自定义View-GridListView(仿Q空间好友动态列表图片展示方式)
2014-09-25 08:51
756 查看
转载请注明出处:http://write.blog.csdn.net/postedit/39545465(我爱吃鱼的博客)
先来看下实现的效果图:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/27/63382e8f8fadda5acd48fa2207337f33)
demo做的比较简单;一个列表,item项根据展示的图片个数区分是按九宫格图片展示样式,还是单张原图样式展示。
接下来说下我这边的实现方式:自定义GridListView继承ViewGroup作为列表Item项,动态添加图片,重写方法onMeasure(计算大小)、onLayoutout(位置布局)方法,对图片进行相应的设置来达到上图的效果。
或许比较多的人会有类似的需求,谷歌查询到的比较多的实现是ListView嵌套GridView,重写GridView的onMeasure方法计算大小,实现列表九宫格;这样算是满足需求,但是一个列表需要维护两个adapter(适配器),再一个列表限定在九宫格样式上;实现自己的GridListView同样是满足需求,列表展现样式上也可以实现多种状态,由于在列表adapter(适配器)getView方法中对GridListView进行复用了,所以性能上面也应该不会消耗太多(这点只是猜测,没有具体去测试)。
接下来看下具体的代码实现:
1、GridListView.java
对上面进行解释下;setData中提供给外部调用,作设置数据用,在方法内实现根据数据添加相应的ImageView,并获取我们布局所需要的相关参数;
onMeasure方法计算本身View和子View(这里既是添加进来相应的ImageView)的大小,其中要理解下MeasureSpec和其对应的相应三种模式,不了解的同学可以自行谷歌、度娘,有很多的相关资料;这边简单说下,MeasureSpec一种整形的数据结构,高2位对应相应的三种模式UNSPECIFIED(不限制)、EXACTLY(精确的)、AT_MOST(最多或最大是多少),后30位对应大小;内部实现方式是对整形数据进行的相应位运算得到相应的模式及大小;最终View也是通过MeasureSpec的模式和大小来确定大小的。
上面计算大小的方法分两种情况;单张图,不对图片大小做限制(UNSPECIFIED),图片要多大,就给多大(其实这边应该是只能给到GridListView大小的,先简单做吧);计算出子View后反过来计算GridListView的大小;多张图,对图片做精确大小计算(EXACTLY),然后根据子View的行数得到GridListView的大小。
计算完大小,接下来就是对子View进行位置设置了。
布局设置也是分两种情况;单张图,直接填充式的充满GridListView;多张图根据一个个追加的方式设置,一行满三张进行换行。(这边布局设置的时候应该考虑padding等值的,这边先忽略了)
以上就是GridListView的大体实现了,有什么不清楚的可以看给出的demo项目。
接下来是使用:
xml布局文件item_grid.xml
xml布局文件activity_main.xml
MainActivity.java
demo下载地址:http://pan.baidu.com/s/1fa0ua
先来看下实现的效果图:
demo做的比较简单;一个列表,item项根据展示的图片个数区分是按九宫格图片展示样式,还是单张原图样式展示。
接下来说下我这边的实现方式:自定义GridListView继承ViewGroup作为列表Item项,动态添加图片,重写方法onMeasure(计算大小)、onLayoutout(位置布局)方法,对图片进行相应的设置来达到上图的效果。
或许比较多的人会有类似的需求,谷歌查询到的比较多的实现是ListView嵌套GridView,重写GridView的onMeasure方法计算大小,实现列表九宫格;这样算是满足需求,但是一个列表需要维护两个adapter(适配器),再一个列表限定在九宫格样式上;实现自己的GridListView同样是满足需求,列表展现样式上也可以实现多种状态,由于在列表adapter(适配器)getView方法中对GridListView进行复用了,所以性能上面也应该不会消耗太多(这点只是猜测,没有具体去测试)。
接下来看下具体的代码实现:
1、GridListView.java
package com.pig.android.gridlist; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ImageView.ScaleType; public class GridListView extends ViewGroup { /** * 子项大小 */ private int mItemWidth; /** * 间隙大小 */ private int mPadding = 0; /** * 列数 */ private int mClunNum = 0; /** * 数据源 */ private List<Integer> mData; public GridListView(Context context, AttributeSet attrs) { super(context, attrs); mPadding = (int) (4*getResources().getDisplayMetrics().density); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 宽度获取 int widthSize = MeasureSpec.getSize(widthMeasureSpec); mItemWidth = (widthSize - 2*mPadding)/3; // 测量子View int childCount = getChildCount(); if(1 == childCount) { View child = getChildAt(0); child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int height = child.getMeasuredHeight(); setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); }else { for(int i=0; i<childCount; i++) { View child = getChildAt(i); child.measure(MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY)); } // 测量当前View int rowNum = childCount/mClunNum; rowNum = childCount%mClunNum == 0 ? rowNum : rowNum + 1; int heightSize = (rowNum - 1) * mPadding + rowNum*mItemWidth; setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY)); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); if(count == 1) { View view = getChildAt(0); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); }else { layoutChild(count, mClunNum); } } private void layoutChild(int childCount, int clunNum) { if(clunNum == 0) { return; } for(int i=0; i<childCount; i++) { View childView = getChildAt(i); int xPosition = i%clunNum; int yPosition = i/clunNum; int l = mPadding*xPosition + mItemWidth*xPosition; int t = mPadding*yPosition + mItemWidth*yPosition; childView.layout(l, t, l+mItemWidth, t+mItemWidth); } } /** * 设置数据源 * @param data */ public void setData(List<Integer> data) { this.mData = data; if(null == data) { return; } int size = data.size(); if(size == 0) { return; } if(size == 1) { mClunNum = 1; }else { mClunNum = 3; } removeAllViews(); // 添加子View for(int i=0; i<mData.size(); i++) { ImageView imvItem = new ImageView(getContext()); imvItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); imvItem.setImageResource(mData.get(i)); imvItem.setBackgroundResource(R.drawable.bg_imv); imvItem.setScaleType(ScaleType.CENTER_CROP); addView(imvItem); } } }
对上面进行解释下;setData中提供给外部调用,作设置数据用,在方法内实现根据数据添加相应的ImageView,并获取我们布局所需要的相关参数;
onMeasure方法计算本身View和子View(这里既是添加进来相应的ImageView)的大小,其中要理解下MeasureSpec和其对应的相应三种模式,不了解的同学可以自行谷歌、度娘,有很多的相关资料;这边简单说下,MeasureSpec一种整形的数据结构,高2位对应相应的三种模式UNSPECIFIED(不限制)、EXACTLY(精确的)、AT_MOST(最多或最大是多少),后30位对应大小;内部实现方式是对整形数据进行的相应位运算得到相应的模式及大小;最终View也是通过MeasureSpec的模式和大小来确定大小的。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 宽度获取 int widthSize = MeasureSpec.getSize(widthMeasureSpec); mItemWidth = (widthSize - 2*mPadding)/3; // 测量子View int childCount = getChildCount(); if(1 == childCount) { View child = getChildAt(0); child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int height = child.getMeasuredHeight(); setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); }else { for(int i=0; i<childCount; i++) { View child = getChildAt(i); child.measure(MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mItemWidth, MeasureSpec.EXACTLY)); } // 测量当前View int rowNum = childCount/mClunNum; rowNum = childCount%mClunNum == 0 ? rowNum : rowNum + 1; int heightSize = (rowNum - 1) * mPadding + rowNum*mItemWidth; setMea 4000 suredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY)); } }
上面计算大小的方法分两种情况;单张图,不对图片大小做限制(UNSPECIFIED),图片要多大,就给多大(其实这边应该是只能给到GridListView大小的,先简单做吧);计算出子View后反过来计算GridListView的大小;多张图,对图片做精确大小计算(EXACTLY),然后根据子View的行数得到GridListView的大小。
计算完大小,接下来就是对子View进行位置设置了。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); if(count == 1) { View view = getChildAt(0); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); }else { layoutChild(count, mClunNum); } } private void layoutChild(int childCount, int clunNum) { if(clunNum == 0) { return; } for(int i=0; i<childCount; i++) { View childView = getChildAt(i); int xPosition = i%clunNum; int yPosition = i/clunNum; int l = mPadding*xPosition + mItemWidth*xPosition; int t = mPadding*yPosition + mItemWidth*yPosition; childView.layout(l, t, l+mItemWidth, t+mItemWidth); } }
布局设置也是分两种情况;单张图,直接填充式的充满GridListView;多张图根据一个个追加的方式设置,一行满三张进行换行。(这边布局设置的时候应该考虑padding等值的,这边先忽略了)
以上就是GridListView的大体实现了,有什么不清楚的可以看给出的demo项目。
接下来是使用:
xml布局文件item_grid.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" > <TextView android:id="@+id/txv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="我是爱吃鱼的呀" /> <ImageView android:id="@+id/imv_icon" android:layout_width="72dp" android:layout_height="72dp" android:layout_margin="8dp" android:layout_below="@id/txv_title" android:layout_weight="0.0" android:contentDescription="@null" android:scaleType="centerCrop" android:src="@drawable/ic_avatar" /> <TextView android:id="@+id/txv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignTop="@id/imv_icon" android:layout_toRightOf="@+id/imv_icon" android:textSize="14sp" android:textColor="@android:color/darker_gray" android:singleLine="true" android:text="人生何其短,唯本心难持,蹉跎岁月! \n 抬头看,看前方路;低头看,看脚下路……" /> <com.pig.android.gridlist.GridListView android:id="@+id/grd_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/txv_content" android:layout_margin="8dp" android:layout_toRightOf="@id/imv_icon" android:layout_weight="1.0" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/grd_item" android:text="要来评论嘛?" android:textColor="@android:color/darker_gray" android:textSize="14sp"/> <TextView android:id="@+id/txv_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_below="@id/grd_item" android:textColor="@android:color/darker_gray" android:textSize="14sp"/> </RelativeLayout>
xml布局文件activity_main.xml
<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/lsv_grid" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:listSelector="@android:color/transparent"/>
MainActivity.java
package com.pig.android.gridlist; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends Activity { private List<List<Integer>> mData; private int[] mRes = new int[] {R.drawable.img_1, R.drawable.img_2, R.drawable.img_3, R.drawable.img_4, R.drawable.img_5, R.drawable.img_6, R.drawable.img_7, R.drawable.img_8, R.drawable.img_9, R.drawable.img_10, R.drawable.img_11, R.drawable.img_12, R.drawable.img_13, R.drawable.img_13, R.drawable.img_14}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView lsvGrid = (ListView) findViewById(R.id.lsv_grid); mData = loadTestData(); lsvGrid.setAdapter(new GridTestAdapter()); } private List<List<Integer>> loadTestData() { List<List<Integer>> result = new ArrayList<List<Integer>>(); for(int i=0; i<100; i++) { List<Integer> item = new ArrayList<Integer>(); int count = new Random().nextInt(10) + 1; Log.e("ZLH", ""+count); for(int j=0; j<count; j++) { item.add(mRes[new Random().nextInt(mRes.length-1) + 1]); } result.add(item); } return result; } class GridTestAdapter extends BaseAdapter { public GridTestAdapter() { } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(null == convertView) { convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_grid, null); holder = new ViewHolder(); holder.grdItem = (GridListView) convertView.findViewById(R.id.grd_item); holder.txvNum = (TextView) convertView.findViewById(R.id.txv_num); convertView.setTag(holder); }else { holder = (ViewHolder) convertView.getTag(); } List<Integer> data = mData.get(position); holder.grdItem.setData(data); holder.txvNum.setText("图片数:" + data.size()); return convertView; } class ViewHolder { TextView txvNum; GridListView grdItem; } } }
demo下载地址:http://pan.baidu.com/s/1fa0ua
相关文章推荐
- android利用recyclerview展示带有日期的图片列表
- Android可以动态控制图片显示区域的自定义ImageView
- 【Android】Glide结合Recyclerview(也适用于Listview)实现列表滑动的时候图片不加载,滑动停止的时候加载(已修正Listview部分以及排版)
- Android 网络请求的图片用自定义圆形View展示
- android 自定义ListView显示微信聊天好友列表
- RecyclerView+ImageLoader图片列表的加载与展示【从listview加载图片性能优化引出的解决方法】
- Android 动态设置列表样式,不用ListView或者RecyclerView
- Android Kotlin(二)—— Kotlin与Retrofit进行网络请求RecyclerView展示图片列表
- android结合异步任务,动态加载图片,Json解析数据展示在ListView,并且实现按日期分类展示,借口回调
- Android 控件使用教程(三)—— NineGridImageView 展示图片
- Android HorizontalScrollView 水平滑动 在listview上面动态添加图片
- 最近较流行的效果 Android自定义View实现倾斜列表/图片
- android 实现listview动态加载列表项
- 自定义ListView FastScroller滑块图片 以及 android:fastScrollEnabled="true" 不起作用
- Android Listview异步动态加载网络图片
- android ListView布局之三(使用自定义的Adapter绑定数据,通过contextView.setTag绑定数据)有按钮的ListView
- Android ExpandableListView 展开列表控件(手机QQ好友列表) (二)控件版
- android 实现listview动态加载列表项
- [Android开发] 代码code设置9.png/9-patch 图片背景后,此view中的TextView等控件显示不正常(常见于listview中)
- Android ExpandableListView 展开列表控件(手机QQ好友列表)