基于Bmob实现浏览图片,可点击查看大图和双击、手势缩放图片。
效果图:
列表采用Listview嵌套GridView,查看大图时使用ViewPager+PhotoView。
图片和文字的数据来源Bmob的Publish表上:
每个用户可发表文字或图片或图片+文字到Publish表中。
Publish表的结构:
内容包括用户名,用户发表帖子的内容(文字),用户发表帖子的时间,用户发表的图片,用户发了几张图(有这个变量后面处理图片会容易些)。
public class Publish extends BmobObject {
//用户名
private String name;
//用户发布帖子的内容
private String message;
//用户发布帖子的时间
private String time;
//用户最多能发9张图
private List<BmobFile> picture;
public List<BmobFile> getPicture() {
return picture;
}
public void setPicture(List<BmobFile> picture) {
this.picture = picture;
}
//上传多少张图片
private int n;
public int getN() {
return n;
}
public void setN(int n) {
this.n = n;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
本文主要记录把图片和文字放在列表中,模仿朋友圈浏览信息的过程。
1、ListView中嵌套GridView实现布局
ListView的每个Item都要加载多张网络图片并且显示到屏幕中,Item中的每个GridView要显示多张图片,所以GridView要重写来决解宽高的问题,不然图片会显示不出来。
GridView重写:
1、重写onMeasure方法来决解ListView中嵌套GridView图片显示不全的问题。
2、GridView显示规则图片,根据图片的大小自动调整,不然图片会变得参差不齐。
参考:https://www.cnblogs.com/android-yus/p/5106462.html
class MyGridView extends GridView {
private Context context;
private OnTouchInvalidPositionListener onTouchInvalidPositionListener;
public MyGridView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
/**
* ListView嵌套GridView,GridView显示不全问题的解决
* */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//先创建一个监听接口,一旦点击了无效区域,便实现onTouchInvalidPosition方法,返回true or false来确认是否消费了这个事件
if(onTouchInvalidPositionListener!=null){
if(!isEnabled()){
return isClickable()||isLongClickable();
}
int motionPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
if(ev.getAction()==MotionEvent.ACTION_UP&&motionPosition == INVALID_POSITION){
super.onTouchEvent(ev);
return onTouchInvalidPositionListener.onTouchInvalidPosition(motionPosition);
}
}
return super.onTouchEvent(ev);
}
public void setOnTouchInvalidPositionListener(
OnTouchInvalidPositionListener onTouchInvalidPositionListener) {
this.onTouchInvalidPositionListener = onTouchInvalidPositionListener;
}
public interface OnTouchInvalidPositionListener{
public boolean onTouchInvalidPosition(int motionEvent);
}
}
Item布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="8dp"
android:src="@drawable/home" />
<LinearLayout
android:layout_marginLeft="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10sp"
android:orientation="vertical">
<TextView
android:padding="5dp"
android:id="@+id/name"
android:text="名字"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="内容"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.example.implanthearts.MyGridView
android:layout_marginTop="10dp"
android:id="@+id/gridview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnWidth="90dp"
android:focusable="false"
android:gravity="center_horizontal"
android:horizontalSpacing="1dip"
android:numColumns="3"
android:stretchMode="columnWidth"
android:verticalSpacing="12dip"
>
</com.example.implanthearts.MyGridView>
<TextView
android:layout_marginTop="16dp"
android:layout_marginBottom="12dp"
android:id="@+id/time"
android:text="发布时间"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
其中一个Item会显示如图:
2、获取并显示图片
图片和文字都是从Bmob的Publish表中获取的。获取到的数据都存在list中,list是List<Publish>类型。然后把list传给ListView设配器。
如果不是从Bmob上获取数据,可以直接定义list,然后把数据传给ListView设配器。
private void loadDate() {
BmobQuery<Publish> bmobQuery = new BmobQuery<Publish>();
bmobQuery.findObjects(new FindListener<Publish>() { //按行查询
@Override
public void done(List<Publish> list, BmobException e) {
if (e == null){
//数据倒序显示,最新的数据在最上面
Collections.reverse(list);
adapter = new ListViewAdapter(getContext(),list);
listview.setAdapter(adapter);
}
}
});
}
ListViewAdapter:
使用ImageLoader加载图片。
ImageLoader使用可看:https://blog.csdn.net/weixin_39492854/article/details/88935088
public class ListViewAdapter extends BaseAdapter {
private Context context;
private List<Publish> list;
List <String> imageurl;
GrideViewAdapter adapter;
private DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.mipmap.ic_launcher) //设置图片下载期间显示的图片
.showImageForEmptyUri(R.mipmap.ic_launcher) //设置图片uri为空或者是错位的时候显示的图片
.showImageOnFail(R.mipmap.ic_launcher) //设置图片加载或解码过程中发生错误显示的图片
.cacheInMemory(true) //设置下载的图片是否缓存在内存中
.cacheOnDisk(true) //设置下载的图片是否缓存在SD中
.displayer(new RoundedBitmapDisplayer(20)) // 设置成圆角图片
.build(); //创建配置或的DisplayImageOption对象
ImageLoader imageLoader = ImageLoader.getInstance();
public ListViewAdapter(Context context,List<Publish> list) {
this.context = context;
this.list = list;
}
@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(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
convertView = View.inflate(context, R.layout.listview_content, null);
viewHolder = new ViewHolder();
viewHolder.image = (ImageView) convertView.findViewById(R.id.image);
viewHolder.name = (TextView) convertView.findViewById(R.id.name);
viewHolder.text = (TextView) convertView.findViewById(R.id.text);
viewHolder.time = (TextView)convertView.findViewById(R.id.time);
viewHolder.gridview = (MyGridView) convertView.findViewById(R.id.gridview);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
Publish publish = list.get(position);
viewHolder.name.setText(publish.getName());//用户名
viewHolder.text.setText(publish.getMessage());//发表内容
viewHolder.time.setText("发布时间:"+publish.getTime());//发表时间
//显示发表消息的用户的头像
BmobQuery<advertisement> categoryBmobQuery = new BmobQuery<>();
categoryBmobQuery.addWhereEqualTo("name", publish.getName());//根据用户名查找对应的图片头像
categoryBmobQuery.findObjects(new FindListener<advertisement>() {
@Override
public void done(List<advertisement> object, BmobException e) {
if (e == null) {
String touxiang = object.get(0).getPicture().getFileUrl();
Log.w("BMOB",touxiang);
imageLoader.displayImage(touxiang, viewHolder.image, options);
} else {
Log.e("BMOB", e.toString());
}
}
});
if (imageurl == null || imageurl.size() == 0) { // 没有图片资源就隐藏GridView
viewHolder.gridview.setVisibility(View.GONE);
} else {
adapter = new GrideViewAdapter(context,imageurl);
viewHolder.gridview.setAdapter(adapter);
}
return convertView;
}
public class ViewHolder {
ImageView image;
TextView name;
TextView text;
TextView time;
MyGridView gridview;
}
}
GeidViewAdapter:
使用ImageLoader加载图片。
public class GrideViewAdapter extends BaseAdapter{
private Context context;
private List<String > picture;
private DisplayImageOptions options;
public GrideViewAdapter (Context context,List<String>picture){
this.context = context;
this.picture = picture;
}
@Override
public int getCount() {
return picture.size();
}
@Override
public Object getItem(int position) {
return picture.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = View.inflate(context, R.layout.item_gridview, null);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_image);
options = new DisplayImageOptions.Builder()
.showStubImage(R.mipmap.ic_launcher) //设置图片下载期间显示的图片
.showImageForEmptyUri(R.mipmap.ic_launcher) //设置图片uri为空或者是错位的时候显示的图片
.showImageOnFail(R.mipmap.ic_launcher) //设置图片加载或解码过程中发生错误显示的图片
.cacheInMemory(true) //设置下载的图片是否缓存在内存中
.cacheOnDisk(true) //设置下载的图片是否缓存在SD中
.build(); //创建配置或的DisplayImageOption对象
Log.w("ppp",picture.get(position));
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(picture.get(position), imageView, options);
return view;
}
}
这样就可以显示图片了!接下来是点击查看大图,这里重点是要实现ListView和GridView都可以点击,并且要记录是点击了哪个ListView的Item的图片。
ListView的Item在滑动的时候,会重新获取刚刚因为滑动被隐藏(在屏幕外)的图片,如果不记录位置的话,会导致点击图片查看大图的时候,显示的并不是我们想要的那个图片,会产生错位。
参考:https://www.cnblogs.com/android-yus/p/5098206.html
1、记录位置
在ListViewAdapter中添加:
//给当前的GridView设置一个位置标记
viewHolder.gridview.setTag(position);
在GridView的点击事件中添加:
//GridView在ListView条目里的位置
int lv_item_position= (Integer) parent.getTag();
2、GridView查看大图
这里我采用的是在最开始获取list的时候,就记录好每个item有几张图,存进a二维数组里面,代表总共有多少条消息,每条消息有多少张图片。a二维数组为static变量。
//总共有多少条朋友圈
size = list.size();
Log.w("nnn","总共有多少条朋友圈="+size);
a=new String [size][];
n = new int[size];
//每条朋友圈的图片数量
for(int i = 0;i<size;i++){
Log.w("nnn","每条朋友圈的图片数量="+list.get(i).getN());
n[i] = list.get(i).getN();
a[i] = new String[list.get(i).getN()];
for(int j =0;j<list.get(i).getN();j++){
a[i][j] = list.get(i).getPicture().get(j).getFileUrl();
Log.w("nnn","图片地址"+a[i][j]);
}
}
在后面查看大图的时候根据item的位置,查找a二维数组,存进b变量以便显示图片。
//获取点击的图片,查看对应消息的所有大图
List<String> b = new ArrayList<>();
for(int i = 0;i<CommunityFragment.size;i++){
if(i==lv_item_position){
for(int j = 0;j <CommunityFragment.n[i];j++){
b.add( CommunityFragment.a[i][j]);
}
}
}
3、点击任意图片可滑动查看大图
把b传进ShowImageActivity查看大图。使用ViewPager显示图片。
使用Parcelable传递位置和图片。
public class Image implements Parcelable {
private int id;
private List<String> imageurl;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<String> getImageurl() {
return imageurl;
}
public void setImageurl(List<String> imageurl) {
this.imageurl = imageurl;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeStringList(imageurl);
}
public static final Parcelable.Creator<Image> CREATOR = new Parcelable.
Creator<Image>() {
@Override
public Image createFromParcel(Parcel source) {
Image image = new Image();
image.id = source.readInt();
if (image.imageurl == null) {
image.imageurl = new ArrayList<String>();
}
source.readStringList(image.imageurl);
return image;
}
@Override
public Image[] newArray(int size) {
return new Image[size];
}
};
}
Image image = new Image();
image.setId(position);
image.setImageurl(b);
Intent intent = new Intent(context,ShowImageActivity.class);
intent.putExtra("data",image);
获取位置和图片:
Image image = (Image)getIntent().getParcelableExtra("data");
int id = image.getId();
List<String> imageurl = image.getImageurl();
将图片添加到ViewPager中:
for (int i = 0; i < imageurl.size() ; i++) { //for循环将试图添加到list中
View view = LayoutInflater.from(getApplicationContext()).inflate(
R.layout.view_pager_item, null); //绑定viewpager的item布局文件
ImageView iv = (ImageView) view.findViewById(R.id.view_image); //绑定布局中的id
// Log.w("BBB",image.getId(i));
options = new DisplayImageOptions.Builder()
.showStubImage(R.mipmap.ic_launcher) //设置图片下载期间显示的图片
.showImageForEmptyUri(R.mipmap.ic_launcher) //设置图片uri为空或者是错位的时候显示的图片
.showImageOnFail(R.mipmap.ic_launcher) //设置图片加载或解码过程中发生错误显示的图片
.cacheInMemory(true) //设置下载的图片是否缓存在内存中
.cacheOnDisk(true) //设置下载的图片是否缓存在SD中
.build(); //创建配置或的DisplayImageOption对象
//设置当前点击的图片
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(imageurl.get(i), iv, options);
listViews.add(view);
/**
* 图片的长按监听
* */
iv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//弹出提示,提示内容为当前的图片位置
Toast.makeText(ShowImageActivity.this, "这是第" + (index + 1) + "图片", Toast.LENGTH_SHORT).show();
return false;
}
});
}
ViewPager的item布局采用PhotoView,实现双击、手势放大图片。
<com.github.chrisbanes.photoview.PhotoView
android:layout_centerInParent="true"
android:id="@+id/view_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
使用PhotoView需要以下步骤:
1、app的build.gradle中添加:
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
2、在项目的build.gradle中的allprojects添加:
maven {
url "https://jitpack.io"
}
至此就完成啦!
源码等整理后放GitHub。