文档章节

Android学习-手把手教学实现仿微信发带图片朋友圈的UI设计

Ryane
 Ryane
发布于 2016/06/14 16:02
字数 2199
阅读 1405
收藏 5

#Android学习-手把手教学实现仿微信发带图片朋友圈的UI设计#

功能需求

最近自己在开发一个社交APP,发送动态(类似朋友圈)是社交APP必备的一个功能,而自己在开发过程中也需要开发到这一个功能,但是在开发中遇到了一个问题,就是如何绘制一个类似朋友群那样动态添加图片,并加号随着自己的图片增加而后移这一个UI,而这篇小文就是教你如何制作一个仿朋友圈发带图朋友圈的UI设计。注意,这是UI设计,并不是实现图片上传功能。

当然,如果你想知道如何实现图片上传到服务器,请看我的另一篇文章:Android学习-使用Async-Http实现图片压缩并上传功能。个人水平有限,如有不足的地方,欢迎交流,勿喷。

效果图

按照惯例,先上效果图。

困难

在自己开发学习过程中,主要遇到了两个难点:

  1. 添加过多图片时,会出现OOM。
  2. 如何动态修改图片展示栏的高度。
  3. 加号如何伴随图片的增加而后移。
  4. 如何保证最多添加照片为9张。

难点解决

添加过多图片时,会出现OOM

出现第一种情况的原因很简单,就是随着我们手机的像素越来越高,图片的大小也越来越大,我们普通的机拍出来照片至少也有1~2M,更不说像素高的手机。而对于一个安卓应用来说,由于手机设备的限制,一般应用使用的RAM不能超过某个设定值,不同产商默认值不太一样,一般常见的有16M,24M,32M,48M。所以一个Activity中加载几张高清原图,就会报Out Of Memory 错误,也就是所谓的OOM错误。所以知道了这个问题之后我们就很容易解决了,我们就可以先将图片压缩,然后再使用ImageView加载压缩后的图片即可。而我们这里是通过对图片的尺寸进行压缩实现图片的压缩,这里大概说一下。

  1. 要对图片压缩,首先要先将BitmapFactory.Options中的inJustDecodeBounds设置为true。

     final BitmapFactory.Options options = new BitmapFactory.Options();
     // 若要对图片进行压缩,必须先设置Option的inJustDecodeBounds为true
     options.inJustDecodeBounds = true;
    
  2. 然后通过BitmapFactory中decodeFile方法来获取到照片的高度和宽度,这里只要存进一个图片地址即可。获取图片地址这里就不详讲了。

     BitmapFactory.decodeFile(pathName,options)
    
  3. 然后需要对BitmapFactory.Options中的inSampleSize根据你需要压缩比例进行设置,options.inSampleSize是图片的压缩比,例如原来大小是100 * 100,options.inSampleSize为1,则不变,options.inSampleSize为2,则压缩成50 * 50。而我这里是根据自己设置最低宽度和最低高度来获取inSampleSize的值:

     private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
     	final int height = options.outHeight;
     	final int width = options.outWidth;
     	int inSampleSize = 1;
     	if (height > reqHeight || width > reqWidth) {
     		//首先获取原图高度和宽度的一半
     		final int halfHeight = height / 2;
     		final int halfWidth = width / 2;
     		//循环,如果halfHeight和halfWidth同时大于最小宽度和最小高度时,inSampleSize乘2,
     		//最后得到的宽或者高都是最接近最小宽度或者最小高度的
     		while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
     			inSampleSize *= 2;
     		}
     	}
     	return inSampleSize;
     }
    
  4. 获取到inSampleSize值之后,重新设置options.inJustDecodeBounds为false,不能修改option,调用BitmapFactory中的decodeFile方法即可获取到压缩后的照片,这样在加载图片时就可以避免OOM的出现了。

     options.inJustDecodeBounds = false;
     // 根据options重新加载图片
     Bitmap src = BitmapFactory.decodeFile(pathName, options);
    

综上,我将按尺寸压缩照片的功能包装成BitmapUtil类,在使用时直接调用即可。

public class BitmapUtils {

	private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;
		if (height > reqHeight || width > reqWidth) {
			final int halfHeight = height / 2;
			final int halfWidth = width / 2;
			while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
				inSampleSize *= 2;
			}
		}
		return inSampleSize;
	}

	/**
	 * 根据Resources压缩图片
	 * 
	 * @param res
	 * @param resId
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeResource(res, resId, options);
		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
		options.inJustDecodeBounds = false;
		Bitmap src = BitmapFactory.decodeResource(res, resId, options);
		return src;
	}

	/**
	 * 根据地址压缩图片
	 * 
	 * @param pathName
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	public static Bitmap decodeSampledBitmapFromFd(String pathName, int reqWidth, int reqHeight) {
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(pathName, options);
		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
		options.inJustDecodeBounds = false;
		Bitmap src = BitmapFactory.decodeFile(pathName, options);
		return src;
	}
}

如何动态修改图片展示栏的高度

如何动态修改图片展示栏的高度,首先我说一下我是使用GridView来实现图片栏的展示,所以我们可以在第一次加载GridView时可以获取到下图的参数,大家看图会容易理解一点。

  • 我们的照片如果只有一栏,则GridView的高度不变
  • 如果照片有两栏,则高度设置为gridViewH * 2 - (gridViewH - imageViewH) / 2
  • 如果有三栏,则GrideView的高度设置为gridViewH * 3 - (gridViewH - imageViewH)

  1. 我们在第一次加载GridView时记录GridView的高度GridViewH。

     LinearLayout.LayoutParams params =(android.widget.LinearLayout.LayoutParams) mGridView.getLayoutParams();
    
     gridViewH = params.height;
    
  2. 同时记录ImageView的高度

     RelativeLayout.LayoutParams params = (android.widget.RelativeLayout.LayoutParams) holder.imageView
     			.getLayoutParams();
     imageViewH = params.height;
    
  3. 则上下的边距为

     (gridViewH - imageViewH) / 2
    
  4. 将它写成一个方法,在每次getView()方法中调用即可。

     private void setGridView() {
     	LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) mGridView.getLayoutParams();
     	if (data.size() < 4) {
     		lp.height = gridViewH;
     	} else if (data.size() < 8) {
     		lp.height = gridViewH * 2 - (gridViewH - imageViewH) / 2;
     	} else {
     		lp.height = gridViewH * 3 - (gridViewH - imageViewH);
     	}
     	mGridView.setLayoutParams(lp);
     }
    

加号如何伴随图片的增加而后移

因为我的数据源是List<Bitmap>,所以可以这么做:

  • 当第一次加载时,List中只有一张加号的照片

  • 当添加了照片之后,List先移除加号照片,再添加照片,最后再把加号照片添加进去

      	data.remove(data.size() - 1);
      	Bitmap bp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_addpic);
      	data.add(newBp);
      	data.add(bp);
      	//将路径设置为空,防止在手机休眠后返回Activity调用此方法时添加照片
      	photoPath = null;
      	adapter.notifyDataSetChanged();
    

如何保证最多添加照片为9张

这个问题只需要在每次添加之前判断数据源的大小是否为10(包括加号照片,大小就为10)。

if (data.size() == 10) {
	Toast.makeText(MainActivity.this, "图片数9张已满", Toast.LENGTH_SHORT).show();
} else {
	if (position == data.size() - 1) {
		Toast.makeText(MainActivity.this, "添加图片", Toast.LENGTH_SHORT).show();
		// 选择图片
		Intent intent = new Intent(Intent.ACTION_PICK, null);
		intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
		startActivityForResult(intent, 0x1);
	} else {
		Toast.makeText(MainActivity.this, "点击第" + (position + 1) + " 号图片", Toast.LENGTH_SHORT).show();
	}
}

界面

activity_main.xml

<ScrollView 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:background="#F3F6F8"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginTop="2dp"
            android:src="#E4E3E3" />

        <EditText
            android:id="@+id/content_et"
            android:layout_width="fill_parent"
            android:layout_height="120dp"
            android:background="#FFFFFF"
            android:gravity="top"
            android:hint="随手说出你此刻的心声..."
            android:maxLength="500"
            android:padding="5dp"
            android:singleLine="false"
            android:textColor="#000000"
            android:textSize="20sp" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:src="#E4E3E3" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginTop="10dp"
            android:src="#E4E3E3" />

        <GridView
            android:id="@+id/gridView1"
            android:layout_width="fill_parent"
            android:layout_height="100dp"
            android:background="#FFFFFF"
            android:columnWidth="90dp"
            android:gravity="center"
            android:horizontalSpacing="5dp"
            android:numColumns="4"
            android:padding="10dp"
            android:stretchMode="columnWidth"
            android:verticalSpacing="5dp" >
        </GridView>

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:src="#E4E3E3" />

        <Button
            android:id="@+id/send_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="35dp"
            android:layout_marginRight="35dp"
            android:layout_marginTop="20dp"
            android:background="@drawable/send_btn_selector"
            android:gravity="center"
            android:text="发送"
            android:textColor="#FFFFFF"
            android:textSize="20sp" />
    </LinearLayout>

</ScrollView>

griditem.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:descendantFocusability="blocksDescendants"
    android:gravity="center" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_addpic" />

</RelativeLayout>

Demo下载

Github:https://github.com/ryanlijianchang/TestUpload

CSDN: http://download.csdn.net/detail/ljcitworld/9549313

后话

博主只是实现了这一个UI界面,我们开发过程中肯定要实现图片,文字的上传等,这里博主就不再详述了,大家可以看我的另一篇博文Android学习-使用Async-Http实现图片压缩并上传功能,就这个例子而言,大家如果需要上传多张照片,就可以在添加完照片之后将bitmap存起来,然后通过循环容器的大小,然后每一张图片再上传到服务器即可。还是那句话,个人能力有限,欢迎大家一起交流学习,我也会虚心接纳大家的指教,不喜勿喷。

参考资料

Android Developer:Loading Large Bitmaps Efficiently

© 著作权归作者所有

Ryane
粉丝 42
博文 22
码字总数 55318
作品 0
程序员
私信 提问
关于微信的那点帖。。。。。。(各种实例)

照惯例发帖先吐槽一下:最近看到大家发防微信功能特别的多,所以就汇总了一下,看看大家还有什么其他方面的汇总需求,回复我噢,均可满足。。。。。 (ps:有些帖子由于主观、客观还有神马原因...

程序袁_绪龙
2015/04/01
233
0
2017 我用 5 个月分享了 98 篇优秀博文

对上半年所分享的文章进行一个整理,很多读者当时忘记了收藏,以致于查找一篇历史文章很费劲,因此在这里顺便做下记录。目前就分下下面几个大类,没有更多细分,已基本可以查找了。 如果觉得...

code小生
2018/10/30
0
0
ZBLibrary 19.0 发布,Star 数已达 3K

更新内容: 新增一键自动删除所有编译缓存 build 目录的 Window 批处理脚本 clearAllBuildCache.bat; getActivity 抽象到 BaseActivity; 解决条形码与预览框的水平倾角超过30%识别不了,感...

孤独的探索号
2018/11/28
2.4K
0
Android快速开发框架 - Android-ZBLibrary

ZBLibrary-Android快速开发框架 MVP架构的Android快速开发框架, 提供一套开发标准(View,Data,Event)以及模板和工具类并规范代码。 封装层级少,简单高效兼容性好。 OKHttp、UIL图片加载、...

孤独的探索号
2016/04/21
5.9K
4
Android部分源码资源共享(视屏转GIF图片工具、仿抖音、仿朋友圈、仿红包、饼状图、引导图,图灵源码等)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/generallizhong/article/details/90202879 视屏转为gif图片工具: 下载地...

generallizhong
05/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

为构建社交关系链手淘都做了啥?

作者|王卫(泓冰) 出品|阿里巴巴新零售淘系技术部 01、淘宝社交关系推荐的背景 1、互联网下半场到来:互联网的下半场,人口红利消失,各大平台需要对用户做精细化运营,用户的增长和留存是每一...

阿里云官方博客
25分钟前
4
0
Iaas/Paas/Saas有何区别,一个故事告诉你

云计算有三种服务模式,IaaS,PaaS和SaaS。单从英文全称去理解,他们分别是“基础设施即服务”“平台即服务”和“软件即服务”。 这样翻译过来可不好理解,但是我们可以举个例子。现在我们就以...

JEPaaS云平台
33分钟前
4
0
温度传感器怎么测好坏

  温度传感器也就是负温度系数热敏电阻,温度越高,电阻越小,测量时先看其阻值能不能根据温度的变化而变,再看其变化的阻值是不是在标定的范围之内。   有以下四种方法;   1、若是有...

仙溪
34分钟前
4
0
zk中ZooKeeperServer解析

内部类 ChangeRecord 处理PrepRP和FinalRP之间的信息 static class ChangeRecord { ChangeRecord(long zxid, String path, StatPersisted stat, int childCount, List<ACL> acl) {......

writeademo
44分钟前
3
0
LNMP---安装worrdpress、discuz,域名重定向,用户认证,nginx访问日志

4.34 安装wordpress 4.35 安装discuz 4.36 域名重定向 4.37 用户认证 4.38 nginx访问日志 一、安装wordpress 创建博客: 添加一个博客的虚拟主机 blog.tobe.com.conf 做如下更改 安装博客wor...

tobej
46分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部