这几天还是在做那个项目 有一个部分是需要有一个类似微信朋友圈那样的功能 开始自己实现是用RecycleView嵌套RecycleView 然后已经把别的弄好了 动态图片那块还没有加上结果我不会搞也没有找到栗子 然后就换了一个思路 看到有listview+gridview的栗子就照着做了一个
先看一下现在的效果
呐 这个是listview嵌套Gridview实现的 评论那些我在recycleview里面做好了之后再加 觉得 把这个嵌套弄好了真是超级开心呐;
嗯 就再回顾一下说一下思路;
1 listview适配器
2 每一个item中有一个gridview;
3 gridview也需要一个适配器
4 图片个数问题
5 图片加载和避免oom
6 我最不知道的就是gridview的adapter要放在哪里用
嗯嗯 大概这些问题都解决了的话这个demo也就ok了
这里加载用到了afinal框架 嗯嗯 我代码里面注释的很详细 因为git上的是给eclipse的 所以这个是jar包的链接afinal_0.5.1_bin.jar
可以下载复制到AS里
然后先看一下工程结构
然后 我的顺序是先从里面往外写 最后写的MainActivity
不过 就从MainActivity里面看
package com.example.katherine_qj.listviewgridview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.example.katherine_qj.listviewgridviewadapter.GridViewAdapter;
import com.example.katherine_qj.listviewgridviewadapter.ListViewAdapter;
import com.example.katherine_qj.listviewwithgridbean.GridTest;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List listgrid;
private ListViewAdapter listViewAdapter;
private ListView listView;
private String imgs1;
private String imgs3;
private String imgs2;
private String imgs4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listgrid = new ArrayList();
init();
initData();
}
private void init(){
listView = (ListView)findViewById(R.id.listview);
}
private void initData(){
imgs1="http://pic60.nipic.com/file/20150211/18733170_145247158001_2.jpg#"
+"http://mvimg1.meitudata.com/566507ca1bcc65451.jpg";
imgs2="http://rj1.douguo.net/upload/diet/6/6/8/666f180617cab130bef1dea9fb3f7fe8.jpg#" +
"http://img4.duitang.com/uploads/blog/201312/01/20131201120117_F5QXY.jpeg#"+
"http://www.sh.xinhuanet.com/133071048_13905438457501n.jpg";
imgs3="http://t2.fansimg.com/uploads2011/02/userid290276time20110205120020.jpg#" +
"http://image81.360doc.com/DownloadImg/2015/01/2113/49316679_9.jpg#" +
"http://image.tianjimedia.com/uploadImages/2014/133/11/EN2I6768CHU1_1000x500.jpg#"+
"http://pic72.nipic.com/file/20150716/6659253_104414205000_2.jpg#"+
"http://pic36.nipic.com/20131222/10558908_214221305000_2.jpg";
imgs4 = "http://h.hiphotos.baidu.com/zhidao/pic/item/5243fbf2b21193133f9f1e3967380cd790238d5f.jpg";
GridTest gridTest = null;
for(int i = 0;i<=3;i++){
gridTest = new GridTest();
switch (i){
case 0:gridTest.setUsername("小仙女");
gridTest.setHeadphoto("http://cdn.duitang.com/uploads/item/201412/12/20141212184514_BJjWy.jpeg");
gridTest.setContent("啊啊啊啊啊啊啊萌死了");
gridTest.setTime("1分钟前");
gridTest.setImage(imgs1);
break;
case 1:
gridTest.setUsername("喵呜是地球吗");
gridTest.setHeadphoto("http://cdn.duitang.com/uploads/item/201501/19/20150119171935_ZkRsZ.thumb.224_0.jpeg");
gridTest.setContent("好吃的日料 超级开心的啦啦啦");
gridTest.setTime("3分钟前");
gridTest.setImage(imgs2);
break;
case 2:
gridTest.setUsername("ill_kaaa");
gridTest.setHeadphoto("http://img5q.duitang.com/uploads/item/201404/03/20140403135406_XFS3M.jpeg");
gridTest.setContent("呐呐呐呐呐");
gridTest.setTime("5分钟前");
gridTest.setImage(imgs3);
break;
case 3:
gridTest.setUsername("Brark");
gridTest.setHeadphoto("http://img3.imgtn.bdimg.com/it/u=3367770910,1075442079&fm=21&gp=0.jpg");
gridTest.setContent("我又在写Bug了 难过");
gridTest.setTime("5分钟前");
gridTest.setImage(imgs4);
break;
}
listgrid.add(gridTest);
}
listViewAdapter = new ListViewAdapter(this,listgrid);
listView.setAdapter(listViewAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this, "点击了第" + (position + 1) + "项", Toast.LENGTH_LONG).show();
}
});
}
}
开始声明了listview的适配器 listview控件
还有四个数据源照片的链接 假数据看个样子
然后oncreat()方法里 主要有两个
init()用来找到控件
initData();很明显就是初始数据的
然后绑定适配器也是在这个里面完成
嗯 每一个item的子项都是一个类去写的
GridTest:
package com.example.katherine_qj.listviewwithgridbean;
import java.io.Serializable;
/**
* Created by Katherine-qj on 2016/6/2.
*/
/*继承 Serializable 接口之后就可以序列化这个对象方便传输数据 很好用*/
public class GridTest implements Serializable{
private String username;
private String headphoto;
private String content;
private String time;
private String image;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getHeadphoto() {
return headphoto;
}
public void setHeadphoto(String headphoto) {
this.headphoto = headphoto;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}
SysUtils类
package com.example.katherine_qj.listviewwithgridutil;
import android.app.Activity;
import android.content.Context;
import android.view.Display;
import android.view.WindowManager;
/**
* Created by Katherine-qj on 2016/6/5.
*/
public class SysUtils {
public static int Dp2Px(Context context, float dp){
final float scale = context.getResources().getDisplayMetrics().density;
/* android context.getResources().getDisplayMetrics()这是获取手机屏幕参数,
后面的density就是屏幕的密度,类似分辨率,但不是
float scale = getResources().getDisplayMetrics().density;
density值表示每英寸有多少个显示点,与分辨率是两个不同的概念。
这个得到的不应该叫做密度,应该是密度的一个比例。不是真实的屏幕密度,而是相对于某个值的屏幕密度。
也可以说是相对密度*/
return (int) (dp * scale + 0.5f);
}
public static int getScreenWidth(Activity activity){
WindowManager windowManager = activity.getWindowManager();
/* WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。*/
Display display = windowManager.getDefaultDisplay();
/* 获取默认的显示对象返回值
默认的Display对象*/
return display.getWidth();
}
}
/*根据手机的分辨率从 dp 的单位 转成为 px(像素)
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
根据手机的分辨率从 px(像素) 的单位 转成为 dp
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}*/
重写的GridView:MyGridView
package com.example.katherine_qj.listviewwithgrid;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridView;
/**
* Created by Katherine-qj on 2016/6/2.
*/
public class MyGridView extends GridView {
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/* AttributeSet 是接收xml中定义的属性信息
super后加参数的是用来调用父类中具有相同形式的构造函数
“this通常指代当前对象,super通常指代父类*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
/*
MeasureSpec.AT_MOST这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
onMeasure方法是测量view和它的内容,决定measured width和measured height的,这个方法由 measure(int, int)方法唤起,子类可以覆写onMeasure来提供更加准确和有效的测量。
其中两个输入参数:
widthMeasureSpec
heightMeasureSpec
分别是parent提出的水平和垂直的空间要求。
这两个要求是按照View.MeasureSpec类来进行编码的。
参见View.MeasureSpec这个类的说明:这个类包装了从parent传递下来的布局要求,传递给这个child。
每一个MeasureSpec代表了对宽度或者高度的一个要求。
每一个MeasureSpec有一个尺寸(size)和一个模式(mode)构成。
MeasureSpecs这个类提供了把一个的元组包装进一个int型的方法,从而减少对象分配。当然也提供了逆向的解析方法,从int值中解出size和mode*/
}
最重要的两个适配器:::::
GridViewAdapter:
package com.example.katherine_qj.listviewgridviewadapter;
import android.app.Activity;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import com.example.katherine_qj.listviewgridview.R;
import com.example.katherine_qj.listviewwithgridutil.SysUtils;
import net.tsz.afinal.FinalBitmap;
import java.util.ArrayList;
/**
* Created by Katherine-qj on 2016/6/2.
*/
public class GridViewAdapter extends BaseAdapter{
Activity context;
ArrayList list;
public Bitmap bitmaps[];
private FinalBitmap finaImageLoader;
private int wh;
public GridViewAdapter(Activity context,ArrayList list){
this.context = context;
this.list = list;
this.wh=(SysUtils.getScreenWidth(context)-SysUtils.Dp2Px(context, 99))/3;
this.finaImageLoader = FinalBitmap.create(context);/*获取一个FinalBitmap对象*/
this.finaImageLoader.configLoadfailImage(R.drawable.loding);/*图片加载完成前显示的图片*/
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
Holder holder;//java内部类
if(view==null){
view = LayoutInflater.from(context).inflate(R.layout.item_gridview,null);
holder = new Holder();
holder.imageView = (ImageView)view.findViewById(R.id.imagevView);
view.setTag(holder);
/*首先我们要知道setTag方法是干什么的,他是给View对象的一个标签,标签可以是任何内容,
我们这里把他设置成了一个对象,因为我们是把item_gridview.xml的元素抽象出来成为一个类ViewHolder,
用了setTag,这个标签就是ViewHolder实例化后对象的一个属性。我们之后对于ViewHolder实例化的对象holder的操作,
都会因为Java的引用机制而一直存活并改变convertView的内容,而不是每次都是去new一个。我们就这样达到的重用*/
}
else{
holder = (Holder)view.getTag();
}
finaImageLoader.display(holder.imageView, list.get(position));
AbsListView.LayoutParams param = new AbsListView.LayoutParams(wh,wh);
/* 创建一个布局(LayoutParams)的实例 param。
AbsListView.LayoutParams(wh,wh) 指定了该布局的宽和高;*/
view.setLayoutParams(param);
return view;
}
class Holder{
ImageView imageView;
}
}
ListViewAdapter:
package com.example.katherine_qj.listviewgridviewadapter;
import android.app.Activity;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.katherine_qj.listviewgridview.R;
import com.example.katherine_qj.listviewwithgrid.MyGridView;
import com.example.katherine_qj.listviewwithgridbean.GridTest;
import com.example.katherine_qj.listviewwithgridutil.SysUtils;
import net.tsz.afinal.FinalBitmap;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Katherine-qj on 2016/6/2.
*/
public class ListViewAdapter extends BaseAdapter{
private LayoutInflater mInflater;
private Activity context;
private List list;
private FinalBitmap finalBitmap;
private GridViewAdapter gridViewAdapter;
private int wh;
public ListViewAdapter(Activity context, List list){
super();
this.mInflater = LayoutInflater.from(context);
this.context = context;
this.wh=(SysUtils.getScreenWidth(context)- SysUtils.Dp2Px(context, 99))/3;
this.list = list;
this.finalBitmap = FinalBitmap.create(context);
this.finalBitmap.configLoadfailImage(R.drawable.head);
}
public List getlist(){
return list;
}
@Override
public int getCount() {
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position) {
return list == null ? null : list.get(position);
}
@Override
public long getItemId(int position) {
return list == null ? null : position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (list.size()==0){
return null;
}
final ViewHolder holder;
if(convertView==null){
convertView = mInflater.inflate(R.layout.item_listview,null);
holder = new ViewHolder();
holder.headphoto = (ImageView) convertView.findViewById(R.id.info_iv_head);//头像
holder.disName = (TextView) convertView.findViewById(R.id.info_tv_name);//昵称
holder.time = (TextView) convertView.findViewById(R.id.info_tv_time);//时间
holder.content = (TextView) convertView.findViewById(R.id.info_tv_content);//发布内容
holder.rl4=(RelativeLayout) convertView.findViewById(R.id.rl4);//图片布局
holder.gv_images = (MyGridView) convertView.findViewById(R.id.gv_images);//图片
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
}
final GridTest gridTest = list.get(position);
String name = null,time = null,content = null,headpath = null,contentimage = null;
if(gridTest!=null){
name = gridTest.getUsername();
time = gridTest.getTime();
content = gridTest.getContent();
headpath = gridTest.getHeadphoto();
contentimage =gridTest.getImage();
}
//昵称
if (name!=null&&!name.equals("")) {
holder.disName.setText(name);
}
//是否含有图片
if (contentimage!=null&&!contentimage.equals("")) {
holder.rl4.setVisibility(View.VISIBLE);
initInfoImages(holder.gv_images,contentimage);
} else {
holder.rl4.setVisibility(View.GONE);
}
//发布时间
if (time!=null&&!time.equals("")) {
holder.time.setText(time);
}
//内容
if (content!=null&&!content.equals("")) {
holder.content.setText(content);
Linkify.addLinks(holder.content, Linkify.WEB_URLS);
}
//头像
if (headpath!=null&&!headpath.equals("")) {
finalBitmap.display(holder.headphoto,headpath);
} else {
holder.headphoto.setImageResource(R.drawable.head);
}
holder.headphoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(context, "点击了头像", Toast.LENGTH_LONG).show();
}
});
return convertView;
}
static class ViewHolder {
ImageView headphoto;
TextView disName;
TextView time;
TextView content;
MyGridView gv_images;
RelativeLayout rl4;
}
public void initInfoImages(MyGridView gv_images,final String imgspath){
if(imgspath!=null&&!imgspath.equals("")){
String[] imgs=imgspath.split("#");
ArrayList list=new ArrayList();
for(int i=0;iint w=0;
switch (imgs.length) {
case 1:
w=wh;
gv_images.setNumColumns(1);
break;
case 2:
case 4:
w=2*wh+SysUtils.Dp2Px(context, 2);
gv_images.setNumColumns(2);
break;
case 3:
case 5:
case 6:
w=wh*3+SysUtils.Dp2Px(context, 2)*2;
gv_images.setNumColumns(3);
break;
}
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(w, RelativeLayout.LayoutParams.WRAP_CONTENT);
gv_images.setLayoutParams(lp);
/*第一个参数为宽的设置,第二个参数为高的设置。
如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去
但LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值:
1,一个确定的值;
2,FILL_PARENT,即填满(和父容器一样大小);
3,WRAP_CONTENT,即包裹住组件就好。。*/
gridViewAdapter=new GridViewAdapter(context, list);
gv_images.setAdapter(gridViewAdapter);
gv_images.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> arg0, View arg1,int arg2, long arg3) {
Toast.makeText(context, "点击了第"+(arg2+1)+"张图片", Toast.LENGTH_LONG).show();
}
});
}
}
}
上面就是所有的类了 代码里面的注释挺详细的
还有三个layout;
分别是listview的item
gridview的item
还有activity_main
代码贴上来吧
activity_main:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@android:color/transparent"
android:divider="#70CDCDCD"
android:dividerHeight="0.5dp"
android:fadingEdge="none"
android:fastScrollEnabled="true"
android:listSelector="@drawable/list_item_selector"
android:scrollbars="none" >
ListView>
RelativeLayout>
item_gridview:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/imagevView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="centerCrop"/>
LinearLayout>
item_listview:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:descendantFocusability="blocksDescendants">
<ImageView
android:id="@+id/info_iv_head"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/head"/>
<RelativeLayout
android:id="@+id/rl1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/info_iv_head">
<TextView
android:id="@+id/info_tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="小仙女"
android:textColor="#444444"
android:textSize="12sp" />
<TextView
android:id="@+id/info_tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="一分钟前"
android:textColor="#BEBBB4"
android:textSize="12sp" />
RelativeLayout>
<RelativeLayout
android:id="@+id/rl3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/rl1"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:paddingBottom="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp" >
<TextView
android:id="@+id/info_tv_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="50dp"
android:text="李睿豪是傻逼哦,李睿豪是傻逼哦,李睿豪是傻逼哦,李睿豪是傻逼哦,李睿豪是傻逼哦,李睿豪是傻逼哦"
android:textColor="#615150"
android:textSize="14sp" />
RelativeLayout>
<RelativeLayout
android:id="@+id/rl4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/rl3"
android:paddingRight="25dp"
android:visibility="gone" >
<com.example.katherine_qj.listviewwithgrid.MyGridView
android:id="@+id/gv_images"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:horizontalSpacing="2dp"
android:listSelector="@null"
android:numColumns="3"
android:stretchMode="columnWidth"
android:verticalSpacing="2dp" >
com.example.katherine_qj.listviewwithgrid.MyGridView>
RelativeLayout>
RelativeLayout>
还有一个就是一个selector
list_item_selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/item_back_press" android:state_enabled="true" android:state_focused="true" android:state_pressed="false"/>
<item android:drawable="@color/item_back_press" android:state_enabled="true" android:state_pressed="true"/>
<item android:drawable="@color/item_back_press" android:state_checked="true" android:state_enabled="true"/>
selector>
嗯 所有的有关于实现的都在上面了
其实主要难点就在嵌套的时候gridview的适配器怎么搞 在那里加 数据源怎么传进去。
这里的数据源是网络图片 所以我们用了一个现成的框架 代码里面有注释 大概oom’之类的问题可以不用考虑
嗯 这里是把gridview当成子项中的一个部分 所以肯定是要写在listviewadapter的getview方法里给它绑定监听器 大概就这样
以上over!
源码在这里 嗯 上传失败了。。。