先看效果图:




这就是一个可以联网的图库软件,下面我们来看看需求


业务需求

1.判断是否第一次运行,第一次运行,提示添加新条目

2.点击添加按钮,弹出对话框,输入图片网址和标题

3.下载图片保存到本地SD卡中

4.数据库中保存图片文件路径和图片标题和URL地址

5.listview中列出已保存的所有条目,添加条目后,同步展现到listview中

6.选中listview中一个条目,点击删除,删除存储的条目,同步展现到listview

7.长按listview的条目,弹出删除菜单项,点击菜单项也可以删除条目

8.提供contentprovider供其他软件访问数据库


问题分析

虽然要求看起来挺多但是可以大致分为3部分去实现

  1. 数据的下载保存,包括存储到本地sdcard 和数据库中
  2. 数据的删除
  3. 为其他软件提供数据库接口

主要代码实现

首先用户输入图片地址我们应该去下载图片并保存到本地,此时下载图片属于耗时且需要联网的操作所以不能在ui线程中实现,我们创建异步任务AsyncTask完成图片下载,并通知UI线程更新进度条界面。下载完成应该返回图片的保存地址准备将数据写入数据库中 由于要展示下载的进度我们利用接口回调比较好实现。下面是下载图片的异步任务代码:

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;import android.os.AsyncTask;
import android.os.Environment;public class MyTask extends AsyncTask<String, Integer, String> {public interface CallBack {public void start(); //主界面展示一个进度条public void updataProgress(int progress); //更新进度条public void finish(String imgPath); //下载完成返回文件保存到绝对路径}CallBack cb;public MyTask(CallBack cb) {super();this.cb = cb;}@Overrideprotected void onPreExecute() {super.onPreExecute();if (cb != null) {cb.start(); //准备工作}}@Overrideprotected String doInBackground(String... params) {// 1.HttpURLConnectionHttpURLConnection conn = null;String imgPath = null;// 2.URLtry {URL url = new URL(params[0]);// 3.url.openConnectionconn = (HttpURLConnection) url.openConnection();// 4.InputStreamInputStream in = conn.getInputStream();// 获取该文件的总长度int total = conn.getContentLength();// 5.获取保存文件的路径及文件 名/sdcard/imageString path_sdcard = Environment.getExternalStorageDirectory().getAbsolutePath() + "/image";File fileParent = new File(path_sdcard);// 判断该目录是否存在,如果不存在,创建该目录if (!fileParent.exists()) {// 创建目录fileParent.mkdirs();}String arr[] = params[0].split("/");String filenameString = arr[arr.length - 1];// 6.创建File对象,再拿到OutputStreamFile file = new File(path_sdcard, filenameString);if (file.exists()) {return file.getAbsolutePath();}// 用来返回该img路径imgPath = file.getAbsolutePath();OutputStream out = new FileOutputStream(file);byte[] buffer = new byte[4096];int sum = 0;int len = 0;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);// 累加sum = sum + len;// 计算百分比int per = (int) (sum * 100f / total);// 发布进度值publishProgress(per);}out.flush();out.close();in.close();// 返回当前被保存的img的绝对路径return imgPath;} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (conn != null) {conn.disconnect();}}//下载异常返回NULLreturn null;}@Overrideprotected void onProgressUpdate(Integer... values) {// TODO Auto-generated method stubsuper.onProgressUpdate(values);if (cb != null) {cb.updataProgress(values[0]); //更新进度条}}/** result表示的是图片所在的的路径*/@Overrideprotected void onPostExecute(String result) {// TODO Auto-generated method stubsuper.onPostExecute(result);if (cb != null) {cb.finish(result); //返回图片地址}}}

图片下载完成并且保存到本地了 此时我们应该将图片名称 URL 绝对路径 写入数据库。 那么此时我们应该开始创建数据库了,自定义MySqliteHelper 继承SQLiteOpenHelper 就好了,代码如下

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;public class MySqliteHelper extends SQLiteOpenHelper {public MySqliteHelper(Context context) {super(context, "picture.db", null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {// 创表String sql = "create table img(_id integer primary key autoincrement,name text ,url text,path text)";db.execSQL(sql);Log.d("onCreateDataBase", "helper onCreate create table img");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// TODO Auto-generated method stub}}

数据库有着落了,那么我们应该想着如何将数据展示到listview中,这里ListView中的每个item包含了一张图片一个文本 。 这里就用就灵活的BaseAdapter完成,我们自定义一个MyAdapter继承自BaseAdapter。

import java.io.File;
import java.util.ArrayList;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;public class MyAdapter extends BaseAdapter {ArrayList<Picture> data;Context context;LayoutInflater inflater;int progress;public MyAdapter(ArrayList<Picture> data, Context context) {super();this.data = data;this.context = context;inflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return data.size();}@Overridepublic Object getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubViewHolder holder = null;if (convertView == null) {holder = new ViewHolder();convertView = inflater.inflate(R.layout.list_item, null);holder.tv_1 = (TextView) convertView.findViewById(R.id.tv);holder.iv = (ImageView) convertView.findViewById(R.id.img);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}Picture picture = data.get(position);File file = new File(picture.path);if (file.exists()) { // 如果sd卡的图片存在去设置图片holder.tv_1.setText(picture.name);Bitmap bitmap = BitmapFactory.decodeFile(picture.path);holder.iv.setImageBitmap(bitmap);} else { // 图片不存在提示用户Toast.makeText(context, picture.name + "好像出了点问题,图片是否被你删除?",Toast.LENGTH_SHORT).show();// 本地图片被删除 删除数据库中的数据MySqliteHelper helper = new MySqliteHelper(context);SQLiteDatabase db = helper.getWritableDatabase();String sql = "DELETE FROM img WHERE path = '" + picture.path + "'";db.execSQL(sql);}return convertView;}class ViewHolder {TextView tv_1;ImageView iv;}}

数据展示问题也解决了,那么 接下来就是删除事件,点击listview 或者长按listview中的item都有对应的监听事件分别是setOnItemClickListener、 setOnItemLongClickListener
删除事件的实现:

lv.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,final int position, long id) {AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);builder.setTitle("刪除" + data.get(position).name).setMessage("此操作不可逆,是否继续?");// 相当于确定builder.setPositiveButton("确定",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {/** 注释部分为普通sql查询语句 String sql =* "delete from img where url='" +* data.get(position).url + "'";* db.execSQL(sql);*/// 1.获取连接服务地址Uri uri = Uri.parse("content://com.picture.provider");// 2.获取ContentResolverContentResolver cr = getContentResolver();// 3.根据位置获取到数据库中相应的url 作为条件删除相应的数据cr.delete(uri, "url='" + data.get(position).url+ "'", null);File file = new File(data.get(position).path);if (file.exists()) { // 如果存在那么删除本地文件file.delete();}data = readDataBase(); // 读取数据库中的内容MainActivity.this.position = -1;// 每次删除完毕设置当前位置为-1 为下一次点击做准备adapter.notifyDataSetChanged();// 提示更新界面}});// 相当于取消 这里啥都不干builder.setNegativeButton("取消",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {}});builder.show();return false;}});

public void del(View view) {// 删除图片if (position != -1) {AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);builder.setTitle("刪除" + data.get(position).name).setMessage("此操作不可逆,是否继续?");// 相当于确定builder.setPositiveButton("确定",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {/** String sql = "delete from img where url='" +* data.get(position).url + "'"; db.execSQL(sql);*/Uri uri = Uri.parse("content://com.picture.provider");// 获取ContentResolverContentResolver cr = getContentResolver();// 准备数据cr.delete(uri, "url='" + data.get(position).url+ "'", null);File file = new File(data.get(position).path);if (file.exists()) { // 如果存在那么删除本地文件file.delete();}data = readDataBase(); // 读取数据库中的内容if (data.size() == 0) { // 没有东西那么接下的链接可以直接下载flag = true;MainActivity.this.position = -1;}adapter.notifyDataSetChanged();// 提示更新界面}});// 相当于取消builder.setNegativeButton("取消",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}});builder.show();} else {Toast.makeText(this, "当前没有选中任何图片!", Toast.LENGTH_LONG).show();}}

删除功能也实现了,接下来我们实现ContentProvider功能其实也简单,写一个类继承自ContentProvider

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;public class MyContentProvider extends ContentProvider {MySqliteHelper helper;@Overridepublic boolean onCreate() {helper = new MySqliteHelper(getContext());if (helper != null) {return true;}return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {SQLiteDatabase db = helper.getWritableDatabase();Cursor cursor = db.query("img", projection, selection, selectionArgs,null, null, sortOrder);cursor.setNotificationUri(getContext().getContentResolver(), uri); // 通知界面更新return cursor;}@Overridepublic String getType(Uri uri) {return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {Log.e("insert", " " + uri.getAuthority());SQLiteDatabase db = helper.getWritableDatabase();long id = -1;id = db.insert("img", null, values);db.close();getContext().getContentResolver().notifyChange(uri, null);return ContentUris.withAppendedId(uri, id); }@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {Log.e("delete", "delete");SQLiteDatabase db = helper.getWritableDatabase();int count = db.delete("img", selection, selectionArgs);getContext().getContentResolver().notifyChange(uri, null);return count; //返回删除的条数}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {Log.e("update", "update"+selection);SQLiteDatabase db = helper.getWritableDatabase();int count = db.update("img", values, selection, selectionArgs);getContext().getContentResolver().notifyChange(uri, null);return count;}}

在xml中注册provider提供给其他程序

   <provider
            android:name="com.wenxiangli.MyContentProvider"android:authorities="com.picture.provider"android:exported="true" ></provider>

至此我们需要的功能都实现了。接下来说几个遇到的问题注意点:

1.权限一定不要忘记了

 <!-- 联网权限 --><uses-permission android:name="android.permission.INTERNET" /><!-- SDcard的读写权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 读取Sdcard状态权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

2.为了完成第二次启动加载本地图片,我们应该先从数据库读出数据然后绑定到集合中去通知adapter更新
3.删除特别注意越界问题,这里我是通过每次删除结束设置positon为-1,当点击事件产生将改变position的值去判断是否删除。
4.保证图库中的数据唯一,所以每次下载前判断图片是否存在,存在就不去下载。直接提示是否更改文件的名字


下面附上完整源码地址需要的可以在这下载
链接:http://pan.baidu.com/s/1dEW5JBr 密码:m3jb

写这么长不容易啊,如果对你有帮助赏给回复 啊哈哈

简单图库软件的实现(联网下载图片保存到sdcard在Listview中展示,并作为ContentProvider为其他软件提供图库数据)相关推荐

  1. 批量修改文件名后缀,并批量将本地图片保存到word中

      这个文章是帮女朋友做的.   为寻电子书一本,寻几天无果.近日,偶遇某网站,寻得踪迹.不料,图以".pdg"格式存在,遂写以代码,批量改得文件后缀,称只".png&q ...

  2. 使用requests下载图片,存到图片文件的方法

    使用requests下载图片,存到图片文件的方法 url2 = '' method2 = 'get' res2 = requests.request(url=url2, method=method2) ...

  3. 一个把图片保存到SQL数据库的工具

    因公司的业务需要,会把很多图片保存到数据库中.因此做了一个很简单的工具,把图片保存到SQL数据库中. 1.可以连接到不同服务器上的SQL数据库,可以选择操作各个表; 2.可以指定保存到数据库的图片格式 ...

  4. Android—将Bitmap图片保存到SD卡目录下或者指定目录

    直接上代码就不废话啦 一:保存到SD卡下 [java] view plain copy File file = new File(Environment.getExternalStorageDirec ...

  5. Python爬虫:爬取知乎上的视频,并把下载链接保存到md文件中

    Python爬虫:爬取知乎上的视频,并把下载链接保存到md文件中 1.需要的Python模块 主要是requests模块,用于得到的网页的数据 安装命令为:pip install requests 2 ...

  6. iOS 将图片保存到iPhone本地相册

    /** *  将图片保存到iPhone本地相册 *  UIImage *image            图片对象 *  id completionTarget       响应方法对象 *  SEL ...

  7. java保存图片到tomcat_Java上传图片到服务器路径获取系列之--图片保存到Tomcat的webapps目录下...

    Java上传图片到服务器路径获取系列之--图片保存到Tomcat的webapps目录下 note:有差错或不足之处请批评指正,谢谢! 上一篇介绍了Java web项目中把客户端上传的图片保存到服务器中 ...

  8. python爬取站长素材网页图片保存到ppt中

    2019独角兽企业重金招聘Python工程师标准>>> 网站地址:http://sc.chinaz.com/tupian/index.html 直接上代码: import reque ...

  9. Python 多线程、利用request使用代理、利用递归深度抓取电影网页的内容并将电影的介绍和下载链接保存到mysql中

    本文仅为学习python过程的一个笔记,其中还有一些bug! 还请各位大佬赐教 有些专业的说法还不是很熟悉,欢迎各位大佬帮忙指出 本人时一个新晋奶爸,而立之年突然想转业,想学习python 先介绍一个 ...

最新文章

  1. php image mask,PHP GD使用一个图像来遮蔽另一个图像,包括透明度
  2. 《ASCE1885的网络编程》---Winsock APIのIP地址转换函数
  3. rac_安装软件时报版本号过高问题
  4. 对前端Jenkins自动化部署的研究
  5. 信息学奥赛C++语言:小玉家的电费
  6. 递增的整数序列链表的插入
  7. 简约几何海报背景设计素材,可盐可甜
  8. SQL Server与Excel数据互导
  9. 技巧:MacOS 中快速复制文件或文件夹路径
  10. 纺大数学与计算机学院徐涛,数学与统计学院
  11. 2022 最新 R 语言 与 RStudio 安装配置教程
  12. XS128 中断向量表
  13. 规范第三方支付易加大竞合关系
  14. 杂文笔记(一):博弈论在网络安全中的应用
  15. 如何向外行讲解 2G、3G 和 4G LTE 的区别?
  16. mysql小计_Mysql必读用SQL实现统计报表中的小计与合计的方法详解
  17. 微信小程序开发——MySQl存储微信昵称的特殊表情
  18. EBP 和 ESP 详解
  19. eclipse运行java总显示上一个程序的运行结果(解决方案)
  20. 灵活搭建50方局域网视频会议基本系统

热门文章

  1. ES6之---读懂let才能少踩坑
  2. C/C++返回内部静态成员的陷阱
  3. Spring基于 Annotation 的简单介绍
  4. hadoop环境搭建之伪分布集群环境搭建(单节点)
  5. mysql awr v1.0.1发布
  6. Linxu用户登录监测
  7. 如何删除需要管理员权限才能删除的文件夹
  8. Android 手电筒源码
  9. 当Windows Vista提示“内存不足”……
  10. Leetcode1700. 无法吃午餐的学生数量[C++题解]:模拟题简单,用queue