微信朋友圈的图片上传,多图上传怎么去撸才合适?我们一起来实现吧!
图片上传是非常常见的功能,而多图上传在大多数应用中也是非常常见的,比如微信的朋友圈,微博的动态,都是有九宫格图片的,那这里肯定涉及了多图上传,所以今天我们来一起撸一下,怎么去思考这个实现逻辑!
这里我想到的思路是比较简单的,首先,我们有一个按钮,按钮是上传图片,点击之后弹出某个界面进行图片的选择,一般是九张图片或者十二张,选完之后就直接上传了,大致的流程应该是这个样子,那我们首先来写个按钮
activity_main.xml
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
他只是一个主页,我们只要实现它的点击事件就好了,点击之后跳转到我们的上传图片的Activcity
MainActivity
packagecom.liuguilin.uploadphotossample;
importandroid.content.Intent;
importandroid.support.v7.app.AppCompatActivity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
publicclassMainActivityextendsAppCompatActivity{
privateButtonbtnAddPhoto;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//点击事件
findViewById(R.id.btnAddPhoto).setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewview){
startActivity(newIntent(MainActivity.this,UploadPhotoActivity.class));
}
});
}
}
这些都是可以一笔带过的,真正的逻辑全部都在这个UploadPhotoActivity,我们用GridView显示图片,并且进行多选,下面有一个按钮负责显示已选图片的数量以及完成上传的功能
activity_upload.xml
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
android:id="@+id/mGradView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:numColumns="3"/>
我们现在就要分析我们怎么去实现了,这个GridView肯定是要写的,但是我们首先得要拿到我们的图片,图片怎么拿?肯定是看相册的源码来分析他是怎么去拿的,这里呢,我们使用的是ContentResolver内容访问者,我们查看下源码,我们主要还是看MediaProvider这个项目
http://androidxref.com/4.0.3_r1/xref/packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
我们只要看他最先的一段静态块
static
{
URI_MATCHER.addURI("media","/images/media",IMAGES_MEDIA);
URI_MATCHER.addURI("media","/images/media/#",IMAGES_MEDIA_ID);
URI_MATCHER.addURI("media","/images/thumbnails",IMAGES_THUMBNAILS);
URI_MATCHER.addURI("media","/images/thumbnails/#",IMAGES_THUMBNAILS_ID);
URI_MATCHER.addURI("media","/audio/media",AUDIO_MEDIA);
URI_MATCHER.addURI("media","/audio/media/#",AUDIO_MEDIA_ID);
URI_MATCHER.addURI("media","/audio/media/#/genres",AUDIO_MEDIA_ID_GENRES);
URI_MATCHER.addURI("media","/audio/media/#/genres/#",AUDIO_MEDIA_ID_GENRES_ID);
URI_MATCHER.addURI("media","/audio/media/#/playlists",AUDIO_MEDIA_ID_PLAYLISTS);
URI_MATCHER.addURI("media","/audio/media/#/playlists/#",AUDIO_MEDIA_ID_PLAYLISTS_ID);
URI_MATCHER.addURI("media","/audio/genres",AUDIO_GENRES);
URI_MATCHER.addURI("media","/audio/genres/#",AUDIO_GENRES_ID);
URI_MATCHER.addURI("media","/audio/genres/#/members",AUDIO_GENRES_ID_MEMBERS);
URI_MATCHER.addURI("media","/audio/genres/all/members",AUDIO_GENRES_ALL_MEMBERS);
URI_MATCHER.addURI("media","/audio/playlists",AUDIO_PLAYLISTS);
URI_MATCHER.addURI("media","/audio/playlists/#",AUDIO_PLAYLISTS_ID);
URI_MATCHER.addURI("media","/audio/playlists/#/members",AUDIO_PLAYLISTS_ID_MEMBERS);
URI_MATCHER.addURI("media","/audio/playlists/#/members/#",AUDIO_PLAYLISTS_ID_MEMBERS_ID);
URI_MATCHER.addURI("media","/audio/artists",AUDIO_ARTISTS);
URI_MATCHER.addURI("media","/audio/artists/#",AUDIO_ARTISTS_ID);
URI_MATCHER.addURI("media","/audio/artists/#/albums",AUDIO_ARTISTS_ID_ALBUMS);
URI_MATCHER.addURI("media","/audio/albums",AUDIO_ALBUMS);
URI_MATCHER.addURI("media","/audio/albums/#",AUDIO_ALBUMS_ID);
URI_MATCHER.addURI("media","/audio/albumart",AUDIO_ALBUMART);
URI_MATCHER.addURI("media","/audio/albumart/#",AUDIO_ALBUMART_ID);
URI_MATCHER.addURI("media","/audio/media/#/albumart",AUDIO_ALBUMART_FILE_ID);
URI_MATCHER.addURI("media","/video/media",VIDEO_MEDIA);
URI_MATCHER.addURI("media","/video/media/#",VIDEO_MEDIA_ID);
URI_MATCHER.addURI("media","/video/thumbnails",VIDEO_THUMBNAILS);
URI_MATCHER.addURI("media","/video/thumbnails/#",VIDEO_THUMBNAILS_ID);
URI_MATCHER.addURI("media","/media_scanner",MEDIA_SCANNER);
URI_MATCHER.addURI("media","/fs_id",FS_ID);
URI_MATCHER.addURI("media","/version",VERSION);
URI_MATCHER.addURI("media","/mtp_connected",MTP_CONNECTED);
URI_MATCHER.addURI("media","",VOLUMES_ID);
URI_MATCHER.addURI("media",null,VOLUMES);
//UsedbyMTPimplementation
URI_MATCHER.addURI("media","/file",FILES);
URI_MATCHER.addURI("media","/file/#",FILES_ID);
URI_MATCHER.addURI("media","/object",MTP_OBJECTS);
URI_MATCHER.addURI("media","/object/#",MTP_OBJECTS_ID);
URI_MATCHER.addURI("media","/object/#/references",MTP_OBJECT_REFERENCES);
/
@deprecatedusethe''basic''or''fancy''searchUrisinstead
/
URI_MATCHER.addURI("media","/audio/"+SearchManager.SUGGEST_URI_PATH_QUERY,
AUDIO_SEARCH_LEGACY);
URI_MATCHER.addURI("media","/audio/"+SearchManager.SUGGEST_URI_PATH_QUERY+"/",
AUDIO_SEARCH_LEGACY);
//usedforsearchsuggestions
URI_MATCHER.addURI("media","/audio/search/"+SearchManager.SUGGEST_URI_PATH_QUERY,
AUDIO_SEARCH_BASIC);
URI_MATCHER.addURI("media","/audio/search/"+SearchManager.SUGGEST_URI_PATH_QUERY+
"/",AUDIO_SEARCH_BASIC);
//usedbythemusicapp''ssearchactivity
URI_MATCHER.addURI("media","/audio/search/fancy",AUDIO_SEARCH_FANCY);
URI_MATCHER.addURI("media","/audio/search/fancy/",AUDIO_SEARCH_FANCY);
}
这段代码块就是我们访问系统数据库索要获取任意数据的URI,而我们访问图片的URI是
URI_MATCHER.addURI("media","/images/media",IMAGES_MEDIA);
1
1
不过既然是内容提供者,google也是封装好了一些方法供我们使用
/
初始化数据
/
privatevoidinitData(){
//获取手机相册图片访问本机数据库
ContentResolvermContentResolver=getContentResolver();
//图片数据的url(外部存储)
Uriuri=MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//查询
Cursorcursor=mContentResolver.query(uri,null,null,null,null);
//遍历
while(cursor.moveToNext()){
PhotoBeanbean=newPhotoBean();
//获取路径
Stringpath=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
//设置路径
bean.setPath(path);
//默认fasle
bean.setSelect(false);
//保存
mList.add(bean);
}
}
OK,当我们拿到这些相册的图片肯定是要去存储,那我们怎么去存储?一般都是用个实体对象的
PhotoBean
packagecom.liuguilin.uploadphotossample;
/
项目名:UploadPhotosSample
包名:com.liuguilin.uploadphotossample
文件名:PhotoBean
创建者:LGL
创建时间:2016/8/3113:14
描述:图片存储对象
/
importandroid.graphics.Bitmap;
publicclassPhotoBean{
//路径
privateStringpath;
//是否选择
privatebooleanisSelect;
//位图转换
privateBitmapbitmap;
publicStringgetPath(){
returnpath;
}
publicvoidsetPath(Stringpath){
this.path=path;
}
publicbooleanisSelect(){
returnisSelect;
}
publicvoidsetSelect(booleanselect){
isSelect=select;
}
publicBitmapgetBitmap(){
returnbitmap;
}
publicvoidsetBitmap(Bitmapbitmap){
this.bitmap=bitmap;
}
}
OK,现在可以专心的来写我们的Adapter,也就是数据适配器了
GridAdapter
packagecom.liuguilin.uploadphotossample;
/
项目名:UploadPhotosSample
包名:com.liuguilin.uploadphotossample
文件名:GridAdapter
创建者:LGL
创建时间:2016/8/3113:32
描述:数据适配器
/
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.os.AsyncTask;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.BaseAdapter;
importandroid.widget.ImageView;
importjava.util.List;
publicclassGridAdapterextendsBaseAdapter{
//数据
privateListmList;
//布局加载器
privateLayoutInflatermInflater;
//实体类
privatePhotoBeanbean;
//上下文
privateContextmContext;
//屏幕宽高
privateintw,h;
/
@parammContext
@parammList
/
publicGridAdapter(ContextmContext,ListmList){
this.mContext=mContext;
this.mList=mList;
//系統服務
mInflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
getPhoneWH();
}
/
获取手机屏幕的宽高
/
privatevoidgetPhoneWH(){
w=mContext.getResources().getDisplayMetrics().widthPixels;
h=mContext.getResources().getDisplayMetrics().heightPixels;
}
@Override
publicintgetCount(){
returnmList.size();
}
@Override
publicObjectgetItem(inti){
returnmList.get(i);
}
@Override
publiclonggetItemId(inti){
returni;
}
@Override
publicViewgetView(inti,Viewview,ViewGroupviewGroup){
ViewHolderviewHolder=null;
if(view==nulwww.sm136.coml){
viewHolder=newViewHolder();
view=mInflater.inflate(R.layout.list_item,null);
//初始化
viewHolder.img=(ImageView)view.findViewById(R.id.img);
//设置图片最小宽高
viewHolder.img.setMinimumWidth(w/3);
viewHolder.img.setMinimumHeight(h/3);
viewHolder.img_select=(ImageView)view.findViewById(R.id.img_select);
view.setTag(viewHolder);
}else{
viewHolder=(ViewHolder)view.getTag();
}
//获取position
bean=mList.get(i);
//是否选中
if(bean.isSelect()){
viewHolder.img_select.setVisibility(View.VISIBLE);
}else{
viewHolder.img_select.setVisibility(View.INVISIBLE);
}
//是否有图片
if(bean.getBitmap()==null){
//图片加载,异步加载
newImgTask().execute(bean.getPath(),String.valueOf(i));
}else{
//设置图片
viewHolder.img.setImageBitmap(bean.getBitmap());
}
returnview;
}
/
緩存
/
staticclassViewHolder{
privateImageViewimg;
privateImageViewimg_select;
}
/
异步任务
/
privateclassImgTaskextendsAsyncTask{
/
后台加载
@paramstrings
@return
/
@Override
protectedBitmapdoInBackground(String...strings){
//图片路径
Stringpath=strings[0];
//position
intposition=Integer.parseInt(strings[1]);
//图片压缩
Bitmapbitmap=BitmapUtils.getScaleBitmapPath(mContext,path);
//设置图片
mList.get(position).setBitmap(bitmap);
returnbitmap;
}
/
刷新视图
@parambitmap
/
@Override
protectedvoidonPostExecute(Bitmapbitmap){
super.onPostExecwww.shanxiwang.netute(bitmap);
//刷新
GridAdapter.this.notifyDataSetChanged();
}
}
}
我们在里面做了很多的事情,首选是ViewHolder的优化,然后就是异步加载图片了,接着获取屏幕的高宽去适配,当然,这里做了一个bitmap的工具类
BitmapUtils
packagecom.liuguilin.uploadphotossample;
/
项目名:UploadPhotosSample
包名:com.liuguilin.uploadphotossample
文件名:BitmapUtils
创建者:LGL
创建时间:2016/8/3114:18
描述:图片压缩处理
/
importandroid.content.Context;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
publicclassBitmapUtils{
/
本地图片压缩处理
@parammContext上下文
@parampath路径
@return
/
publicstaticBitmapgetScaleBitmapPath(ContextmContext,Stringpath){
Bitmapbitmap;
intw;
BitmapFactory.Optionsoptions=newBitmapFactory.Options();
options.inJustDecodeBounds=true;
bitmap=BitmapFactory.decodeFile(path,options);
w=options.outWidth;
if(w<50){
options.inSampleSize=w/50;
options.inJustDecodeBounds=false;
bitmap=BitmapFactory.decodeFile(path,options);
}else{
bitmap=BitmapFactory.decodeFile(path);
}
returnbitmap;
}
}
现在我们可以把我们的数据加载进去了
/
初始化View
/
privatevoidinitView(){
btnOk=(Button)findViewById(R.id.btnOk);
btnOk.setOnClickListener(this);
mGridView=(GridView)findViewById(R.id.mGridView);
//设置数据
adapter=newGridAdapter(this,mList);
mGridView.setAdapter(adapter);
}
这样,我们其实是可以看到加载的效果的,这就是相册实现的最基本原理了
到这里,基本上就成功了一半了,现在开始做点击了,只要点击图片,就显示勾选,那我们就要监听他的点击事件了
/
点击事件
/
mGridView.setOnItemClickListener(newAdapterView.OnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView>adapterView,Viewview,inti,longl){
count=0;
PhotoBeanbean=mList.get(i);
bean.setSelect(!bean.isSelect());
//遍历
for(PhotoBeanp:mList){
//如果
if(p.isSelect()){
count++;
}
}
//刷新
adapter.notifyDataSetChanged();
btnOk.setText(count+"/9完成");
}
});
到这里,我们大致的模样是不是已经出来了,我们看下效果
现在只要点击做的就是上传了,我们怎么上传?其实很简单,我们只要在遍历的时候同时拿到路径就好了,所以我们的GradView的点击事件应该是这样写的
/
点击事件
/
mGridView.setOnItemClickListener(newAdapterView.OnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView>adapterView,Viewview,inti,longl){
count=0;
mListPath.clear();
PhotoBeanbean=mList.get(i);
bean.setSelect(!bean.isSelect());
//遍历
for(PhotoBeanp:mList){
//如果
if(p.isSelect()){
count++;
mListPath.add(p.getPath());
}
}
//刷新
adapter.notifyDataSetChanged();
btnOk.setText(count+"/9完成");
}
});
而我们的按钮点击事件
/
点击事件
@paramview
/
@Override
publicvoidonClick(Viewview){
switch(view.getId()){
//上传图片
caseR.id.btnOk:
finish();
Toast.makeText(this,mListPath.toString(),Toast.LENGTH_LONG).show();
break;
}
}
我就直接Toast了,因为我们有路径了,只要往服务器一扔就完事了,对吧,这里就推荐使用RxVolley了,很方便
//post请求简洁版实现
HttpParamsparams=newHttpParams();
//文件上传
params.put("image",newFile("path"))
RxVolley.post(url,params,newHttpCallback(){
@Override
publicvoidonSuccess(Stringt){
Loger.debug("请求到的数据:"+t);
}
});
OK,我们来最后看一遍效果图
记得在清单文件里添加一个小权限哦!
|
|