首页 > 代码库 > 微信朋友圈的图片上传,多图上传怎么去撸才合适?我们一起来实现吧!

微信朋友圈的图片上传,多图上传怎么去撸才合适?我们一起来实现吧!

微信朋友圈的图片上传,多图上传怎么去撸才合适?我们一起来实现吧!


技术分享

图片上传是非常常见的功能,而多图上传在大多数应用中也是非常常见的,比如微信的朋友圈,微博的动态,都是有九宫格图片的,那这里肯定涉及了多图上传,所以今天我们来一起撸一下,怎么去思考这个实现逻辑!

这里我想到的思路是比较简单的,首先,我们有一个按钮,按钮是上传图片,点击之后弹出某个界面进行图片的选择,一般是九张图片或者十二张,选完之后就直接上传了,大致的流程应该是这个样子,那我们首先来写个按钮

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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">

    <Button
        android:id="@+id/btnAddPhoto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="上传图片"/>


</LinearLayout>

他只是一个主页,我们只要实现它的点击事件就好了,点击之后跳转到我们的上传图片的Activcity

MainActivity

package com.liuguilin.uploadphotossample;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button btnAddPhoto;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //点击事件
        findViewById(R.id.btnAddPhoto).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this,UploadPhotoActivity.class));
            }
        });
    }
}

这些都是可以一笔带过的,真正的逻辑全部都在这个UploadPhotoActivity,我们用GridView显示图片,并且进行多选,下面有一个按钮负责显示已选图片的数量以及完成上传的功能

activity_upload.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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">

    <GridView
        android:id="@+id/mGradView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:numColumns="3"/>

    <Button
        android:id="@+id/btnOk"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@color/colorAccent"
        android:text="2/12 完成"
        android:textColor="@android:color/white"/>

</LinearLayout>

我们现在就要分析我们怎么去实现了,这个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);

        // Used by MTP implementation
        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);

        /**
        * @deprecated use the ‘basic‘ or ‘fancy‘ search Uris instead
         */
        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);

        // used for search suggestions
        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);

        // used by the music app‘s search activity
        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);

不过既然是内容提供者,google也是封装好了一些方法供我们使用

    /**
     * 初始化数据
     */
    private void initData() {
        //获取手机相册图片 访问本机数据库
        ContentResolver mContentResolver = getContentResolver();
        //图片数据的url(外部存储)
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        //查询
        Cursor cursor = mContentResolver.query(uri,null,null,null,null);
        //遍历
        while (cursor.moveToNext()){
            PhotoBean bean = new PhotoBean();
            //获取路径
            String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            //设置路径
            bean.setPath(path);
            //默认fasle
            bean.setSelect(false);
            //保存
            mList.add(bean);
        }

    }

OK,当我们拿到这些相册的图片肯定是要去存储,那我们怎么去存储?一般都是用个实体对象的

PhotoBean

package com.liuguilin.uploadphotossample;

/*
 *  项目名:  UploadPhotosSample 
 *  包名:    com.liuguilin.uploadphotossample
 *  文件名:   PhotoBean
 *  创建者:   LGL
 *  创建时间:  2016/8/31 13:14
 *  描述:    图片存储对象
 */

import android.graphics.Bitmap;

public class PhotoBean {

    //路径
    private String path;
    //是否选择
    private boolean isSelect;
    //位图转换
    private Bitmap bitmap;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public boolean isSelect() {
        return isSelect;
    }

    public void setSelect(boolean select) {
        isSelect = select;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
    }
}

OK,现在可以专心的来写我们的Adapter,也就是数据适配器了

GridAdapter

package com.liuguilin.uploadphotossample;

/*
 *  项目名:  UploadPhotosSample 
 *  包名:    com.liuguilin.uploadphotossample
 *  文件名:   GridAdapter
 *  创建者:   LGL
 *  创建时间:  2016/8/31 13:32
 *  描述:    数据适配器
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

import java.util.List;

public class GridAdapter extends BaseAdapter {

    //数据
    private List<PhotoBean> mList;
    //布局加载器
    private LayoutInflater mInflater;
    //实体类
    private PhotoBean bean;
    //上下文
    private Context mContext;
    //屏幕宽高
    private int w, h;

    /**
     * @param mContext
     * @param mList
     */
    public GridAdapter(Context mContext, List<PhotoBean> mList) {
        this.mContext = mContext;
        this.mList = mList;
        //系統服務
        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        getPhoneWH();
    }

    /**
     * 获取手机屏幕的宽高
     */
    private void getPhoneWH() {
        w = mContext.getResources().getDisplayMetrics().widthPixels;
        h = mContext.getResources().getDisplayMetrics().heightPixels;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int i) {
        return mList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder = null;
        if (view == null) {
            viewHolder = new ViewHolder();
            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) {
            //图片加载,异步加载
            new ImgTask().execute(bean.getPath(), String.valueOf(i));
        } else {
            //设置图片
            viewHolder.img.setImageBitmap(bean.getBitmap());
        }
        return view;
    }


    /**
     * 緩存
     */
    static class ViewHolder {
        private ImageView img;
        private ImageView img_select;
    }

    /**
     * 异步任务
     */
    private class ImgTask extends AsyncTask<String, Void, Bitmap> {

        /**
         * 后台加载
         *
         * @param strings
         * @return
         */
        @Override
        protected Bitmap doInBackground(String... strings) {
            //图片路径
            String path = strings[0];
            //position
            int position = Integer.parseInt(strings[1]);
            //图片压缩
            Bitmap bitmap = BitmapUtils.getScaleBitmapPath(mContext, path);
            //设置图片
            mList.get(position).setBitmap(bitmap);
            return bitmap;
        }

        /**
         * 刷新视图
         *
         * @param bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //刷新
            GridAdapter.this.notifyDataSetChanged();
        }
    }
}

我们在里面做了很多的事情,首选是ViewHolder的优化,然后就是异步加载图片了,接着获取屏幕的高宽去适配,当然,这里做了一个bitmap的工具类

BitmapUtils

package com.liuguilin.uploadphotossample;

/*
 *  项目名:  UploadPhotosSample 
 *  包名:    com.liuguilin.uploadphotossample
 *  文件名:   BitmapUtils
 *  创建者:   LGL
 *  创建时间:  2016/8/31 14:18
 *  描述:    图片压缩处理
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class BitmapUtils {

    /**
     * 本地图片压缩处理
     *
     * @param mContext 上下文
     * @param path     路径
     * @return
     */
    public static Bitmap getScaleBitmapPath(Context mContext, String path) {
        Bitmap bitmap;
        int w;
        BitmapFactory.Options options = new BitmapFactory.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);
        }
        return bitmap;
    }
}

现在我们可以把我们的数据加载进去了

    /**
     * 初始化View
     */
    private void initView() {

        btnOk = (Button) findViewById(R.id.btnOk);
        btnOk.setOnClickListener(this);
        mGridView = (GridView) findViewById(R.id.mGridView);

        //设置数据
        adapter = new GridAdapter(this, mList);
        mGridView.setAdapter(adapter);
    }

这样,我们其实是可以看到加载的效果的,这就是相册实现的最基本原理了

技术分享

到这里,基本上就成功了一半了,现在开始做点击了,只要点击图片,就显示勾选,那我们就要监听他的点击事件了

        /**
         * 点击事件
         */
        mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                count =0;
                PhotoBean bean = mList.get(i);
                bean.setSelect(!bean.isSelect());
                //遍历
                for (PhotoBean p : mList) {
                    //如果
                    if (p.isSelect()) {
                        count++;
                    }
                }
                //刷新
                adapter.notifyDataSetChanged();
                btnOk.setText(count + "/ 9 完成");
            }
        });

到这里,我们大致的模样是不是已经出来了,我们看下效果

技术分享

现在只要点击做的就是上传了,我们怎么上传?其实很简单,我们只要在遍历的时候同时拿到路径就好了,所以我们的GradView的点击事件应该是这样写的

        /**
         * 点击事件
         */
        mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                count = 0;
                mListPath.clear();
                PhotoBean bean = mList.get(i);
                bean.setSelect(!bean.isSelect());
                //遍历
                for (PhotoBean p : mList) {
                    //如果
                    if (p.isSelect()) {
                        count++;
                        mListPath.add(p.getPath());
                    }
                }
                //刷新
                adapter.notifyDataSetChanged();
                btnOk.setText(count + "/ 9 完成");
            }
        });

而我们的按钮点击事件

    /**
     * 点击事件
     *
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            //上传图片
            case R.id.btnOk:
                finish();
                Toast.makeText(this, mListPath.toString(), Toast.LENGTH_LONG).show();
                break;
        }
    }

我就直接Toast了,因为我们有路径了,只要往服务器一扔就完事了,对吧,这里就推荐使用RxVolley了,很方便

   //post请求简洁版实现
    HttpParams params = new HttpParams();
    //文件上传
    params.put("image", new File("path"))

    RxVolley.post(url, params, new HttpCallback() {
        @Override
        public void onSuccess(String t) {
           Loger.debug("请求到的数据:" + t);
      }
 });

OK,我们来最后看一遍效果图

技术分享

记得在清单文件里添加一个小权限哦!

好的,这篇博客就到这里了,每次写博客都写到深更半夜,太痛苦了!!!

一起玩玩?加群:555974449

UploadPhotosSample:http://download.csdn.net/detail/qq_26787115/9618368

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    微信朋友圈的图片上传,多图上传怎么去撸才合适?我们一起来实现吧!