android-自定义View-GridListView(仿Q空间好友动态列表图片展示方式)

转载请注明出处:http://write.blog.csdn.net/postedit/39545465(我爱吃鱼的博客)

先来看下实现的效果图:

   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;
			setMeasuredDimension(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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值